Compare commits

...

12 Commits

Author SHA1 Message Date
renovate[bot]
880b226d10 Update dependency sinon to v21.1.0 (#51529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-13 07:04:53 +02:00
renovate[bot]
031e6ea789 Update dependency prettier to v3.8.2 (#51534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-13 07:04:15 +02:00
pcan08
d025a842c4 Use header-subtitle in dialog-edit-home (#51525)
Replace the custom `<p class="description">` element and its associated
CSS with the built-in `headerSubtitle` property on `ha-dialog`.
2026-04-12 11:27:11 +02:00
Wendelin
775f145c9f Add ha-progress-bar highlight style variables (#51489)
Enhance ha-progress-bar styles and properties for improved customization
2026-04-12 09:51:01 +02:00
Paulus Schoutsen
f9caf5365e Hide internal panels from navigation picker (#51497)
* Hide _my_redirect and notfound panels from navigation picker

These internal panels are not useful navigation targets and should not
appear in the "Other routes" section of the navigation picker.

https://claude.ai/code/session_01DT6YNh9gjLpTztxA6z79w5

* Address review: use panel constants and move to module level

- Add MY_REDIRECT_PANEL constant to src/data/panel.ts
- Use NOT_FOUND_PANEL and MY_REDIRECT_PANEL instead of string literals
- Move HIDDEN_PANELS to module level since it doesn't need recreation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add SYSTEM_PANELS constant and use in navigation picker and quick bar

- Add APP_PANEL constant and SYSTEM_PANELS array to data/panel.ts
- Use SYSTEM_PANELS in ha-navigation-picker.ts and quick_bar.ts
- Remove obsolete hassio panel filter from quick bar (no longer exists)
- Also hides the app panel from navigation picker

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-12 09:49:15 +02:00
renovate[bot]
b1419b7761 Update vitest monorepo to v4.1.4 (#51524)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-12 07:45:00 +00:00
Paulus Schoutsen
be0a673d4e Add error handling for backup creation failures (#51520)
* Show error details when backup creation fails

When generateBackup or generateBackupWithAutomaticSettings raises an
error (e.g., not enough free space), the error details are now shown
to the user in an alert dialog instead of only being logged to the
console.

https://claude.ai/code/session_01XWxeS4ZxxZfnt8h2pLsSMn

* Consolidate duplicate try-catch into single block in _newBackup

https://claude.ai/code/session_01XWxeS4ZxxZfnt8h2pLsSMn

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-12 09:39:10 +02:00
Aidan Timson
8e31316692 Add link to UX design in issue template (#51503) 2026-04-12 09:38:01 +02:00
renovate[bot]
f9db26166f Update dependency typescript-eslint to v8.58.1 (#51514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-11 11:00:39 +02:00
Wendelin
7ceba8d231 Add context groups (#51471) 2026-04-10 16:03:34 +02:00
renovate[bot]
2a0b4c8f18 Update vitest monorepo to v4.1.3 (#51505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-10 10:52:27 +00:00
Aidan Timson
6c762e0105 Ignore local opencode directory (#51504) 2026-04-10 12:44:48 +02:00
54 changed files with 1216 additions and 757 deletions

View File

@@ -3,6 +3,9 @@ contact_links:
- name: Request a feature for the UI / Dashboards
url: https://github.com/orgs/home-assistant/discussions
about: Request a new feature for the Home Assistant frontend.
- name: Discuss UI or UX design
url: https://github.com/OpenHomeFoundation/ux-design/discussions
about: Share design feedback and discuss visual or UX changes with the design team.
- name: Report a bug that is NOT related to the UI / Dashboards
url: https://github.com/home-assistant/core/issues
about: This is the issue tracker for our frontend. Please report other issues in the backend ("core") repository.

2
.gitignore vendored
View File

@@ -57,4 +57,4 @@ test/coverage/
# AI tooling
.claude
.cursor
.opencode

View File

@@ -10,7 +10,7 @@ import "../../../../src/components/input/ha-input";
import "../../../../src/components/input/ha-input-copy";
import "../../../../src/components/input/ha-input-multi";
import "../../../../src/components/input/ha-input-search";
import { localizeContext } from "../../../../src/data/context";
import { internationalizationContext } from "../../../../src/data/context";
const LOCALIZE_KEYS: Record<string, string> = {
"ui.common.copy": "Copy",
@@ -26,11 +26,19 @@ const LOCALIZE_KEYS: Record<string, string> = {
export class DemoHaInput extends LitElement {
constructor() {
super();
// Provides localizeContext for ha-input-copy, ha-input-multi and ha-input-search
// Provides internationalizationContext for ha-input-copy, ha-input-multi and ha-input-search
// eslint-disable-next-line no-new
new ContextProvider(this, {
context: localizeContext,
initialValue: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
context: internationalizationContext,
initialValue: {
localize: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
language: "en",
selectedLanguage: null,
locale: {} as any,
translationMetadata: {} as any,
loadBackendTranslation: (async () => (key: string) => key) as any,
loadFragmentTranslation: (async () => (key: string) => key) as any,
},
});
}

View File

@@ -164,7 +164,7 @@
"@types/sortablejs": "1.15.9",
"@types/tar": "7.0.87",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.1.2",
"@vitest/coverage-v8": "4.1.4",
"babel-loader": "10.1.1",
"babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.4",
@@ -197,17 +197,17 @@
"lodash.template": "4.5.0",
"map-stream": "0.0.7",
"pinst": "3.0.0",
"prettier": "3.8.1",
"prettier": "3.8.2",
"rspack-manifest-plugin": "5.2.1",
"serve": "14.2.6",
"sinon": "21.0.3",
"sinon": "21.1.0",
"tar": "7.5.13",
"terser-webpack-plugin": "5.4.0",
"ts-lit-plugin": "2.0.2",
"typescript": "6.0.2",
"typescript-eslint": "8.58.0",
"typescript-eslint": "8.58.1",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.1.2",
"vitest": "4.1.4",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0",
"workbox-build": "patch:workbox-build@npm%3A7.4.0#~/.yarn/patches/workbox-build-npm-7.4.0-c84561662c.patch"

View File

@@ -18,15 +18,16 @@ import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { ensureArray } from "../../common/array/ensure-array";
import { getAllGraphColors } from "../../common/color/colors";
import { transform } from "../../common/decorators/transform";
import type { HASSDomEvent } from "../../common/dom/fire_event";
import { fireEvent } from "../../common/dom/fire_event";
import { listenMediaQuery } from "../../common/dom/media_query";
import { afterNextRender } from "../../common/util/render-status";
import { filterXSS } from "../../common/util/xss";
import { themesContext } from "../../data/context";
import { uiContext } from "../../data/context";
import type { Themes } from "../../data/ws-themes";
import type { ECOption } from "../../resources/echarts/echarts";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, HomeAssistantUI } from "../../types";
import { isMac } from "../../util/is_mac";
import "../chips/ha-assist-chip";
import "../ha-icon-button";
@@ -74,8 +75,11 @@ export class HaChartBase extends LitElement {
public extraComponents?: any[];
@state()
@consume({ context: themesContext, subscribe: true })
_themes!: Themes;
@consume({ context: uiContext, subscribe: true })
@transform<HomeAssistantUI, Themes>({
transformer: ({ themes }) => themes,
})
private _themes!: Themes;
@state() private _isZoomed = false;

View File

@@ -17,17 +17,17 @@ import memoizeOne from "memoize-one";
import { STRINGS_SEPARATOR_DOT } from "../../common/const";
import { restoreScroll } from "../../common/decorators/restore-scroll";
import { deepActiveElement } from "../../common/dom/deep-active-element";
import { fireEvent } from "../../common/dom/fire_event";
import type {
HASSDomCurrentTargetEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import { fireEvent } from "../../common/dom/fire_event";
import { stringCompare } from "../../common/string/compare";
import type { LocalizeFunc } from "../../common/translations/localize";
import { debounce } from "../../common/util/debounce";
import { groupBy } from "../../common/util/group-by";
import { nextRender } from "../../common/util/render-status";
import { localeContext, localizeContext } from "../../data/context";
import { internationalizationContext } from "../../data/context";
import type { FrontendLocaleData } from "../../data/translation";
import { haStyleScrollbar } from "../../resources/styles";
import { loadVirtualizer } from "../../resources/virtualizer";
@@ -113,12 +113,8 @@ const AUTO_FOCUS_ALLOWED_ACTIVE_TAGS = ["BODY", "HTML", "HOME-ASSISTANT"];
@customElement("ha-data-table")
export class HaDataTable extends LitElement {
@state()
@consume({ context: localizeContext, subscribe: true })
private _localize?: ContextType<typeof localizeContext>;
@state()
@consume({ context: localeContext, subscribe: true })
private _locale?: ContextType<typeof localeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: ContextType<typeof internationalizationContext>;
@property({ type: Boolean }) public narrow = false;
@@ -531,7 +527,9 @@ export class HaDataTable extends LitElement {
<div class="mdc-data-table__row" role="row">
<div class="mdc-data-table__cell grows center" role="cell">
${this.noDataText ||
this._localize?.("ui.components.data-table.no-data") ||
this._i18n?.localize?.(
"ui.components.data-table.no-data"
) ||
"No data"}
</div>
</div>
@@ -545,8 +543,8 @@ export class HaDataTable extends LitElement {
@scroll=${this._saveScrollPos}
.items=${this._groupData(
this._filteredData,
this._localize,
this._locale,
this._i18n?.localize,
this._i18n?.locale,
this.appendRow,
this.groupColumn,
this.groupOrder,
@@ -716,7 +714,7 @@ export class HaDataTable extends LitElement {
this._sortColumns[this.sortColumn],
this.sortDirection,
this.sortColumn,
this._locale?.language
this._i18n?.locale?.language
)
: filteredData;
@@ -896,8 +894,8 @@ export class HaDataTable extends LitElement {
const groupedData = this._groupData(
this._filteredData,
this._localize,
this._locale,
this._i18n?.localize,
this._i18n?.locale,
this.appendRow,
this.groupColumn,
this.groupOrder,

View File

@@ -3,6 +3,7 @@ import { consume, type ContextType } from "@lit/context";
import type { ActionDetail } from "@material/mwc-list";
import { mdiCalendarToday } from "@mdi/js";
import "cally";
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, queryAll, state } from "lit/decorators";
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
@@ -12,16 +13,13 @@ import {
formatDateYear,
formatISODateOnly,
} from "../../common/datetime/format_date";
import { transform } from "../../common/decorators/transform";
import { fireEvent } from "../../common/dom/fire_event";
import {
configContext,
localeContext,
localizeContext,
} from "../../data/context";
import { configContext, internationalizationContext } from "../../data/context";
import { TimeZone } from "../../data/translation";
import { MobileAwareMixin } from "../../mixins/mobile-aware-mixin";
import { haStyleScrollbar } from "../../resources/styles";
import type { ValueChangedEvent } from "../../types";
import type { HomeAssistantConfig, ValueChangedEvent } from "../../types";
import "../chips/ha-chip-set";
import "../chips/ha-filter-chip";
import type { HaFilterChip } from "../chips/ha-filter-chip";
@@ -48,16 +46,15 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
public timePicker = false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@state()
@consume({ context: localeContext, subscribe: true })
private locale!: ContextType<typeof localeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: configContext, subscribe: true })
private hassConfig!: ContextType<typeof configContext>;
@transform<HomeAssistantConfig, HassConfig>({
transformer: ({ config }) => config,
})
private _hassConfig!: HassConfig;
/** used to show month in calendar-range header */
@state() private _pickerMonth?: string;
@@ -87,12 +84,20 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
? formatCallyDateRange(
this.startDate,
this.endDate,
this.locale,
this.hassConfig
this._i18n?.locale,
this._hassConfig
)
: undefined;
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
this._pickerMonth = formatDateMonth(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerYear = formatDateYear(
date,
this._i18n.locale,
this._hassConfig
);
if (this.timePicker && this.startDate && this.endDate) {
this._timeValue = {
@@ -144,12 +149,12 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
<div class="range">
<calendar-range
.value=${this._dateValue}
.locale=${this.locale.language}
.locale=${this._i18n.locale.language}
.focusedDate=${this._focusDate}
@focusday=${this._focusChanged}
@change=${this._handleChange}
show-outside-days
.firstDayOfWeek=${firstWeekdayIndex(this.locale)}
.firstDayOfWeek=${firstWeekdayIndex(this._i18n.locale)}
>
<ha-icon-button-prev
tabindex="-1"
@@ -162,7 +167,7 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
<ha-icon-button
@click=${this._focusToday}
.path=${mdiCalendarToday}
.label=${this.localize("ui.dialogs.date-picker.today")}
.label=${this._i18n.localize("ui.dialogs.date-picker.today")}
></ha-icon-button>
</div>
<ha-icon-button-next
@@ -176,9 +181,9 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
<div class="times">
<ha-time-input
.value=${`${this._timeValue.from.hours}:${this._timeValue.from.minutes}`}
.locale=${this.locale}
.locale=${this._i18n.locale}
@value-changed=${this._handleChangeTime}
.label=${this.localize(
.label=${this._i18n.localize(
"ui.components.date-range-picker.time_from"
)}
id="from"
@@ -187,9 +192,9 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
></ha-time-input>
<ha-time-input
.value=${`${this._timeValue.to.hours}:${this._timeValue.to.minutes}`}
.locale=${this.locale}
.locale=${this._i18n.locale}
@value-changed=${this._handleChangeTime}
.label=${this.localize(
.label=${this._i18n.localize(
"ui.components.date-range-picker.time_to"
)}
id="to"
@@ -203,19 +208,33 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
</div>
<div class="footer">
<ha-button appearance="plain" @click=${this._cancel}
>${this.localize("ui.common.cancel")}</ha-button
>${this._i18n.localize("ui.common.cancel")}</ha-button
>
<ha-button .disabled=${!this._dateValue} @click=${this._save}
>${this.localize("ui.components.date-range-picker.select")}</ha-button
>${this._i18n.localize(
"ui.components.date-range-picker.select"
)}</ha-button
>
</div>`;
}
private _focusToday() {
const date = new Date();
this._focusDate = formatISODateOnly(date, this.locale, this.hassConfig);
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
this._focusDate = formatISODateOnly(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerMonth = formatDateMonth(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerYear = formatDateYear(
date,
this._i18n.locale,
this._hassConfig
);
}
private _cancel() {
@@ -255,12 +274,12 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
}
}
if (this.locale.time_zone === TimeZone.server) {
if (this._i18n.locale.time_zone === TimeZone.server) {
startDate = new Date(
new TZDate(startDate, this.hassConfig.time_zone).getTime()
new TZDate(startDate, this._hassConfig.time_zone).getTime()
);
endDate = new Date(
new TZDate(endDate, this.hassConfig.time_zone).getTime()
new TZDate(endDate, this._hassConfig.time_zone).getTime()
);
}
@@ -286,8 +305,16 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
private _focusChanged(ev: CustomEvent<Date>) {
const date = ev.detail;
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
this._pickerMonth = formatDateMonth(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerYear = formatDateYear(
date,
this._i18n.locale,
this._hassConfig
);
this._focusDate = undefined;
}

View File

@@ -3,6 +3,7 @@ import { consume, type ContextType } from "@lit/context";
import { mdiCalendar } from "@mdi/js";
import "cally";
import { isThisYear } from "date-fns";
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@@ -14,12 +15,10 @@ import {
formatShortDateTime,
formatShortDateTimeWithYear,
} from "../../common/datetime/format_date_time";
import { transform } from "../../common/decorators/transform";
import { fireEvent } from "../../common/dom/fire_event";
import {
configContext,
localeContext,
localizeContext,
} from "../../data/context";
import { configContext, internationalizationContext } from "../../data/context";
import type { HomeAssistantConfig } from "../../types";
import "../ha-bottom-sheet";
import "../ha-icon-button";
import "../ha-icon-button-next";
@@ -43,16 +42,15 @@ const EXTENDED_RANGE_KEYS: DateRange[] = [
@customElement("ha-date-range-picker")
export class HaDateRangePicker extends LitElement {
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@state()
@consume({ context: localeContext, subscribe: true })
private locale!: ContextType<typeof localeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: configContext, subscribe: true })
private hassConfig!: ContextType<typeof configContext>;
@transform<HomeAssistantConfig, HassConfig>({
transformer: ({ config }) => config,
})
private _hassConfig!: HassConfig;
@property({ attribute: false }) public startDate!: Date;
@@ -117,8 +115,8 @@ export class HaDateRangePicker extends LitElement {
this._ranges = {};
rangeKeys.forEach((key) => {
this._ranges![
this.localize(`ui.components.date-range-picker.ranges.${key}`)
] = calcDateRange(this.locale, this.hassConfig, key);
this._i18n.localize(`ui.components.date-range-picker.ranges.${key}`)
] = calcDateRange(this._i18n.locale, this._hassConfig, key);
});
}
@@ -140,41 +138,43 @@ export class HaDateRangePicker extends LitElement {
.value=${(isThisYear(this.startDate)
? formatShortDateTime(
this.startDate,
this.locale,
this.hassConfig
this._i18n.locale,
this._hassConfig
)
: formatShortDateTimeWithYear(
this.startDate,
this.locale,
this.hassConfig
this._i18n.locale,
this._hassConfig
)) +
(window.innerWidth >= 459 ? " - " : " - \n") +
(isThisYear(this.endDate)
? formatShortDateTime(
this.endDate,
this.locale,
this.hassConfig
this._i18n.locale,
this._hassConfig
)
: formatShortDateTimeWithYear(
this.endDate,
this.locale,
this.hassConfig
this._i18n.locale,
this._hassConfig
))}
.label=${this.localize(
.label=${this._i18n.localize(
"ui.components.date-range-picker.start_date"
) +
" - " +
this.localize("ui.components.date-range-picker.end_date")}
this._i18n.localize(
"ui.components.date-range-picker.end_date"
)}
.disabled=${this.disabled}
readonly
></ha-textarea>
<ha-icon-button-prev
.label=${this.localize("ui.common.previous")}
.label=${this._i18n.localize("ui.common.previous")}
@click=${this._handlePrev}
>
</ha-icon-button-prev>
<ha-icon-button-next
.label=${this.localize("ui.common.next")}
.label=${this._i18n.localize("ui.common.next")}
@click=${this._handleNext}
>
</ha-icon-button-next>`
@@ -182,7 +182,7 @@ export class HaDateRangePicker extends LitElement {
@click=${this._openPicker}
.disabled=${this.disabled}
id="field"
.label=${this.localize(
.label=${this._i18n.localize(
"ui.components.date-range-picker.select_date_range"
)}
.path=${mdiCalendar}
@@ -290,8 +290,8 @@ export class HaDateRangePicker extends LitElement {
this.startDate,
this.endDate,
forward,
this.locale,
this.hassConfig
this._i18n.locale,
this._hassConfig
);
this.startDate = start;
this.endDate = end;

View File

@@ -2,6 +2,7 @@ import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume, type ContextType } from "@lit/context";
import { mdiBackspace, mdiCalendarToday } from "@mdi/js";
import "cally";
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
import { css, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";
import {
@@ -10,12 +11,10 @@ import {
formatDateYear,
formatISODateOnly,
} from "../../common/datetime/format_date";
import {
configContext,
localeContext,
localizeContext,
} from "../../data/context";
import { transform } from "../../common/decorators/transform";
import { configContext, internationalizationContext } from "../../data/context";
import { DialogMixin } from "../../dialogs/dialog-mixin";
import type { HomeAssistantConfig } from "../../types";
import "../ha-button";
import type { DatePickerDialogParams } from "../ha-date-input";
import "../ha-dialog";
@@ -40,16 +39,15 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
LitElement
) {
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@state()
@consume({ context: localeContext, subscribe: true })
private locale!: ContextType<typeof localeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: configContext, subscribe: true })
private hassConfig!: ContextType<typeof configContext>;
@transform<HomeAssistantConfig, HassConfig>({
transformer: ({ config }) => config,
})
private _hassConfig!: HassConfig;
@state() private _value?: {
year: string;
@@ -74,14 +72,26 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
? new Date(`${this.params.value.split("T")[0]}T00:00:00`)
: new Date();
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
this._pickerYear = formatDateYear(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerMonth = formatDateMonth(
date,
this._i18n.locale,
this._hassConfig
);
this._value = this.params.value
? {
year: this._pickerYear,
title: formatDateShort(date, this.locale, this.hassConfig),
dateString: formatISODateOnly(date, this.locale, this.hassConfig),
title: formatDateShort(date, this._i18n.locale, this._hassConfig),
dateString: formatISODateOnly(
date,
this._i18n.locale,
this._hassConfig
),
}
: undefined;
}
@@ -95,7 +105,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
open
width="small"
.headerTitle=${this._value?.title ||
this.localize("ui.dialogs.date-picker.title")}
this._i18n.localize("ui.dialogs.date-picker.title")}
.headerSubtitle=${this._value?.year}
header-subtitle-position="above"
>
@@ -103,7 +113,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
? html`
<ha-icon-button
.path=${mdiBackspace}
.label=${this.localize("ui.dialogs.date-picker.clear")}
.label=${this._i18n.localize("ui.dialogs.date-picker.clear")}
slot="headerActionItems"
@click=${this._clear}
></ha-icon-button>
@@ -131,7 +141,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
<ha-icon-button
@click=${this._setToday}
.path=${mdiCalendarToday}
.label=${this.localize("ui.dialogs.date-picker.today")}
.label=${this._i18n.localize("ui.dialogs.date-picker.today")}
></ha-icon-button>
</div>
<ha-icon-button-next tabindex="-1" slot="next"></ha-icon-button-next>
@@ -143,10 +153,10 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
slot="secondaryAction"
@click=${this.closeDialog}
>
${this.localize("ui.common.cancel")}
${this._i18n.localize("ui.common.cancel")}
</ha-button>
<ha-button slot="primaryAction" @click=${this._setValue}>
${this.localize("ui.common.ok")}
${this._i18n.localize("ui.common.ok")}
</ha-button>
</ha-dialog-footer>
</ha-dialog>`;
@@ -164,23 +174,39 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
? new Date(`${value.split("T")[0]}T00:00:00`)
: new Date();
this._value = {
year: formatDateYear(date, this.locale, this.hassConfig),
title: formatDateShort(date, this.locale, this.hassConfig),
year: formatDateYear(date, this._i18n.locale, this._hassConfig),
title: formatDateShort(date, this._i18n.locale, this._hassConfig),
dateString:
value || formatISODateOnly(date, this.locale, this.hassConfig),
value || formatISODateOnly(date, this._i18n.locale, this._hassConfig),
};
if (setFocusDay) {
this._focusDate = this._value.dateString;
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
this._pickerMonth = formatDateMonth(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerYear = formatDateYear(
date,
this._i18n.locale,
this._hassConfig
);
}
}
private _focusChanged(ev: CustomEvent<Date>) {
const date = ev.detail;
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
this._pickerMonth = formatDateMonth(
date,
this._i18n.locale,
this._hassConfig
);
this._pickerYear = formatDateYear(
date,
this._i18n.locale,
this._hassConfig
);
this._focusDate = undefined;
}

View File

@@ -7,7 +7,7 @@ import memoizeOne from "memoize-one";
import { computeCssColor, THEME_COLORS } from "../common/color/compute-color";
import { fireEvent } from "../common/dom/fire_event";
import type { LocalizeKeys } from "../common/translations/localize";
import { localizeContext } from "../data/context";
import { internationalizationContext } from "../data/context";
import type { UiColorExtraOption } from "../data/selector";
import type { ValueChangedEvent } from "../types";
import "./ha-combo-box-item";
@@ -55,8 +55,8 @@ export class HaColorPicker extends LitElement {
@property({ type: Boolean }) public required = false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
render() {
const effectiveValue = this.value ?? this.defaultColor ?? "";
@@ -73,7 +73,7 @@ export class HaColorPicker extends LitElement {
.rowRenderer=${this._rowRenderer}
.valueRenderer=${this._valueRenderer}
@value-changed=${this._valueChanged}
.notFoundLabel=${this.localize?.(
.notFoundLabel=${this._i18n?.localize?.(
"ui.components.color-picker.no_colors_found"
)}
.getAdditionalItems=${this._getAdditionalItems}
@@ -103,7 +103,7 @@ export class HaColorPicker extends LitElement {
{
id: searchString,
primary:
this.localize?.("ui.components.color-picker.custom_color") ||
this._i18n?.localize?.("ui.components.color-picker.custom_color") ||
"Custom color",
secondary: searchString,
},
@@ -130,14 +130,15 @@ export class HaColorPicker extends LitElement {
const items: PickerComboBoxItem[] = [];
const defaultSuffix =
this.localize?.("ui.components.color-picker.default") || "Default";
this._i18n?.localize?.("ui.components.color-picker.default") ||
"Default";
const addDefaultSuffix = (label: string, isDefault: boolean) =>
isDefault && defaultSuffix ? `${label} (${defaultSuffix})` : label;
if (includeNone) {
const noneLabel =
this.localize?.("ui.components.color-picker.none") || "None";
this._i18n?.localize?.("ui.components.color-picker.none") || "None";
items.push({
id: "none",
primary: addDefaultSuffix(noneLabel, defaultColor === "none"),
@@ -147,7 +148,7 @@ export class HaColorPicker extends LitElement {
if (includeState) {
const stateLabel =
this.localize?.("ui.components.color-picker.state") || "State";
this._i18n?.localize?.("ui.components.color-picker.state") || "State";
items.push({
id: "state",
primary: addDefaultSuffix(stateLabel, defaultColor === "state"),
@@ -170,7 +171,7 @@ export class HaColorPicker extends LitElement {
Array.from(THEME_COLORS).forEach((color) => {
const themeLabel =
this.localize?.(
this._i18n?.localize?.(
`ui.components.color-picker.colors.${color}` as LocalizeKeys
) || color;
items.push({
@@ -227,7 +228,7 @@ export class HaColorPicker extends LitElement {
return html`
<ha-svg-icon slot="start" .path=${mdiInvertColorsOff}></ha-svg-icon>
<span slot="headline">
${this.localize?.("ui.components.color-picker.none") || "None"}
${this._i18n?.localize?.("ui.components.color-picker.none") || "None"}
</span>
`;
}
@@ -235,7 +236,8 @@ export class HaColorPicker extends LitElement {
return html`
<ha-svg-icon slot="start" .path=${mdiPalette}></ha-svg-icon>
<span slot="headline">
${this.localize?.("ui.components.color-picker.state") || "State"}
${this._i18n?.localize?.("ui.components.color-picker.state") ||
"State"}
</span>
`;
}
@@ -243,7 +245,7 @@ export class HaColorPicker extends LitElement {
const extraOption = this.extraOptions?.find((o) => o.value === value);
const label =
extraOption?.label ||
this.localize?.(
this._i18n?.localize?.(
`ui.components.color-picker.colors.${value}` as LocalizeKeys
) ||
value;

View File

@@ -14,7 +14,7 @@ import { ifDefined } from "lit/directives/if-defined";
import type { HASSDomEvent } from "../common/dom/fire_event";
import { fireEvent } from "../common/dom/fire_event";
import { withViewTransition } from "../common/util/view-transition";
import { localizeContext } from "../data/context";
import { internationalizationContext } from "../data/context";
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
import { haStyleScrollbar } from "../resources/styles";
import "./ha-dialog-header";
@@ -123,13 +123,13 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
@query(".body") public bodyContainer!: HTMLDivElement;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
// disabled till iOS app fix the "focus_element" implementation
// @state()
// @consume({ context: authContext, subscribe: true })
// private auth?: ContextType<typeof authContext>;
// @consume({ context: configContext, subscribe: true })
// private _hassConfig?: ContextType<typeof configContext>;
@state()
private _bodyScrolled = false;
@@ -184,7 +184,7 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
<slot name="headerNavigationIcon" slot="navigationIcon">
<ha-icon-button
data-dialog="close"
.label=${this.localize?.("ui.common.close") ?? "Close"}
.label=${this._i18n?.localize("ui.common.close") ?? "Close"}
.path=${mdiClose}
></ha-icon-button>
</slot>
@@ -222,13 +222,13 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
requestAnimationFrame(() => {
// disabled till iOS app fix the "focus_element" implementation
// if (this.auth?.external && isIosApp(this.auth.external)) {
// if (this._hassConfig?.auth.external && isIosApp(this._hassConfig.auth.external)) {
// const element = this.querySelector("[autofocus]");
// if (element !== null) {
// if (!element.id) {
// element.id = "ha-dialog-autofocus";
// }
// this.auth.external.fireMessage({
// this._hassConfig.auth.external.fireMessage({
// type: "focus_element",
// payload: {
// element_id: element.id,

View File

@@ -2,12 +2,7 @@ import { consume, type ContextType } from "@lit/context";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import {
authContext,
configContext,
connectionContext,
themesContext,
} from "../data/context";
import { configContext, connectionContext, uiContext } from "../data/context";
import {
DEFAULT_DOMAIN_ICON,
domainIcon,
@@ -38,12 +33,8 @@ export class HaDomainIcon extends LitElement {
private _connection?: ContextType<typeof connectionContext>;
@state()
@consume({ context: themesContext, subscribe: true })
private _themes?: ContextType<typeof themesContext>;
@state()
@consume({ context: authContext, subscribe: true })
private _auth?: ContextType<typeof authContext>;
@consume({ context: uiContext, subscribe: true })
private _hassUi?: ContextType<typeof uiContext>;
protected render() {
if (this.icon) {
@@ -59,8 +50,8 @@ export class HaDomainIcon extends LitElement {
}
const icon = domainIcon(
this._connection,
this._hassConfig,
this._connection.connection,
this._hassConfig.config,
this.domain,
this.deviceClass,
this.state
@@ -86,9 +77,9 @@ export class HaDomainIcon extends LitElement {
{
domain: this.domain!,
type: "icon",
darkOptimized: this._themes?.darkMode,
darkOptimized: this._hassUi?.themes.darkMode,
},
this._auth?.data.hassUrl
this._hassConfig?.auth.data.hassUrl
);
return html`
<img

View File

@@ -8,7 +8,7 @@ import { caseInsensitiveStringCompare } from "../common/string/compare";
import { titleCase } from "../common/string/title-case";
import { getConfigEntries, type ConfigEntry } from "../data/config_entries";
import { fetchConfig } from "../data/lovelace/config/types";
import { getPanelIcon, getPanelTitle } from "../data/panel";
import { getPanelIcon, getPanelTitle, SYSTEM_PANELS } from "../data/panel";
import { findRelated, type RelatedResult } from "../data/search";
import { PANEL_DASHBOARDS } from "../panels/config/lovelace/dashboards/ha-config-lovelace-dashboards";
import { computeAreaPath } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper";
@@ -274,6 +274,7 @@ export class HaNavigationPicker extends LitElement {
const otherRoutes: NavigationItem[] = [];
for (const panel of panels) {
if (SYSTEM_PANELS.includes(panel.id)) continue;
const path = `/${panel.url_path}`;
const panelTitle = getPanelTitle(this.hass, panel);
const primary = panelTitle || path;

View File

@@ -15,7 +15,7 @@ import memoizeOne from "memoize-one";
import { tinykeys } from "tinykeys";
import { fireEvent } from "../common/dom/fire_event";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { localeContext, localizeContext } from "../data/context";
import { internationalizationContext } from "../data/context";
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
import {
multiTermSortedSearch,
@@ -162,12 +162,8 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
@query("ha-input-search") private _searchFieldElement?: HaInputSearch;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@state()
@consume({ context: localeContext, subscribe: true })
private locale!: ContextType<typeof localeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private i18n!: ContextType<typeof internationalizationContext>;
@state() private _items: PickerComboBoxItem[] = [];
@@ -222,9 +218,9 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
const searchLabel =
this.label ??
(this.allowCustomValue
? (this.localize?.("ui.components.combo-box.search_or_custom") ??
? (this.i18n.localize?.("ui.components.combo-box.search_or_custom") ??
"Search | Add custom value")
: (this.localize?.("ui.common.search") ?? "Search"));
: (this.i18n.localize?.("ui.common.search") ?? "Search"));
return html`<ha-input-search
appearance="outlined"
@@ -351,7 +347,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
return caseInsensitiveStringCompare(
sortLabelA,
sortLabelB,
this.locale?.language ?? navigator.language
this.i18n.locale?.language ?? navigator.language
);
});
}
@@ -368,7 +364,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
id: this._search,
primary:
this.customValueLabel ??
this.localize?.("ui.components.combo-box.add_custom_item") ??
this.i18n.localize?.("ui.components.combo-box.add_custom_item") ??
"Add custom item",
secondary: `"${this._search}"`,
icon_path: mdiPlus,
@@ -402,10 +398,10 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
? typeof this.notFoundLabel === "function"
? this.notFoundLabel(this._search)
: this.notFoundLabel ||
this.localize?.("ui.components.combo-box.no_match") ||
this.i18n.localize?.("ui.components.combo-box.no_match") ||
"No matching items found"
: this.emptyLabel ||
this.localize?.("ui.components.combo-box.no_items") ||
this.i18n.localize?.("ui.components.combo-box.no_items") ||
"No items available"}</span
>
</ha-combo-box-item>
@@ -497,7 +493,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
id: searchString,
primary:
this.customValueLabel ??
this.localize?.("ui.components.combo-box.add_custom_item") ??
this.i18n.localize?.("ui.components.combo-box.add_custom_item") ??
"Add custom item",
secondary: `"${searchString}"`,
icon_path: mdiPlus,

View File

@@ -1,4 +1,4 @@
import { consume } from "@lit/context";
import { consume, type ContextType } from "@lit/context";
import { mdiClose, mdiMenuDown } from "@mdi/js";
import {
css,
@@ -11,9 +11,8 @@ import {
import { customElement, property, query, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import { localizeContext } from "../data/context";
import { internationalizationContext } from "../data/context";
import { PickerMixin } from "../mixins/picker-mixin";
import type { HomeAssistant } from "../types";
import "./ha-combo-box-item";
import type { HaComboBoxItem } from "./ha-combo-box-item";
import "./ha-icon";
@@ -34,8 +33,8 @@ export class HaPickerField extends PickerMixin(LitElement) {
@query("ha-combo-box-item", true) public item!: HaComboBoxItem;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: HomeAssistant["localize"];
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
public async focus() {
await this.updateComplete;
@@ -89,7 +88,7 @@ export class HaPickerField extends PickerMixin(LitElement) {
${this.unknown
? html`<div slot="supporting-text" class="unknown">
${this.unknownItemText ||
this.localize("ui.components.combo-box.unknown_item")}
this._i18n?.localize("ui.components.combo-box.unknown_item")}
</div>`
: nothing}
${showClearIcon

View File

@@ -3,7 +3,7 @@ import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { copyToClipboard } from "../../common/util/copy-clipboard";
import { localizeContext } from "../../data/context";
import { internationalizationContext } from "../../data/context";
import { showToast } from "../../util/toast";
import "../ha-button";
import "../ha-icon-button";
@@ -59,8 +59,8 @@ export class HaInputCopy extends LitElement {
false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state() private _showMasked = true;
@@ -90,7 +90,7 @@ export class HaInputCopy extends LitElement {
? html`<ha-icon-button
slot="end"
class="toggle-unmasked"
.label=${this.localize(
.label=${this._i18n.localize(
`ui.common.${this._showMasked ? "show" : "hide"}`
)}
@click=${this._toggleMasked}
@@ -101,7 +101,7 @@ export class HaInputCopy extends LitElement {
</div>
<ha-button @click=${this._copy} appearance="plain" size="small">
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
${this.label || this.localize("ui.common.copy")}
${this.label || this._i18n.localize("ui.common.copy")}
</ha-button>
</div>
`;
@@ -119,7 +119,7 @@ export class HaInputCopy extends LitElement {
private async _copy(): Promise<void> {
await copyToClipboard(this.value);
showToast(this, {
message: this.localize("ui.common.copied_clipboard"),
message: this._i18n.localize("ui.common.copied_clipboard"),
});
}

View File

@@ -5,7 +5,7 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { fireEvent } from "../../common/dom/fire_event";
import { localizeContext } from "../../data/context";
import { internationalizationContext } from "../../data/context";
import { haStyle } from "../../resources/styles";
import "../ha-button";
import "../ha-icon-button";
@@ -64,8 +64,8 @@ class HaInputMulti extends LitElement {
public updateOnBlur = false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize?: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: ContextType<typeof internationalizationContext>;
protected render() {
return html`
@@ -109,7 +109,7 @@ class HaInputMulti extends LitElement {
.index=${index}
slot="navigationIcon"
.label=${this.removeLabel ??
this.localize?.("ui.common.remove") ??
this._i18n?.localize("ui.common.remove") ??
"Remove"}
@click=${this._removeItem}
.path=${mdiDeleteOutline}
@@ -137,10 +137,10 @@ class HaInputMulti extends LitElement {
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
${this.addLabel ??
(this.label
? this.localize?.("ui.components.multi-textfield.add_item", {
? this._i18n?.localize("ui.components.multi-textfield.add_item", {
item: this.label,
})
: this.localize?.("ui.common.add")) ??
: this._i18n?.localize("ui.common.add")) ??
"Add"}
</ha-button>
</div>

View File

@@ -2,7 +2,7 @@ import { consume, type ContextType } from "@lit/context";
import { mdiMagnify } from "@mdi/js";
import { html, type PropertyValues } from "lit";
import { customElement, state } from "lit/decorators";
import { localizeContext } from "../../data/context";
import { internationalizationContext } from "../../data/context";
import { HaInput } from "./ha-input";
/**
@@ -18,8 +18,8 @@ import { HaInput } from "./ha-input";
@customElement("ha-input-search")
export class HaInputSearch extends HaInput {
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
constructor() {
super();
@@ -33,9 +33,9 @@ export class HaInputSearch extends HaInput {
if (
!this.label &&
!this.placeholder &&
(!this.hasUpdated || changedProps.has("localize"))
(!this.hasUpdated || changedProps.has("_i18n"))
) {
this.placeholder = this.localize("ui.common.search");
this.placeholder = this._i18n.localize("ui.common.search");
}
}

View File

@@ -14,10 +14,14 @@ import { customElement, property } from "lit/decorators";
* Supports regular, indeterminate, and loading states with Home Assistant theming.
*
* @cssprop --ha-progress-bar-indicator-color - Color of the filled progress indicator.
* @cssprop --ha-progress-bar-indicator-background - Background of the filled progress indicator. Overrides `--ha-progress-bar-indicator-color` when set (accepts any CSS background value, e.g. gradients).
* @cssprop --ha-progress-bar-track-color - Color of the progress track.
* @cssprop --ha-progress-bar-track-height - Height of the progress track. Defaults to `16px`.
* @cssprop --ha-progress-bar-border-radius - Border radius of the progress bar. Defaults to `var(--ha-border-radius-pill)`.
* @cssprop --ha-progress-bar-animation-duration - Animation duration for indeterminate/loading highlight. Defaults to `2.5s`.
* @cssprop --ha-progress-bar-indicator-highlight-image - Image shown at the progress indicator tip (accepts any CSS `background-image` value). Hidden during indeterminate state.
* @cssprop --ha-progress-bar-indicator-highlight-width - Width of the indicator highlight element. Defaults to `calc(var(--track-height) * 2)`.
* @cssprop --ha-progress-bar-indicator-highlight-height - Height of the indicator highlight element. Defaults to `calc(var(--track-height) * 2)`.
*
* @attr {boolean} loading - Shows the loading highlight animation on top of the indicator.
* @attr {boolean} indeterminate - Shows indeterminate progress animation (inherited from ProgressBar).
@@ -42,6 +46,7 @@ export class HaProgressBar extends ProgressBar {
);
--track-height: var(--ha-progress-bar-track-height, 16px);
--wa-transition-slow: var(--ha-animation-duration-slow);
position: relative;
}
.progress-bar {
border-radius: var(
@@ -49,36 +54,70 @@ export class HaProgressBar extends ProgressBar {
var(--ha-border-radius-pill)
);
}
@keyframes slide-highlight {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
.indicator {
background: var(
--ha-progress-bar-indicator-background,
var(--indicator-color)
);
}
:host([indeterminate]) .indicator {
animation: wa-progress-indeterminate
var(--ha-progress-bar-animation-duration, 2.5s) infinite
cubic-bezier(0.37, 0, 0.63, 1);
}
:host([indeterminate]) .indicator::after,
:host([loading]) .indicator::after {
@keyframes slide-highlight {
0% {
transform: translateX(-200%);
}
100% {
transform: translateX(200%);
}
}
:host([loading]:not([indeterminate])) .progress-bar::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 0%,
var(--ha-color-fill-primary-normal-hover) 45%,
var(--ha-color-fill-primary-normal-active) 50%,
var(--ha-color-fill-primary-normal-hover) 55%,
oklch(from var(--indicator-color) 85% c h) 50%,
transparent 100%
);
opacity: 0.4;
opacity: 0.5;
animation: slide-highlight
var(--ha-progress-bar-animation-duration, 2.5s) infinite
cubic-bezier(0.37, 0, 0.63, 1);
width: 50%;
}
:host::after {
--width: var(
--ha-progress-bar-indicator-highlight-width,
calc(var(--track-height) * 2)
);
width: var(--width);
height: var(
--ha-progress-bar-indicator-highlight-height,
calc(var(--track-height) * 2)
);
content: "";
position: absolute;
inset-inline-start: clamp(
var(--width) / 2,
var(--percentage, 0%),
calc(100% - var(--width) / 2)
);
top: 50%;
transform: translate(-50%, -50%);
background-image: var(--ha-progress-bar-indicator-highlight-image);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
pointer-events: none;
transition: inset-inline-start var(--wa-transition-slow, 200ms)
var(--wa-transition-easing, ease);
}
:host([indeterminate])::after {
display: none;
}
`,
];

View File

@@ -5,6 +5,8 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import type { Trigger } from "../../data/automation";
import { migrateAutomationTrigger } from "../../data/automation";
import { describeCondition, describeTrigger } from "../../data/automation_i18n";
import { fullEntitiesContext, labelsContext } from "../../data/context";
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
@@ -24,8 +26,6 @@ import "../ha-icon-button";
import "./hat-logbook-note";
import type { NodeInfo } from "./hat-script-graph";
import { traceTabStyles } from "./trace-tab-styles";
import type { Trigger } from "../../data/automation";
import { migrateAutomationTrigger } from "../../data/automation";
const TRACE_PATH_TABS = [
"step_config",

View File

@@ -1,38 +0,0 @@
import { createContext } from "@lit/context";
import type { HassConfig } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../types";
import type { ConfigEntry } from "./config_entries";
import type { EntityRegistryEntry } from "./entity/entity_registry";
import type { LabelRegistryEntry } from "./label/label_registry";
export const connectionContext =
createContext<HomeAssistant["connection"]>("connection");
export const statesContext = createContext<HomeAssistant["states"]>("states");
export const entitiesContext =
createContext<HomeAssistant["entities"]>("entities");
export const devicesContext =
createContext<HomeAssistant["devices"]>("devices");
export const areasContext = createContext<HomeAssistant["areas"]>("areas");
export const localizeContext =
createContext<HomeAssistant["localize"]>("localize");
export const localeContext = createContext<HomeAssistant["locale"]>("locale");
export const configContext = createContext<HassConfig>("config");
export const themesContext = createContext<HomeAssistant["themes"]>("themes");
export const selectedThemeContext =
createContext<HomeAssistant["selectedTheme"]>("selectedTheme");
export const userContext = createContext<HomeAssistant["user"]>("user");
export const userDataContext =
createContext<HomeAssistant["userData"]>("userData");
export const panelsContext = createContext<HomeAssistant["panels"]>("panels");
export const fullEntitiesContext =
createContext<EntityRegistryEntry[]>("extendedEntities");
export const floorsContext = createContext<HomeAssistant["floors"]>("floors");
export const labelsContext = createContext<LabelRegistryEntry[]>("labels");
export const configEntriesContext =
createContext<ConfigEntry[]>("configEntries");
export const authContext = createContext<HomeAssistant["auth"]>("auth");

148
src/data/context/index.ts Normal file
View File

@@ -0,0 +1,148 @@
import { createContext } from "@lit/context";
import type { HassConfig } from "home-assistant-js-websocket";
import type {
HomeAssistant,
HomeAssistantApi,
HomeAssistantConfig,
HomeAssistantConnection,
HomeAssistantInternationalization,
HomeAssistantRegistries,
HomeAssistantUI,
} from "../../types";
import type { ConfigEntry } from "../config_entries";
import type { EntityRegistryEntry } from "../entity/entity_registry";
import type { LabelRegistryEntry } from "../label/label_registry";
/**
* Entity, device, area, and floor registries
*/
export const registriesContext =
createContext<HomeAssistantRegistries>("hassRegistries");
/**
* Live map of all entity states, keyed by entity ID.
*/
export const statesContext = createContext<HomeAssistant["states"]>("states");
/**
* Provides the map of all available Home Assistant services, keyed by domain.
*/
export const servicesContext =
createContext<HomeAssistant["services"]>("services");
/**
* i18n state: active language, locale settings, the `localize` function, translation metadata, and the
* `loadBackendTranslation` / `loadFragmentTranslation` loaders.
*/
export const internationalizationContext =
createContext<HomeAssistantInternationalization>("hassInternationalization");
/**
* HTTP and WebSocket API surface: `callService`, `callApi`,
* `callApiRaw`, `callWS`, `sendWS`, `fetchWithAuth`, and `hassUrl`.
*/
export const apiContext = createContext<HomeAssistantApi>("hassApi");
/**
* WebSocket connection state: `connection`, `connected`, and `debugConnection`.
*/
export const connectionContext =
createContext<HomeAssistantConnection>("hassConnection");
/**
* UI preferences and global UI state: themes, selected theme,
* panels, sidebar mode, kiosk mode, shortcuts, vibration, and
* `suspendWhenHidden`.
*/
export const uiContext = createContext<HomeAssistantUI>("hassUi");
/**
* HA core configuration together with user session data:
* `auth`, `config` (core HA config), `user`, `userData`, and `systemData`.
*/
export const configContext = createContext<HomeAssistantConfig>("hassConfig");
/**
* Map of all entities in the entity registry, keyed by entity ID.
*/
export const entitiesContext =
createContext<HomeAssistant["entities"]>("entities");
/**
* Map of all devices in the device registry, keyed by device ID.
*/
export const devicesContext =
createContext<HomeAssistant["devices"]>("devices");
/**
* Map of all areas in the area registry, keyed by area ID.
*/
export const areasContext = createContext<HomeAssistant["areas"]>("areas");
/**
* Map of all floors in the floor registry, keyed by floor ID.
*/
export const floorsContext = createContext<HomeAssistant["floors"]>("floors");
// #region lazy-contexts
/**
* Lazy contexts are not subscribed to by default. They are only subscribed to when a provider is consumed with at least one consumer.
*/
/**
* Lazy loaded labels registry, keyed by label ID.
*/
export const labelsContext = createContext<LabelRegistryEntry[]>("labels");
/**
* Lazy loaded entity registry array
*/
export const fullEntitiesContext =
createContext<EntityRegistryEntry[]>("extendedEntities");
/**
* Lazy loaded config entries array
*/
export const configEntriesContext =
createContext<ConfigEntry[]>("configEntries");
// #endregion lazy-contexts
// #region deprecated-contexts
/** @deprecated Use `connectionContext` instead. */
export const connectionSingleContext =
createContext<HomeAssistant["connection"]>("connection");
/** @deprecated Use `internationalizationContext` instead. */
export const localizeContext =
createContext<HomeAssistant["localize"]>("localize");
/** @deprecated Use `internationalizationContext` instead. */
export const localeContext = createContext<HomeAssistant["locale"]>("locale");
/** @deprecated Use `configContext` instead. */
export const configSingleContext = createContext<HassConfig>("config");
/** @deprecated Use `uiContext` instead. */
export const themesContext = createContext<HomeAssistant["themes"]>("themes");
/** @deprecated Use `uiContext` instead. */
export const selectedThemeContext =
createContext<HomeAssistant["selectedTheme"]>("selectedTheme");
/** @deprecated Use `configContext` instead. */
export const userContext = createContext<HomeAssistant["user"]>("user");
/** @deprecated Use `configContext` instead. */
export const userDataContext =
createContext<HomeAssistant["userData"]>("userData");
/** @deprecated Use `uiContext` instead. */
export const panelsContext = createContext<HomeAssistant["panels"]>("panels");
/** @deprecated Use `configContext` instead. */
export const authContext = createContext<HomeAssistant["auth"]>("auth");
// #endregion deprecated-contexts

View File

@@ -0,0 +1,166 @@
import type {
HomeAssistant,
HomeAssistantApi,
HomeAssistantConfig,
HomeAssistantConnection,
HomeAssistantInternationalization,
HomeAssistantRegistries,
HomeAssistantUI,
} from "../../types";
const updateRegistries = (
hass: HomeAssistant,
value?: HomeAssistantRegistries
): HomeAssistantRegistries => {
if (
!value ||
value.entities !== hass.entities ||
value.devices !== hass.devices ||
value.areas !== hass.areas ||
value.floors !== hass.floors
) {
return {
entities: hass.entities,
devices: hass.devices,
areas: hass.areas,
floors: hass.floors,
};
}
return value;
};
const updateInternationalization = (
hass: HomeAssistant,
value?: HomeAssistantInternationalization
): HomeAssistantInternationalization => {
if (
!value ||
value.localize !== hass.localize ||
value.locale !== hass.locale ||
value.loadBackendTranslation !== hass.loadBackendTranslation ||
value.loadFragmentTranslation !== hass.loadFragmentTranslation ||
value.language !== hass.language ||
value.selectedLanguage !== hass.selectedLanguage ||
value.translationMetadata !== hass.translationMetadata
) {
return {
localize: hass.localize,
locale: hass.locale,
loadBackendTranslation: hass.loadBackendTranslation,
loadFragmentTranslation: hass.loadFragmentTranslation,
language: hass.language,
selectedLanguage: hass.selectedLanguage,
translationMetadata: hass.translationMetadata,
};
}
return value;
};
const updateApi = (
hass: HomeAssistant,
value?: HomeAssistantApi
): HomeAssistantApi => {
if (
!value ||
value.callService !== hass.callService ||
value.callApi !== hass.callApi ||
value.callApiRaw !== hass.callApiRaw ||
value.callWS !== hass.callWS ||
value.sendWS !== hass.sendWS ||
value.fetchWithAuth !== hass.fetchWithAuth
) {
return {
callService: hass.callService,
callApi: hass.callApi,
callApiRaw: hass.callApiRaw,
callWS: hass.callWS,
sendWS: hass.sendWS,
fetchWithAuth: hass.fetchWithAuth,
};
}
return value;
};
const updateConnection = (
hass: HomeAssistant,
value?: HomeAssistantConnection
): HomeAssistantConnection => {
if (
!value ||
value.connection !== hass.connection ||
value.connected !== hass.connected ||
value.debugConnection !== hass.debugConnection ||
value.hassUrl !== hass.hassUrl
) {
return {
connection: hass.connection,
connected: hass.connected,
debugConnection: hass.debugConnection,
hassUrl: hass.hassUrl,
};
}
return value;
};
const updateUi = (
hass: HomeAssistant,
value?: HomeAssistantUI
): HomeAssistantUI => {
if (
!value ||
value.themes !== hass.themes ||
value.selectedTheme !== hass.selectedTheme ||
value.panels !== hass.panels ||
value.panelUrl !== hass.panelUrl ||
value.dockedSidebar !== hass.dockedSidebar ||
value.kioskMode !== hass.kioskMode ||
value.enableShortcuts !== hass.enableShortcuts ||
value.vibrate !== hass.vibrate ||
value.suspendWhenHidden !== hass.suspendWhenHidden
) {
return {
themes: hass.themes,
selectedTheme: hass.selectedTheme,
panels: hass.panels,
panelUrl: hass.panelUrl,
dockedSidebar: hass.dockedSidebar,
kioskMode: hass.kioskMode,
enableShortcuts: hass.enableShortcuts,
vibrate: hass.vibrate,
suspendWhenHidden: hass.suspendWhenHidden,
};
}
return value;
};
const updateConfig = (
hass: HomeAssistant,
value?: HomeAssistantConfig
): HomeAssistantConfig => {
if (
!value ||
value.auth !== hass.auth ||
value.config !== hass.config ||
value.user !== hass.user ||
value.userData !== hass.userData ||
value.systemData !== hass.systemData
) {
return {
auth: hass.auth,
config: hass.config,
user: hass.user,
userData: hass.userData,
systemData: hass.systemData,
};
}
return value;
};
export const updateHassGroups = {
registries: updateRegistries,
internationalization: updateInternationalization,
api: updateApi,
connection: updateConnection,
ui: updateUi,
config: updateConfig,
};

View File

@@ -12,11 +12,16 @@ import type { LocalizeKeys } from "../common/translations/localize";
import type { PageNavigation } from "../layouts/hass-tabs-subpage";
import type { HomeAssistant, PanelInfo } from "../types";
export const APP_PANEL = "app";
export const HOME_PANEL = "home";
export const MY_REDIRECT_PANEL = "_my_redirect";
export const NOT_FOUND_PANEL = "notfound";
export const PROFILE_PANEL = "profile";
export const LOVELACE_PANEL = "lovelace";
/** Panels that are internal/system-level and should not appear in user-facing navigation UIs. */
export const SYSTEM_PANELS = [MY_REDIRECT_PANEL, NOT_FOUND_PANEL, APP_PANEL];
/** Panel to show when no panel is picked. */
export const DEFAULT_PANEL = HOME_PANEL;

View File

@@ -18,7 +18,11 @@ import type { FuseWeightedKey } from "../resources/fuseMultiTerm";
import type { HomeAssistant } from "../types";
import type { HassioAddonInfo } from "./hassio/addon";
import { domainToName } from "./integration";
import { getPanelIcon, getPanelNameTranslationKey } from "./panel";
import {
getPanelIcon,
getPanelNameTranslationKey,
SYSTEM_PANELS,
} from "./panel";
export interface NavigationComboBoxItem extends PickerComboBoxItem {
path: string;
@@ -51,12 +55,7 @@ const generateNavigationPanelCommands = (
apps?: HassioAddonInfo[]
): BaseNavigationCommand[] =>
Object.entries(panels)
.filter(
([panelKey]) =>
panelKey !== "_my_redirect" &&
panelKey !== "hassio" &&
panelKey !== "app"
)
.filter(([panelKey]) => !SYSTEM_PANELS.includes(panelKey))
.map(([_panelKey, panel]) => {
const translationKey = getPanelNameTranslationKey(panel);
const icon = getPanelIcon(panel) || "mdi:view-dashboard";

View File

@@ -5,7 +5,7 @@ import type { LocalizeKeys } from "../../common/translations/localize";
import "../../components/ha-alert";
import "../../components/ha-dialog";
import "../../components/ha-svg-icon";
import { localizeContext } from "../../data/context";
import { internationalizationContext } from "../../data/context";
import { isMac } from "../../util/is_mac";
import { DialogMixin } from "../dialog-mixin";
@@ -168,8 +168,8 @@ const _SHORTCUTS: Section[] = [
@customElement("dialog-shortcuts")
class DialogShortcuts extends DialogMixin(LitElement) {
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
private _renderShortcut(
shortcutKeys: ShortcutString[],
@@ -183,13 +183,15 @@ class DialogShortcuts extends DialogMixin(LitElement) {
>${shortcutKey === CTRL_CMD
? isMac
? "⌘"
: this.localize("ui.dialogs.shortcuts.keys.ctrl")
: this._i18n.localize("ui.dialogs.shortcuts.keys.ctrl")
: typeof shortcutKey === "string"
? shortcutKey
: this.localize(shortcutKey.shortcutTranslationKey)}</span
: this._i18n.localize(
shortcutKey.shortcutTranslationKey
)}</span
>`
)}
${this.localize(descriptionKey)}
${this._i18n.localize(descriptionKey)}
</div>
`;
}
@@ -198,12 +200,12 @@ class DialogShortcuts extends DialogMixin(LitElement) {
return html`
<ha-dialog
open
.headerTitle=${this.localize("ui.dialogs.shortcuts.title")}
.headerTitle=${this._i18n.localize("ui.dialogs.shortcuts.title")}
>
<div class="content">
${_SHORTCUTS.map(
(section) => html`
<h3>${this.localize(section.titleTranslationKey)}</h3>
<h3>${this._i18n.localize(section.titleTranslationKey)}</h3>
<div class="items">
${section.items.map((item) => {
if ("shortcut" in item) {
@@ -213,7 +215,7 @@ class DialogShortcuts extends DialogMixin(LitElement) {
);
}
return html`<p>
${this.localize((item as Text).textTranslationKey)}
${this._i18n.localize((item as Text).textTranslationKey)}
</p>`;
})}
</div>
@@ -222,9 +224,9 @@ class DialogShortcuts extends DialogMixin(LitElement) {
</div>
<ha-alert slot="footer">
${this.localize("ui.dialogs.shortcuts.enable_shortcuts_hint", {
${this._i18n.localize("ui.dialogs.shortcuts.enable_shortcuts_hint", {
user_profile: html`<a href="/profile/general#shortcuts"
>${this.localize(
>${this._i18n.localize(
"ui.dialogs.shortcuts.enable_shortcuts_hint_user_profile"
)}</a
>`,

View File

@@ -303,7 +303,6 @@ export const provideHass = (
debugConnection: false,
kioskMode: false,
suspendWhenHidden: false,
moreInfoEntityId: null as any,
// @ts-ignore
async callService(domain, service, data) {
if (data && "entity_id" in data) {

View File

@@ -31,6 +31,8 @@ import { storage } from "../../../../common/decorators/storage";
import { fireEvent } from "../../../../common/dom/fire_event";
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { computeDomain } from "../../../../common/entity/compute_domain";
import { computeObjectId } from "../../../../common/entity/compute_object_id";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
import { handleStructError } from "../../../../common/structs/handle-errors";
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
@@ -95,8 +97,6 @@ import "./types/ha-automation-action-set_conversation_response";
import "./types/ha-automation-action-stop";
import "./types/ha-automation-action-wait_for_trigger";
import "./types/ha-automation-action-wait_template";
import { computeDomain } from "../../../../common/entity/compute_domain";
import { computeObjectId } from "../../../../common/entity/compute_object_id";
export const getAutomationActionType = memoizeOne(
(action: Action | undefined) => {

View File

@@ -6,7 +6,7 @@ import "../../../../../components/ha-formfield";
import "../../../../../components/ha-switch";
import "../../../../../components/input/ha-input";
import type { HaInput } from "../../../../../components/input/ha-input";
import { localizeContext } from "../../../../../data/context";
import { internationalizationContext } from "../../../../../data/context";
import type { StopAction } from "../../../../../data/script";
import type { ActionElement } from "../ha-automation-action-row";
@@ -17,8 +17,8 @@ export class HaStopAction extends LitElement implements ActionElement {
@property({ type: Boolean }) public disabled = false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
public static get defaultConfig(): StopAction {
return { stop: "" };
@@ -29,7 +29,7 @@ export class HaStopAction extends LitElement implements ActionElement {
return html`
<ha-input
.label=${this.localize(
.label=${this._i18n.localize(
"ui.panel.config.automation.editor.actions.type.stop.stop"
)}
.value=${stop}
@@ -37,7 +37,7 @@ export class HaStopAction extends LitElement implements ActionElement {
@change=${this._stopChanged}
></ha-input>
<ha-input
.label=${this.localize(
.label=${this._i18n.localize(
"ui.panel.config.automation.editor.actions.type.stop.response_variable"
)}
.value=${response_variable || ""}
@@ -46,7 +46,7 @@ export class HaStopAction extends LitElement implements ActionElement {
></ha-input>
<ha-formfield
.disabled=${this.disabled}
.label=${this.localize(
.label=${this._i18n.localize(
"ui.panel.config.automation.editor.actions.type.stop.error"
)}
>

View File

@@ -99,9 +99,9 @@ import {
domainToName,
fetchIntegrationManifests,
} from "../../../data/integration";
import { filterSelectorEntities } from "../../../data/selector";
import type { LabelRegistryEntry } from "../../../data/label/label_registry";
import { subscribeLabFeature } from "../../../data/labs";
import { filterSelectorEntities } from "../../../data/selector";
import {
TARGET_SEPARATOR,
getConditionsForTarget,

View File

@@ -1,7 +1,7 @@
import "@home-assistant/webawesome/dist/components/tree-item/tree-item";
import "@home-assistant/webawesome/dist/components/tree/tree";
import type { WaSelectionChangeEvent } from "@home-assistant/webawesome/dist/events/selection-change";
import { consume } from "@lit/context";
import { consume, type ContextType } from "@lit/context";
import { mdiTextureBox } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import {
@@ -44,12 +44,9 @@ import {
type ConfigEntry,
} from "../../../../data/config_entries";
import {
areasContext,
devicesContext,
entitiesContext,
floorsContext,
internationalizationContext,
labelsContext,
localizeContext,
registriesContext,
statesContext,
} from "../../../../data/context";
import { getDeviceEntityLookup } from "../../../../data/device/device_registry";
@@ -99,28 +96,14 @@ export default class HaAutomationAddFromTarget extends LitElement {
// #region context
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: HomeAssistant["localize"];
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: statesContext, subscribe: true })
private states!: HomeAssistant["states"];
private states!: ContextType<typeof statesContext>;
@state()
@consume({ context: floorsContext, subscribe: true })
private floors!: HomeAssistant["floors"];
@state()
@consume({ context: areasContext, subscribe: true })
private areas!: HomeAssistant["areas"];
@state()
@consume({ context: devicesContext, subscribe: true })
private devices!: HomeAssistant["devices"];
@state()
@consume({ context: entitiesContext, subscribe: true })
private entities!: HomeAssistant["entities"];
@consume({ context: registriesContext, subscribe: true })
private _registries!: ContextType<typeof registriesContext>;
@state()
@consume({ context: labelsContext, subscribe: true })
@@ -205,7 +188,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
? html`
<div class="targets-show-more">
<ha-button appearance="filled" @click=${this._expandHeight}>
${this.localize("ui.panel.config.automation.editor.show_more")}
${this._i18n.localize(
"ui.panel.config.automation.editor.show_more"
)}
</ha-button>
</div>
`
@@ -235,7 +220,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
if (valueType === "area" && valueId) {
const floor =
entries[
`floor${TARGET_SEPARATOR}${this.areas[valueId]?.floor_id || ""}`
`floor${TARGET_SEPARATOR}${this._registries.areas[valueId]?.floor_id || ""}`
];
const { devices, entities } =
floor.areas![`area${TARGET_SEPARATOR}${valueId}`];
@@ -255,9 +240,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
if (valueId && valueType === "device") {
const areaId = this.devices[valueId]?.area_id;
const areaId = this._registries.devices[valueId]?.area_id;
if (areaId) {
const floorId = this.areas[areaId]?.floor_id || "";
const floorId = this._registries.areas[areaId]?.floor_id || "";
const { entities } =
entries[`floor${TARGET_SEPARATOR}${floorId}`].areas![
`area${TARGET_SEPARATOR}${areaId}`
@@ -266,7 +251,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
return entities.length ? this._renderEntities(entities) : nothing;
}
const device = this.devices[valueId];
const device = this._registries.devices[valueId];
const isService = device.entry_type === "service";
const { entities } =
entries[`${isService ? "service" : "area"}${TARGET_SEPARATOR}`]
@@ -316,7 +301,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
)
: this._renderItem(
!floor.id
? this.localize(
? this._i18n.localize(
"ui.panel.config.automation.editor.other_areas"
)
: floor.primary,
@@ -337,7 +322,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
)
);
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.panel.config.automation.editor.home"
)}</ha-section-title
>
@@ -345,7 +330,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
? html`<ha-md-list>
<ha-md-list-item type="text">
<div slot="headline">
${this.localize("ui.components.area-picker.no_areas")}
${this._i18n.localize("ui.components.area-picker.no_areas")}
</div>
</ha-md-list-item>
</ha-md-list>`
@@ -362,9 +347,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
(narrow: boolean, value?: SingleHassServiceTarget) => {
const labels = this._getLabelsMemoized(
this.states,
this.areas,
this.devices,
this.entities,
this._registries.areas,
this._registries.devices,
this._registries.entities,
this._labelRegistry,
undefined,
undefined,
@@ -380,7 +365,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.components.label-picker.labels"
)}</ha-section-title
>
@@ -447,7 +432,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
const open = entries[`device${TARGET_SEPARATOR}`].open;
items.push(
this._renderItem(
this.localize("ui.components.target-picker.type.entities"),
this._i18n.localize("ui.components.target-picker.type.entities"),
`device${TARGET_SEPARATOR}`,
true,
false,
@@ -468,7 +453,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
const open = entries[`helper${TARGET_SEPARATOR}`].open;
items.push(
this._renderItem(
this.localize("ui.panel.config.automation.editor.helpers"),
this._i18n.localize("ui.panel.config.automation.editor.helpers"),
`helper${TARGET_SEPARATOR}`,
true,
false,
@@ -489,7 +474,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
const open = entries[`area${TARGET_SEPARATOR}`].open;
items.push(
this._renderItem(
this.localize("ui.components.target-picker.type.devices"),
this._i18n.localize("ui.components.target-picker.type.devices"),
`area${TARGET_SEPARATOR}`,
true,
false,
@@ -507,7 +492,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
const open = entries[`service${TARGET_SEPARATOR}`].open;
items.push(
this._renderItem(
this.localize("ui.panel.config.automation.editor.services"),
this._i18n.localize("ui.panel.config.automation.editor.services"),
`service${TARGET_SEPARATOR}`,
true,
false,
@@ -524,7 +509,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.panel.config.automation.editor.unassigned"
)}</ha-section-title
>${narrow
@@ -539,11 +524,11 @@ export default class HaAutomationAddFromTarget extends LitElement {
const renderedAreas = Object.keys(areas)
.filter((areaTargetId) => {
const [, areaId] = areaTargetId.split(TARGET_SEPARATOR, 2);
return this.areas[areaId];
return this._registries.areas[areaId];
})
.map((areaTargetId) => {
const [, areaId] = areaTargetId.split(TARGET_SEPARATOR, 2);
const area = this.areas[areaId];
const area = this._registries.areas[areaId];
return [
areaTargetId,
computeAreaName(area) || area.area_id,
@@ -578,7 +563,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
if (this.narrow) {
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.components.target-picker.type.areas"
)}</ha-section-title
>
@@ -590,9 +575,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
private _renderDevices(devices: Record<string, Level3Entries>) {
const renderedDevices = Object.keys(devices)
.filter((deviceId) => this.devices[deviceId])
.filter((deviceId) => this._registries.devices[deviceId])
.map((deviceId) => {
const device = this.devices[deviceId];
const device = this._registries.devices[deviceId];
const configEntry = device.primary_config_entry
? this._configEntryLookup?.[device.primary_config_entry]
: undefined;
@@ -627,7 +612,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
if (this.narrow) {
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.components.target-picker.type.devices"
)}</ha-section-title
>
@@ -648,7 +633,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
domainTargetId.length - TARGET_SEPARATOR.length
);
const label = domainToName(
this.localize,
this._i18n.localize,
domain,
this.manifests![domain]
);
@@ -674,7 +659,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
if (this.narrow) {
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.components.target-picker.type.devices"
)}</ha-section-title
>
@@ -697,16 +682,16 @@ export default class HaAutomationAddFromTarget extends LitElement {
const [entityName, deviceName] = computeEntityNameList(
stateObj,
[{ type: "entity" }, { type: "device" }, { type: "area" }],
this.entities,
this.devices,
this.areas,
this.floors
this._registries.entities,
this._registries.devices,
this._registries.areas,
this._registries.floors
);
let label = entityName || deviceName || entityId;
if (this.entities[entityId]?.hidden) {
label += ` (${this.localize("ui.panel.config.automation.editor.entity_hidden")})`;
if (this._registries.entities[entityId]?.hidden) {
label += ` (${this._i18n.localize("ui.panel.config.automation.editor.entity_hidden")})`;
}
return [entityId, label, stateObj] as [string, string, HassEntity];
@@ -729,7 +714,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
if (this.narrow) {
return html`<ha-section-title
>${this.localize(
>${this._i18n.localize(
"ui.components.target-picker.type.entities"
)}</ha-section-title
>
@@ -867,10 +852,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
private _getTreeData() {
this._floorAreas = getAreasNestedInFloors(
this.states,
this.floors,
this.areas,
this.devices,
this.entities,
this._registries.floors,
this._registries.areas,
this._registries.devices,
this._registries.entities,
this._formatId,
undefined,
undefined,
@@ -903,7 +888,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
private _loadUnassignedDevices() {
const unassignedDevices = Object.values(this.devices).filter(
const unassignedDevices = Object.values(this._registries.devices).filter(
(device) => !device.area_id
);
@@ -912,16 +897,16 @@ export default class HaAutomationAddFromTarget extends LitElement {
const services: Record<string, Level3Entries> = {};
unassignedDevices.forEach(({ id: deviceId, entry_type }) => {
const device = this.devices[deviceId];
const device = this._registries.devices[deviceId];
if (!device || device.disabled_by) {
return;
}
const deviceEntry = {
open: false,
entities:
this._getDeviceEntityLookupMemoized(this.entities)[deviceId]?.map(
(entity) => entity.entity_id
) || [],
this._getDeviceEntityLookupMemoized(this._registries.entities)[
deviceId
]?.map((entity) => entity.entity_id) || [],
};
if (entry_type === "service") {
services[deviceId] = deviceEntry;
@@ -953,7 +938,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
private _loadUnassignedEntities() {
Object.values(this.entities)
Object.values(this._registries.entities)
.filter((entity) => !entity.area_id && !entity.device_id)
.forEach(({ entity_id }) => {
const domain = entity_id.split(".", 2)[0];
@@ -1010,23 +995,23 @@ export default class HaAutomationAddFromTarget extends LitElement {
private _loadArea(area: FloorComboBoxItem) {
const [, id] = area.id.split(TARGET_SEPARATOR, 2);
const referenced_devices =
this._getAreaDeviceLookupMemoized(this.devices)[id] || [];
this._getAreaDeviceLookupMemoized(this._registries.devices)[id] || [];
const referenced_entities =
this._getAreaEntityLookupMemoized(this.entities)[id] || [];
this._getAreaEntityLookupMemoized(this._registries.entities)[id] || [];
const devices: Record<string, Level3Entries> = {};
referenced_devices.forEach(({ id: deviceId }) => {
const device = this.devices[deviceId];
const device = this._registries.devices[deviceId];
if (!device || device.disabled_by) {
return;
}
devices[deviceId] = {
open: false,
entities:
this._getDeviceEntityLookupMemoized(this.entities)[deviceId]?.map(
(entity) => entity.entity_id
) || [],
this._getDeviceEntityLookupMemoized(this._registries.entities)[
deviceId
]?.map((entity) => entity.entity_id) || [],
};
});
@@ -1051,17 +1036,17 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
if (type === "entity") {
const deviceId = this.entities[id]?.device_id;
const device = deviceId ? this.devices[deviceId] : undefined;
const deviceId = this._registries.entities[id]?.device_id;
const device = deviceId ? this._registries.devices[deviceId] : undefined;
const deviceAreaId = (deviceId && device?.area_id) || undefined;
if (!deviceAreaId) {
let floor: string;
let area: string;
const entity = this.entities[id];
const entity = this._registries.entities[id];
if (!deviceId && entity.area_id) {
floor = `floor${TARGET_SEPARATOR}${this.areas[entity.area_id]?.floor_id || ""}`;
floor = `floor${TARGET_SEPARATOR}${this._registries.areas[entity.area_id]?.floor_id || ""}`;
area = `area${TARGET_SEPARATOR}${entity.area_id}`;
} else if (!deviceId) {
const domain = id.split(".", 1)[0];
@@ -1093,7 +1078,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
return;
}
const floor = `floor${TARGET_SEPARATOR}${this.areas[deviceAreaId]?.floor_id || ""}`;
const floor = `floor${TARGET_SEPARATOR}${this._registries.areas[deviceAreaId]?.floor_id || ""}`;
const area = `area${TARGET_SEPARATOR}${deviceAreaId}`;
this._entries = {
@@ -1121,10 +1106,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
if (type === "device") {
const deviceAreaId = this.devices[id]?.area_id;
const deviceAreaId = this._registries.devices[id]?.area_id;
if (!deviceAreaId) {
const device = this.devices[id];
const device = this._registries.devices[id];
const floor = `${device.entry_type === "service" ? "service" : "area"}${TARGET_SEPARATOR}`;
this._entries = {
...this._entries,
@@ -1136,7 +1121,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
return;
}
const floor = `floor${TARGET_SEPARATOR}${this.areas[deviceAreaId]?.floor_id || ""}`;
const floor = `floor${TARGET_SEPARATOR}${this._registries.areas[deviceAreaId]?.floor_id || ""}`;
const area = `area${TARGET_SEPARATOR}${deviceAreaId}`;
this._entries = {
@@ -1157,7 +1142,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
if (type === "area") {
const floor = `floor${TARGET_SEPARATOR}${this.areas[id]?.floor_id || ""}`;
const floor = `floor${TARGET_SEPARATOR}${this._registries.areas[id]?.floor_id || ""}`;
this._entries = {
...this._entries,
[floor]: {
@@ -1231,7 +1216,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
if (type === "area" && id) {
const floorId = `floor${TARGET_SEPARATOR}${this.areas[id]?.floor_id || ""}`;
const floorId = `floor${TARGET_SEPARATOR}${this._registries.areas[id]?.floor_id || ""}`;
this._entries = {
...this._entries,
@@ -1272,10 +1257,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
if (type === "device" && id) {
const areaId = this.devices[id]?.area_id;
const areaId = this._registries.devices[id]?.area_id;
if (areaId) {
const areaTargetId = `area${TARGET_SEPARATOR}${this.devices[id]?.area_id ?? ""}`;
const floorId = `floor${TARGET_SEPARATOR}${(areaId && this.areas[areaId]?.floor_id) || ""}`;
const areaTargetId = `area${TARGET_SEPARATOR}${this._registries.devices[id]?.area_id ?? ""}`;
const floorId = `floor${TARGET_SEPARATOR}${(areaId && this._registries.areas[areaId]?.floor_id) || ""}`;
this._entries = {
...this._entries,
@@ -1300,7 +1285,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
}
const deviceType =
this.devices[id]?.entry_type === "service" ? "service" : "area";
this._registries.devices[id]?.entry_type === "service"
? "service"
: "area";
const floorId = `${deviceType}${TARGET_SEPARATOR}`;
this._entries = {
...this._entries,

View File

@@ -36,6 +36,7 @@ import type { HaAutomationRow } from "../../../../components/ha-automation-row";
import "../../../../components/ha-card";
import "../../../../components/ha-condition-icon";
import "../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
@@ -76,7 +77,6 @@ import "./types/ha-automation-condition-template";
import "./types/ha-automation-condition-time";
import "./types/ha-automation-condition-trigger";
import "./types/ha-automation-condition-zone";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
export interface ConditionElement extends LitElement {
condition: Condition;

View File

@@ -45,13 +45,13 @@ import type {
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import type {
HaDropdown,
HaDropdownSelectEvent,
} from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-button";
import "../../../components/ha-filter-blueprints";
import "../../../components/ha-filter-categories";
import "../../../components/ha-filter-devices";

View File

@@ -21,6 +21,7 @@ import "../../../../components/ha-automation-row";
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
@@ -47,7 +48,6 @@ import {
overflowStyles,
rowStyles,
} from "../styles";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
@customElement("ha-automation-option-row")
export default class HaAutomationOptionRow extends LitElement {

View File

@@ -1,4 +1,4 @@
import { consume } from "@lit/context";
import { consume, type ContextType } from "@lit/context";
import {
mdiAlert,
mdiAlertOctagon,
@@ -16,12 +16,10 @@ import { isTemplate } from "../../../../common/string/has-template";
import "../../../../components/ha-svg-icon";
import type { ConfigEntry } from "../../../../data/config_entries";
import {
areasContext,
configEntriesContext,
devicesContext,
floorsContext,
internationalizationContext,
labelsContext,
localizeContext,
registriesContext,
statesContext,
} from "../../../../data/context";
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
@@ -41,29 +39,20 @@ export class HaAutomationRowTargets extends LitElement {
public targetRequired = false;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: HomeAssistant["localize"];
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: floorsContext, subscribe: true })
private floors!: HomeAssistant["floors"];
@state()
@consume({ context: areasContext, subscribe: true })
private areas!: HomeAssistant["areas"];
@state()
@consume({ context: devicesContext, subscribe: true })
private devices!: HomeAssistant["devices"];
@state()
@consume({ context: statesContext, subscribe: true })
private states!: HomeAssistant["states"];
@consume({ context: registriesContext, subscribe: true })
private _registries!: ContextType<typeof registriesContext>;
@state()
@consume({ context: labelsContext, subscribe: true })
private _labelRegistry!: LabelRegistryEntry[];
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
@state()
@consume({ context: configEntriesContext, subscribe: true })
@transform<ConfigEntry[], Record<string, ConfigEntry>>({
@@ -82,7 +71,7 @@ export class HaAutomationRowTargets extends LitElement {
this.targetRequired
? html`<ha-svg-icon .path=${mdiAlertOctagon}></ha-svg-icon>`
: nothing,
this.localize(
this._i18n.localize(
"ui.panel.config.automation.editor.target_summary.no_target"
),
false,
@@ -124,7 +113,7 @@ export class HaAutomationRowTargets extends LitElement {
return html`<span class="target">
<ha-svg-icon .path=${mdiFormatListBulleted}></ha-svg-icon>
<div class="label">
${this.localize(
${this._i18n.localize(
"ui.panel.config.automation.editor.target_summary.targets",
{
count: totalLength,
@@ -142,16 +131,16 @@ export class HaAutomationRowTargets extends LitElement {
targetId: string
): boolean {
if (targetType === "floor") {
return !!this.floors[targetId];
return !!this._registries.floors[targetId];
}
if (targetType === "area") {
return !!this.areas[targetId];
return !!this._registries.areas[targetId];
}
if (targetType === "device") {
return !!this.devices[targetId];
return !!this._registries.devices[targetId];
}
if (targetType === "entity") {
return !!this.states[targetId];
return !!this._states[targetId];
}
if (targetType === "label") {
return !!this._getLabel(targetId);
@@ -178,7 +167,7 @@ export class HaAutomationRowTargets extends LitElement {
if (targetType === "entity" && ["all", "none"].includes(targetId)) {
return this._renderTargetBadge(
html`<ha-svg-icon .path=${mdiShape}></ha-svg-icon>`,
this.localize(
this._i18n.localize(
`ui.panel.config.automation.editor.target_summary.${targetId as "all" | "none"}_entities`
)
);
@@ -188,7 +177,7 @@ export class HaAutomationRowTargets extends LitElement {
if (isTemplate(targetId)) {
return this._renderTargetBadge(
html`<ha-svg-icon .path=${mdiCodeBraces}></ha-svg-icon>`,
this.localize(
this._i18n.localize(
"ui.panel.config.automation.editor.target_summary.template"
)
);

View File

@@ -39,6 +39,7 @@ import "../../../../components/ha-automation-row";
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
@@ -89,7 +90,6 @@ import "./types/ha-automation-trigger-time";
import "./types/ha-automation-trigger-time_pattern";
import "./types/ha-automation-trigger-webhook";
import "./types/ha-automation-trigger-zone";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
export interface TriggerElement extends LitElement {
trigger: Trigger;

View File

@@ -30,6 +30,7 @@ import "../../../layouts/hass-subpage";
import "../../../layouts/hass-tabs-subpage-data-table";
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-onboarding";
import "./components/overview/ha-backup-overview-progress";
@@ -87,7 +88,17 @@ class HaConfigBackupOverview extends LitElement {
}
fireEvent(this, "ha-refresh-backup-config");
await generateBackupWithAutomaticSettings(this.hass);
try {
await generateBackupWithAutomaticSettings(this.hass);
} catch (err: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.backup.overview.create_backup_failed"
),
text: err.message,
});
return;
}
fireEvent(this, "ha-refresh-backup-info");
}
@@ -109,23 +120,30 @@ class HaConfigBackupOverview extends LitElement {
return;
}
if (type === "manual") {
const params = await showGenerateBackupDialog(this, {
cloudStatus: this.cloudStatus,
});
try {
if (type === "manual") {
const params = await showGenerateBackupDialog(this, {
cloudStatus: this.cloudStatus,
});
if (!params) {
return;
if (!params) {
return;
}
await generateBackup(this.hass, params);
} else if (type === "automatic") {
await generateBackupWithAutomaticSettings(this.hass);
}
await generateBackup(this.hass, params);
fireEvent(this, "ha-refresh-backup-info");
} catch (err: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.backup.overview.create_backup_failed"
),
text: err.message,
});
return;
}
if (type === "automatic") {
await generateBackupWithAutomaticSettings(this.hass);
fireEvent(this, "ha-refresh-backup-info");
}
fireEvent(this, "ha-refresh-backup-info");
}
private get _needsOnboarding() {

View File

@@ -13,7 +13,7 @@ import type {
CategoryRegistryEntry,
CategoryRegistryEntryMutableParams,
} from "../../../data/category_registry";
import { localizeContext } from "../../../data/context";
import { internationalizationContext } from "../../../data/context";
import { DialogMixin } from "../../../dialogs/dialog-mixin";
import { haStyleDialog } from "../../../resources/styles";
import type { ValueChangedEvent } from "../../../types";
@@ -24,8 +24,8 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
LitElement
) {
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state() private _name!: string;
@@ -56,8 +56,8 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
<ha-dialog
open
header-title=${entry
? this.localize("ui.panel.config.category.editor.edit")
: this.localize("ui.panel.config.category.editor.create")}
? this._i18n.localize("ui.panel.config.category.editor.edit")
: this._i18n.localize("ui.panel.config.category.editor.create")}
prevent-scrim-close
>
${this._error
@@ -67,8 +67,10 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
<ha-input
.value=${this._name}
@input=${this._nameChanged}
.label=${this.localize("ui.panel.config.category.editor.name")}
.validationMessage=${this.localize(
.label=${this._i18n.localize(
"ui.panel.config.category.editor.name"
)}
.validationMessage=${this._i18n.localize(
"ui.panel.config.category.editor.required_error_msg"
)}
required
@@ -78,7 +80,9 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
<ha-icon-picker
.value=${this._icon ?? undefined}
@value-changed=${this._iconChanged}
.label=${this.localize("ui.panel.config.category.editor.icon")}
.label=${this._i18n.localize(
"ui.panel.config.category.editor.icon"
)}
></ha-icon-picker>
</div>
<ha-dialog-footer slot="footer">
@@ -87,7 +91,7 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
appearance="plain"
@click=${this.closeDialog}
>
${this.localize("ui.common.cancel")}
${this._i18n.localize("ui.common.cancel")}
</ha-button>
<ha-button
slot="primaryAction"
@@ -95,8 +99,8 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
.disabled=${nameInvalid || !!this._submitting}
>
${entry
? this.localize("ui.common.save")
: this.localize("ui.common.add")}
? this._i18n.localize("ui.common.save")
: this._i18n.localize("ui.common.add")}
</ha-button>
</ha-dialog-footer>
</ha-dialog>
@@ -135,7 +139,7 @@ class DialogCategoryDetail extends DialogMixin<CategoryRegistryDetailDialogParam
} catch (err: any) {
this._error =
err.message ||
this.localize("ui.panel.config.category.editor.unknown_error");
this._i18n.localize("ui.panel.config.category.editor.unknown_error");
} finally {
this._submitting = false;
}

View File

@@ -1,4 +1,5 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { ResizeController } from "@lit-labs/observers/resize-controller";
import { consume } from "@lit/context";
import {
mdiCancel,
@@ -10,7 +11,6 @@ import {
} from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { ResizeController } from "@lit-labs/observers/resize-controller";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { storage } from "../../../common/decorators/storage";
@@ -37,10 +37,11 @@ import type {
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/entity/ha-battery-icon";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-check-list-item";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-button";
import "../../../components/ha-filter-devices";
import "../../../components/ha-filter-floor-areas";
import "../../../components/ha-filter-integrations";
@@ -83,18 +84,17 @@ import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
import {
getAreaTableColumn,
getCreatedAtTableColumn,
getFloorTableColumn,
getLabelsTableColumn,
getModifiedAtTableColumn,
} from "../common/data-table-columns";
import { configSections } from "../ha-panel-config";
import "../integrations/ha-integration-overflow-menu";
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
getAreaTableColumn,
getFloorTableColumn,
getLabelsTableColumn,
getCreatedAtTableColumn,
getModifiedAtTableColumn,
} from "../common/data-table-columns";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
interface DeviceRowData extends DeviceRegistryEntry {
device?: DeviceRowData;

View File

@@ -55,9 +55,10 @@ import type {
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-check-list-item";
import "../../../components/ha-dropdown";
import "../../../components/ha-button";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-filter-devices";
import "../../../components/ha-filter-domains";
@@ -109,26 +110,25 @@ import "../../../layouts/hass-tabs-subpage-data-table";
import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import {
getAreaTableColumn,
getCreatedAtTableColumn,
getDomainTableColumn,
getEntityIdTableColumn,
getLabelsTableColumn,
getModifiedAtTableColumn,
} from "../common/data-table-columns";
import { configSections } from "../ha-panel-config";
import type { Helper } from "../helpers/const";
import { isHelperDomain } from "../helpers/const";
import "../integrations/ha-integration-overflow-menu";
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
getEntityIdTableColumn,
getDomainTableColumn,
getAreaTableColumn,
getLabelsTableColumn,
getCreatedAtTableColumn,
getModifiedAtTableColumn,
} from "../common/data-table-columns";
import {
getAssistantsSortableKey,
getAssistantsTableColumn,
} from "../voice-assistants/expose/assistants-table-column";
import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
export interface StateEntity extends Omit<
EntityRegistryEntry,

View File

@@ -42,9 +42,10 @@ import type {
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-filter-categories";
import "../../../components/ha-filter-devices";
import "../../../components/ha-filter-entities";
@@ -112,15 +113,15 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { fileDownload } from "../../../util/file_download";
import {
getEntityIdTableColumn,
getAreaTableColumn,
getCategoryTableColumn,
getLabelsTableColumn,
getEditableTableColumn,
} from "../common/data-table-columns";
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import {
getAreaTableColumn,
getCategoryTableColumn,
getEditableTableColumn,
getEntityIdTableColumn,
getLabelsTableColumn,
} from "../common/data-table-columns";
import { configSections } from "../ha-panel-config";
import { renderConfigEntryError } from "../integrations/ha-config-integration-page";
import "../integrations/ha-integration-overflow-menu";
@@ -132,7 +133,6 @@ import {
import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants";
import { isHelperDomain, type HelperDomain } from "./const";
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
interface HelperItem {
id: string;

View File

@@ -17,12 +17,10 @@ import "../../../../../../components/ha-md-list";
import "../../../../../../components/ha-md-list-item";
import "../../../../../../components/ha-spinner";
import {
areasContext,
configEntriesContext,
devicesContext,
entitiesContext,
localeContext,
localizeContext,
internationalizationContext,
registriesContext,
statesContext,
} from "../../../../../../data/context";
import {
@@ -38,8 +36,8 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
LitElement
) {
@state()
@consume({ context: localizeContext, subscribe: true })
private _localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: configEntriesContext, subscribe: true })
@@ -84,14 +82,8 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
@consume({ context: entitiesContext, subscribe: true })
private _entities!: ContextType<typeof entitiesContext>;
@consume({ context: localeContext, subscribe: true })
private _locale!: ContextType<typeof localeContext>;
@consume({ context: areasContext, subscribe: true })
private _areas!: ContextType<typeof areasContext>;
@consume({ context: registriesContext, subscribe: true })
private _registries!: ContextType<typeof registriesContext>;
protected render() {
if (!this.params) {
@@ -102,7 +94,7 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
<ha-dialog
open
width="small"
.headerTitle=${this._localize(
.headerTitle=${this._i18n.localize(
`ui.panel.config.zwave_js.rebuild_network_routes.details.${this.params.type}`,
{
count: this._progress ? this._progress.length : 0,
@@ -117,7 +109,7 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
`
: !this._progress || this._progress.length === 0
? html`<p>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.details.no_devices"
)}
</p>`
@@ -165,7 +157,7 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
});
const deviceEntityLookup = getDeviceEntityDisplayLookup(
Object.values(this._entities)
Object.values(this._registries.entities)
);
const configEntryLookup = Object.fromEntries(
@@ -177,12 +169,13 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
const name =
computeDeviceNameDisplay(
device,
this._localize,
this._i18n.localize,
this._states,
deviceEntityLookup[device.id]
) || this._localize("ui.components.device-picker.unnamed_device");
) ||
this._i18n.localize("ui.components.device-picker.unnamed_device");
const area = getDeviceArea(device, this._areas);
const area = getDeviceArea(device, this._registries.areas);
const areaName = area ? computeAreaName(area) : undefined;
@@ -200,7 +193,7 @@ class DialogZWaveJSRebuildNetworkRoutesDetail extends DialogMixin<ZWaveJSRebuild
caseInsensitiveStringCompare(
[entry1.name, entry1.areaName].filter(Boolean).join(" "),
[entry2.name, entry2.areaName].filter(Boolean).join(" "),
this._locale.language
this._i18n.locale.language
)
);
}

View File

@@ -21,7 +21,7 @@ import "../../../../../../components/ha-spinner";
import "../../../../../../components/progress/ha-progress-bar";
import {
connectionContext,
localizeContext,
internationalizationContext,
} from "../../../../../../data/context";
import type {
ZWaveJSNetwork,
@@ -46,8 +46,8 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
LitElement
) {
@state()
@consume({ context: localizeContext, subscribe: true })
private _localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state()
@consume({ context: connectionContext, subscribe: true })
@@ -81,7 +81,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
return html`
<ha-dialog
open
header-title=${this._localize(
header-title=${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.title"
)}
>
@@ -100,7 +100,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
></ha-svg-icon>
<div class="status">
<p>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.introduction"
)}
</p>
@@ -108,7 +108,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
</div>
<p>
<em>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.traffic_warning"
)}
</em>
@@ -119,13 +119,13 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
<div class="status">
<p>
<b>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.in_progress"
)}
</b>
</p>
<p>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.run_in_background"
)}
</p>
@@ -146,7 +146,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
appearance="outlined"
variant="warning"
size="small"
.title=${this._localize(
.title=${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.progress.in_progress",
{ count: this._progress.pending.length }
)}
@@ -163,7 +163,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
appearance="outlined"
variant="success"
size="small"
.title=${this._localize(
.title=${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.progress.completed",
{ count: this._progress.done.length }
)}
@@ -182,7 +182,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
appearance="outlined"
variant="danger"
size="small"
.title=${this._localize(
.title=${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.progress.failed",
{ count: this._progress.failed.length }
)}
@@ -201,7 +201,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
appearance="outlined"
variant="neutral"
size="small"
.title=${this._localize(
.title=${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.progress.skipped",
{ count: this._progress.skipped.length }
)}
@@ -225,7 +225,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
></ha-svg-icon>
<div class="status">
<p>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.rebuilding_routes_failed"
)}
</p>
@@ -241,7 +241,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
></ha-svg-icon>
<div class="status">
<p>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.rebuilding_routes_complete"
)}
</p>
@@ -257,7 +257,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
></ha-svg-icon>
<div class="status">
<p>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.rebuilding_routes_cancelled"
)}
</p>
@@ -272,7 +272,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
slot="primaryAction"
@click=${this._startRebuildingRoutes}
>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.start_rebuilding_routes"
)}
</ha-button>
@@ -285,17 +285,17 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
@click=${this._stopRebuildingRoutes}
variant="danger"
>
${this._localize(
${this._i18n.localize(
"ui.panel.config.zwave_js.rebuild_network_routes.stop_rebuilding_routes"
)}
</ha-button>
<ha-button slot="primaryAction" @click=${this.closeDialog}>
${this._localize("ui.common.close")}
${this._i18n.localize("ui.common.close")}
</ha-button>
`
: html`
<ha-button slot="primaryAction" @click=${this.closeDialog}>
${this._localize("ui.common.close")}
${this._i18n.localize("ui.common.close")}
</ha-button>
`}
</ha-dialog-footer>
@@ -305,7 +305,7 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
private async _fetchData(): Promise<void> {
const network: ZWaveJSNetwork = await fetchZwaveNetworkStatus(
this._connection,
this._connection.connection,
{
entry_id: this.params!.entry_id,
}
@@ -319,21 +319,27 @@ class DialogZWaveJSRebuildNetworkRoutes extends DialogMixin<ZWaveJSRebuildNetwor
}
private _startRebuildingRoutes(): void {
rebuildZwaveNetworkRoutes(this._connection, this.params!.entry_id);
rebuildZwaveNetworkRoutes(
this._connection.connection,
this.params!.entry_id
);
this._status = "started";
this._startSubscribingToProgress();
}
private async _startSubscribingToProgress() {
this._subscribed = subscribeRebuildZwaveNetworkRoutesProgress(
this._connection,
this._connection.connection,
this.params!.entry_id,
this._handleMessage
);
}
private _stopRebuildingRoutes(): void {
stopRebuildingZwaveNetworkRoutes(this._connection, this.params!.entry_id);
stopRebuildingZwaveNetworkRoutes(
this._connection.connection,
this.params!.entry_id
);
this._unsubscribe();
this._status = "cancelled";
}

View File

@@ -11,7 +11,7 @@ import "../../../components/ha-icon-picker";
import "../../../components/ha-switch";
import "../../../components/ha-textarea";
import "../../../components/input/ha-input";
import { localizeContext } from "../../../data/context";
import { internationalizationContext } from "../../../data/context";
import type { LabelRegistryEntryMutableParams } from "../../../data/label/label_registry";
import { DialogMixin } from "../../../dialogs/dialog-mixin";
import { haStyleDialog } from "../../../resources/styles";
@@ -22,8 +22,8 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
LitElement
) {
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: ContextType<typeof localizeContext>;
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
@state() private _name!: string;
@@ -62,7 +62,7 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
open
header-title=${this.params.entry
? this.params.entry.name || this.params.entry.label_id
: this.localize("ui.dialogs.label-detail.new_label")}
: this._i18n.localize("ui.dialogs.label-detail.new_label")}
prevent-scrim-close
>
<div>
@@ -75,8 +75,8 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
.value=${this._name}
.configValue=${"name"}
@input=${this._input}
.label=${this.localize("ui.dialogs.label-detail.name")}
.validationMessage=${this.localize(
.label=${this._i18n.localize("ui.dialogs.label-detail.name")}
.validationMessage=${this._i18n.localize(
"ui.dialogs.label-detail.required_error_msg"
)}
required
@@ -85,19 +85,21 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
.value=${this._icon}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
.label=${this.localize("ui.dialogs.label-detail.icon")}
.label=${this._i18n.localize("ui.dialogs.label-detail.icon")}
></ha-icon-picker>
<ha-color-picker
.value=${this._color}
.configValue=${"color"}
@value-changed=${this._valueChanged}
.label=${this.localize("ui.dialogs.label-detail.color")}
.label=${this._i18n.localize("ui.dialogs.label-detail.color")}
></ha-color-picker>
<ha-textarea
.value=${this._description}
.configValue=${"description"}
@input=${this._input}
.label=${this.localize("ui.dialogs.label-detail.description")}
.label=${this._i18n.localize(
"ui.dialogs.label-detail.description"
)}
></ha-textarea>
</div>
</div>
@@ -112,7 +114,7 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
@click=${this._deleteEntry}
.disabled=${this._submitting}
>
${this.localize("ui.common.delete")}
${this._i18n.localize("ui.common.delete")}
</ha-button>
`
: html`
@@ -121,7 +123,7 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
slot="secondaryAction"
@click=${this.closeDialog}
>
${this.localize("ui.common.cancel")}
${this._i18n.localize("ui.common.cancel")}
</ha-button>
`}
<ha-button
@@ -130,8 +132,8 @@ class DialogLabelDetail extends DialogMixin<LabelDetailDialogParams>(
.disabled=${this._submitting || !this._name}
>
${this.params.entry
? this.localize("ui.common.update")
: this.localize("ui.common.create")}
? this._i18n.localize("ui.common.update")
: this._i18n.localize("ui.common.create")}
</ha-button>
</ha-dialog-footer>
</ha-dialog>

View File

@@ -41,9 +41,10 @@ import type {
SortingChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-filter-blueprints";
import "../../../components/ha-filter-categories";
import "../../../components/ha-filter-devices";
@@ -106,9 +107,9 @@ import { showNewAutomationDialog } from "../automation/show-dialog-new-automatio
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import {
getEntityIdHiddenTableColumn,
getAreaTableColumn,
getCategoryTableColumn,
getEntityIdHiddenTableColumn,
getLabelsTableColumn,
getTriggeredAtTableColumn,
} from "../common/data-table-columns";
@@ -119,7 +120,6 @@ import {
getAssistantsTableColumn,
} from "../voice-assistants/expose/assistants-table-column";
import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
type ScriptItem = ScriptEntity & {
name: string;

View File

@@ -1,4 +1,5 @@
import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume } from "@lit/context";
import {
mdiDotsVertical,
mdiDownload,
@@ -13,13 +14,13 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat";
import { consume } from "@lit/context";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import "../../../components/ha-button";
import "../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/trace/ha-trace-blueprint-config";
@@ -33,6 +34,7 @@ import type {
NodeInfo,
} from "../../../components/trace/hat-script-graph";
import { traceTabStyles } from "../../../components/trace/trace-tab-styles";
import { fullEntitiesContext } from "../../../data/context";
import type { EntityRegistryEntry } from "../../../data/entity/entity_registry";
import type { LogbookEntry } from "../../../data/logbook";
import { getLogbookDataForContext } from "../../../data/logbook";
@@ -44,8 +46,6 @@ import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { fileDownload } from "../../../util/file_download";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
import { fullEntitiesContext } from "../../../data/context";
@customElement("ha-script-trace")
export class HaScriptTrace extends LitElement {

View File

@@ -49,19 +49,19 @@ import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import {
getEntityIdTableColumn,
getDomainTableColumn,
getAreaTableColumn,
getDomainTableColumn,
getEntityIdTableColumn,
} from "../common/data-table-columns";
import "./expose/expose-assistant-icon";
import {
getAssistantsTableColumn,
getAssistantsSortableKey,
getAssistantsTableColumn,
} from "./expose/assistants-table-column";
import { getAvailableAssistants } from "./expose/available-assistants";
import "./expose/expose-assistant-icon";
import { voiceAssistantTabs } from "./ha-config-voice-assistants";
import { showExposeEntityDialog } from "./show-dialog-expose-entity";
import { showVoiceSettingsDialog } from "./show-dialog-voice-settings";
import { getAvailableAssistants } from "./expose/available-assistants";
@customElement("ha-config-voice-assistants-expose")
export class VoiceAssistantsExpose extends LitElement {

View File

@@ -95,13 +95,12 @@ export class DialogEditHome
.hass=${this.hass}
.open=${this._open}
.headerTitle=${this.hass.localize("ui.panel.home.editor.title")}
.headerSubtitle=${this.hass.localize(
"ui.panel.home.editor.description"
)}
prevent-scrim-close
@closed=${this._dialogClosed}
>
<p class="description">
${this.hass.localize("ui.panel.home.editor.description")}
</p>
<ha-entities-picker
autofocus
.hass=${this.hass}
@@ -264,11 +263,6 @@ export class DialogEditHome
--dialog-content-padding: var(--ha-space-6);
}
.description {
margin: 0 0 var(--ha-space-4) 0;
color: var(--secondary-text-color);
}
.section-header {
font-size: 16px;
font-weight: 500;

View File

@@ -1,9 +1,5 @@
import { consume } from "@lit/context";
import type {
HassConfig,
HassEntities,
HassEntity,
} from "home-assistant-js-websocket";
import { consume, type ContextType } from "@lit/context";
import type { HassEntities, HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, state } from "lit/decorators";
@@ -23,23 +19,18 @@ import {
} from "../../../common/entity/state_color";
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
import { iconColorCSS } from "../../../common/style/icon_color_css";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/ha-card";
import "../../../components/ha-ripple";
import { CLIMATE_HVAC_ACTION_TO_MODE } from "../../../data/climate";
import {
configContext,
entitiesContext,
localeContext,
localizeContext,
statesContext,
themesContext,
uiContext,
} from "../../../data/context";
import type { EntityRegistryDisplayEntry } from "../../../data/entity/entity_registry";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { FrontendLocaleData } from "../../../data/translation";
import type { Themes } from "../../../data/ws-themes";
import type { HomeAssistant } from "../../../types";
import type { HomeAssistant, HomeAssistantUI } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { findEntities } from "../common/find-entities";
import { hasAction } from "../common/has-action";
@@ -113,24 +104,15 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
private _stateObj?: HassEntity;
@state()
@consume({ context: themesContext, subscribe: true })
_themes!: Themes;
@state()
@consume({ context: localizeContext, subscribe: true })
_localize!: LocalizeFunc;
@state()
@consume({ context: localeContext, subscribe: true })
_locale!: FrontendLocaleData;
@state()
@consume({ context: configContext, subscribe: true })
_hassConfig!: HassConfig;
@consume({ context: uiContext, subscribe: true })
@transform<HomeAssistantUI, Themes>({
transformer: ({ themes }) => themes,
})
private _themes!: Themes;
@state()
@consume<any>({ context: entitiesContext, subscribe: true })
@transform<HomeAssistant["entities"], EntityRegistryDisplayEntry>({
@transform<ContextType<typeof entitiesContext>, EntityRegistryDisplayEntry>({
transformer: function (this: HuiButtonCard, value) {
return this._config?.entity ? value?.[this._config?.entity] : undefined;
},
@@ -184,7 +166,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
}
protected render() {
if (!this._config || !this._localize || !this._locale) {
if (!this._config) {
return nothing;
}
const stateObj = this._stateObj;

View File

@@ -89,7 +89,6 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
debugConnection: __DEV__,
suspendWhenHidden: true,
enableShortcuts: true,
moreInfoEntityId: null,
hassUrl: (path = "") =>
addBrandsAuth(
new URL(path, auth.data.hassUrl).toString(),

View File

@@ -4,25 +4,33 @@ import {
type ConfigEntryUpdate,
} from "../data/config_entries";
import {
apiContext,
areasContext,
authContext,
configContext,
configEntriesContext,
configSingleContext,
connectionContext,
connectionSingleContext,
devicesContext,
entitiesContext,
floorsContext,
fullEntitiesContext,
internationalizationContext,
labelsContext,
localeContext,
localizeContext,
panelsContext,
registriesContext,
selectedThemeContext,
servicesContext,
statesContext,
themesContext,
uiContext,
userContext,
userDataContext,
} from "../data/context";
import { updateHassGroups } from "../data/context/updateContext";
import { subscribeEntityRegistry } from "../data/entity/entity_registry";
import { subscribeLabelRegistry } from "../data/label/label_registry";
import type { Constructor, HomeAssistant } from "../types";
@@ -33,20 +41,60 @@ export const contextMixin = <T extends Constructor<HassBaseEl>>(
superClass: T
) =>
class extends superClass {
private __hassContextProviderGroups: Record<string, ContextProvider<any>> =
{
registries: new ContextProvider(this, {
context: registriesContext,
initialValue: updateHassGroups.registries(
this.hass || (this._pendingHass as HomeAssistant)
),
}),
internationalization: new ContextProvider(this, {
context: internationalizationContext,
initialValue: updateHassGroups.internationalization(
this.hass || (this._pendingHass as HomeAssistant)
),
}),
api: new ContextProvider(this, {
context: apiContext,
initialValue: updateHassGroups.api(
this.hass || (this._pendingHass as HomeAssistant)
),
}),
connection: new ContextProvider(this, {
context: connectionContext,
initialValue: updateHassGroups.connection(
this.hass || (this._pendingHass as HomeAssistant)
),
}),
ui: new ContextProvider(this, {
context: uiContext,
initialValue: updateHassGroups.ui(
this.hass || (this._pendingHass as HomeAssistant)
),
}),
config: new ContextProvider(this, {
context: configContext,
initialValue: updateHassGroups.config(
this.hass || (this._pendingHass as HomeAssistant)
),
}),
};
private __contextProviders: Record<
string,
ContextProvider<any> | undefined
> = {
connection: new ContextProvider(this, {
context: connectionContext,
initialValue: this.hass
? this.hass.connection
: this._pendingHass.connection,
}),
states: new ContextProvider(this, {
context: statesContext,
initialValue: this.hass ? this.hass.states : this._pendingHass.states,
}),
services: new ContextProvider(this, {
context: servicesContext,
initialValue: this.hass
? this.hass.services
: this._pendingHass.services,
}),
entities: new ContextProvider(this, {
context: entitiesContext,
initialValue: this.hass
@@ -61,6 +109,16 @@ export const contextMixin = <T extends Constructor<HassBaseEl>>(
context: areasContext,
initialValue: this.hass ? this.hass.areas : this._pendingHass.areas,
}),
floors: new ContextProvider(this, {
context: floorsContext,
initialValue: this.hass ? this.hass.floors : this._pendingHass.floors,
}),
connection: new ContextProvider(this, {
context: connectionSingleContext,
initialValue: this.hass
? this.hass.connection
: this._pendingHass.connection,
}),
localize: new ContextProvider(this, {
context: localizeContext,
initialValue: this.hass
@@ -72,7 +130,7 @@ export const contextMixin = <T extends Constructor<HassBaseEl>>(
initialValue: this.hass ? this.hass.locale : this._pendingHass.locale,
}),
config: new ContextProvider(this, {
context: configContext,
context: configSingleContext,
initialValue: this.hass ? this.hass.config : this._pendingHass.config,
}),
themes: new ContextProvider(this, {
@@ -99,10 +157,6 @@ export const contextMixin = <T extends Constructor<HassBaseEl>>(
context: panelsContext,
initialValue: this.hass ? this.hass.panels : this._pendingHass.panels,
}),
floors: new ContextProvider(this, {
context: floorsContext,
initialValue: this.hass ? this.hass.floors : this._pendingHass.floors,
}),
auth: new ContextProvider(this, {
context: authContext,
initialValue: this.hass?.auth,
@@ -151,6 +205,20 @@ export const contextMixin = <T extends Constructor<HassBaseEl>>(
protected _updateHass(obj: Partial<HomeAssistant>) {
super._updateHass(obj);
for (const groupProvider of Object.keys(
this.__hassContextProviderGroups
)) {
if (groupProvider in updateHassGroups) {
this.__hassContextProviderGroups[groupProvider]!.setValue(
updateHassGroups[groupProvider]!(
this.hass!,
this.__hassContextProviderGroups[groupProvider]!.value
)
);
}
}
for (const [key, value] of Object.entries(obj)) {
if (key in this.__contextProviders) {
this.__contextProviders[key]!.setValue(value);

View File

@@ -3386,6 +3386,7 @@
"upload_backup": "Upload backup"
},
"agent_error": "Error in location {name}",
"create_backup_failed": "Failed to create backup",
"new_backup": "Backup now",
"onboarding": {
"title": "Set up backups",

View File

@@ -225,21 +225,14 @@ export interface ServiceCallRequest {
target?: HassServiceTarget;
}
export interface HomeAssistant {
auth: Auth & { external?: ExternalMessaging };
connection: Connection;
connected: boolean;
states: HassEntities;
export interface HomeAssistantRegistries {
entities: Record<string, EntityRegistryDisplayEntry>;
devices: Record<string, DeviceRegistryEntry>;
areas: Record<string, AreaRegistryEntry>;
floors: Record<string, FloorRegistryEntry>;
services: HassServices;
config: HassConfig;
themes: Themes;
selectedTheme: ThemeSettings | null;
panels: Panels;
panelUrl: string;
}
export interface HomeAssistantInternationalization {
// i18n
// current effective language in that order:
// - backend saved user selected language
@@ -250,20 +243,17 @@ export interface HomeAssistant {
// local stored language, keep that name for backward compatibility
selectedLanguage: string | null;
locale: FrontendLocaleData;
resources: Resources;
localize: LocalizeFunc;
translationMetadata: TranslationMetadata;
suspendWhenHidden: boolean;
enableShortcuts: boolean;
vibrate: boolean;
debugConnection: boolean;
kioskMode: boolean;
dockedSidebar: "docked" | "always_hidden" | "auto";
moreInfoEntityId: string | null;
user?: CurrentUser;
userData?: CoreFrontendUserData;
systemData?: CoreFrontendSystemData;
hassUrl(path?): string;
loadBackendTranslation(
category: Parameters<typeof getHassTranslations>[2],
integrations?: Parameters<typeof getHassTranslations>[3],
configFlow?: Parameters<typeof getHassTranslations>[4]
): Promise<LocalizeFunc>;
loadFragmentTranslation(fragment: string): Promise<LocalizeFunc | undefined>;
}
export interface HomeAssistantApi {
callService<T = any>(
domain: ServiceCallRequest["domain"],
service: ServiceCallRequest["service"],
@@ -288,12 +278,9 @@ export interface HomeAssistant {
fetchWithAuth(path: string, init?: Record<string, any>): Promise<Response>;
sendWS(msg: MessageBase): void;
callWS<T>(msg: MessageBase): Promise<T>;
loadBackendTranslation(
category: Parameters<typeof getHassTranslations>[2],
integrations?: Parameters<typeof getHassTranslations>[3],
configFlow?: Parameters<typeof getHassTranslations>[4]
): Promise<LocalizeFunc>;
loadFragmentTranslation(fragment: string): Promise<LocalizeFunc | undefined>;
}
export interface HomeAssistantFormatters {
formatEntityState(stateObj: HassEntity, state?: string): string;
formatEntityStateToParts(stateObj: HassEntity, state?: string): ValuePart[];
formatEntityAttributeValue(
@@ -314,6 +301,47 @@ export interface HomeAssistant {
): string;
}
export interface HomeAssistantConnection {
connection: Connection;
connected: boolean;
debugConnection: boolean;
hassUrl(path?): string;
}
export interface HomeAssistantUI {
themes: Themes;
selectedTheme: ThemeSettings | null;
panels: Panels;
panelUrl: string;
dockedSidebar: "docked" | "always_hidden" | "auto";
kioskMode: boolean;
enableShortcuts: boolean;
vibrate: boolean;
suspendWhenHidden: boolean;
}
export interface HomeAssistantConfig {
auth: Auth & { external?: ExternalMessaging };
config: HassConfig;
user?: CurrentUser;
userData?: CoreFrontendUserData;
systemData?: CoreFrontendSystemData;
}
export interface HomeAssistant
extends
HomeAssistantRegistries,
HomeAssistantInternationalization,
HomeAssistantApi,
HomeAssistantFormatters,
HomeAssistantConnection,
HomeAssistantUI,
HomeAssistantConfig {
states: HassEntities;
services: HassServices;
resources: Resources;
}
export interface Route {
prefix: string;
path: string;

331
yarn.lock
View File

@@ -3935,22 +3935,22 @@ __metadata:
languageName: node
linkType: hard
"@sinonjs/fake-timers@npm:^15.1.1":
version: 15.1.1
resolution: "@sinonjs/fake-timers@npm:15.1.1"
"@sinonjs/fake-timers@npm:^15.3.0":
version: 15.3.0
resolution: "@sinonjs/fake-timers@npm:15.3.0"
dependencies:
"@sinonjs/commons": "npm:^3.0.1"
checksum: 10/f262d613ea7f7cdb1b5d90c0cae01b7c6b797d6d0f1ca0fe30b7b69012e3076bb8a0f69d735bc69d2824b9bb1efb8554ca9765b4a6bb22defdec9ce79e7cd8a4
checksum: 10/657766c969f61af47d480e63112c126bb3088d3b0cfd6a609c90979971c5e98f388c189b7c46c05525edaa543f91d627d01b0007705eac679f7cdf4f66b1959e
languageName: node
linkType: hard
"@sinonjs/samsam@npm:^9.0.3":
version: 9.0.3
resolution: "@sinonjs/samsam@npm:9.0.3"
"@sinonjs/samsam@npm:^10.0.0":
version: 10.0.1
resolution: "@sinonjs/samsam@npm:10.0.1"
dependencies:
"@sinonjs/commons": "npm:^3.0.1"
type-detect: "npm:^4.1.0"
checksum: 10/bbb94c9d5e4a64dccf7de7679d17434aae1c16f209b25534a75eea2b0fb37b4d55829116c51fb24aa83f180a2c698fcdf9444747bd436726068cbc419c89aa2a
checksum: 10/b5eac2408a30aba64d0d5bfbde0bc422354793e4ac19be46cb1b01f754f56e7dd69766739b62a849d93f03eeb14cc93f180e2a79a05f6ec05789e2b95bb89421
languageName: node
linkType: hard
@@ -4688,105 +4688,105 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.58.0"
"@typescript-eslint/eslint-plugin@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/eslint-plugin@npm:8.58.1"
dependencies:
"@eslint-community/regexpp": "npm:^4.12.2"
"@typescript-eslint/scope-manager": "npm:8.58.0"
"@typescript-eslint/type-utils": "npm:8.58.0"
"@typescript-eslint/utils": "npm:8.58.0"
"@typescript-eslint/visitor-keys": "npm:8.58.0"
"@typescript-eslint/scope-manager": "npm:8.58.1"
"@typescript-eslint/type-utils": "npm:8.58.1"
"@typescript-eslint/utils": "npm:8.58.1"
"@typescript-eslint/visitor-keys": "npm:8.58.1"
ignore: "npm:^7.0.5"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.5.0"
peerDependencies:
"@typescript-eslint/parser": ^8.58.0
"@typescript-eslint/parser": ^8.58.1
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/0b1f4d4e62279fc5925c0a89d94816db0e236648eb4aec522cf22071877c6ecc63146b6830a1075049f402b842f45d7332ce6ae67883639754dcbb1881d07c34
checksum: 10/0fcbe6faadb77313aa91c895c977a24fc72a79eed62f46f7b2d5804db52a9af99351b33b9c4d73fdabb0f69772d5d4a9acdef249a0d1526a44d3817fb51419b5
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/parser@npm:8.58.0"
"@typescript-eslint/parser@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/parser@npm:8.58.1"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.58.0"
"@typescript-eslint/types": "npm:8.58.0"
"@typescript-eslint/typescript-estree": "npm:8.58.0"
"@typescript-eslint/visitor-keys": "npm:8.58.0"
"@typescript-eslint/scope-manager": "npm:8.58.1"
"@typescript-eslint/types": "npm:8.58.1"
"@typescript-eslint/typescript-estree": "npm:8.58.1"
"@typescript-eslint/visitor-keys": "npm:8.58.1"
debug: "npm:^4.4.3"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/0498e593b14841023b7495544637acaca84807de1ac17174fa9331af61ba35791da919e16680c6897002b4a843d5f0fe812c460a28a65fca7e3ae8e5971baf7a
checksum: 10/062584d26609e82169459ebf0c59f4925ba6596f4ea1637a320c34a25c34117585c458b9c6c268f5eeaee1988f4c7257d34d4bd05a214a88de12110e71b48493
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/project-service@npm:8.58.0"
"@typescript-eslint/project-service@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/project-service@npm:8.58.1"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.58.0"
"@typescript-eslint/types": "npm:^8.58.0"
"@typescript-eslint/tsconfig-utils": "npm:^8.58.1"
"@typescript-eslint/types": "npm:^8.58.1"
debug: "npm:^4.4.3"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/fab2601f76b2df61b09e3b7ff364d0e17e6d80e65e84e8a8d11f6a0813748bed3912da098659d00f46b1f277d462bd7529157182b72b5e2e0b41ee6176a0edd7
checksum: 10/2f3136268fc262e77e8c8c14291e60c54e0228b63ccb022826b6def6d80b83ce9c3a92fef11c888889fb204343c845556868c49495c3aa0a115e9a861dd5fe99
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/scope-manager@npm:8.58.0"
"@typescript-eslint/scope-manager@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/scope-manager@npm:8.58.1"
dependencies:
"@typescript-eslint/types": "npm:8.58.0"
"@typescript-eslint/visitor-keys": "npm:8.58.0"
checksum: 10/97293f1215faa785a3c1ee8d630591db9dcd5fb6bdcdd0b2e818c80478d41e59a05003fb33000530780dc466fb8cf662352932080ee7406c4aaac72af4000541
"@typescript-eslint/types": "npm:8.58.1"
"@typescript-eslint/visitor-keys": "npm:8.58.1"
checksum: 10/dc070fd73847807e32cb7dfc37512abd0b1a485b0037d8cfb6c593555a5b673d3ee9d19c61504ea71d067ad610c66f64d70d56f3a5db51895c0a25e45621cd08
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.58.0, @typescript-eslint/tsconfig-utils@npm:^8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.58.0"
"@typescript-eslint/tsconfig-utils@npm:8.58.1, @typescript-eslint/tsconfig-utils@npm:^8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/tsconfig-utils@npm:8.58.1"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/4f47212c0e26e6b06e97044ec5e483007d5145ef6b205393a0b43cbc0b385c75c14ba5749d01cf7d1ff100332c2cf1d336f060f7d2191bb67fb892bb4446afaa
checksum: 10/4a5cf9a5eb834d05f2d37f7d80319575cf4a75aa52807b96edc0db24349ba417b41cb6f5257ffb07b8b9b4c59c7438637e8c75ed7c2b513bcb07e259b49e058e
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/type-utils@npm:8.58.0"
"@typescript-eslint/type-utils@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/type-utils@npm:8.58.1"
dependencies:
"@typescript-eslint/types": "npm:8.58.0"
"@typescript-eslint/typescript-estree": "npm:8.58.0"
"@typescript-eslint/utils": "npm:8.58.0"
"@typescript-eslint/types": "npm:8.58.1"
"@typescript-eslint/typescript-estree": "npm:8.58.1"
"@typescript-eslint/utils": "npm:8.58.1"
debug: "npm:^4.4.3"
ts-api-utils: "npm:^2.5.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/da09868cd0b2cebb8cc4494e73aeed3997a7a4ff899fef496c54731610af1f92f8e3169f9b7f23060105db892f3bc880aacc0bdc7c1734ea5829252230c13aea
checksum: 10/39d62d6711590e817cf9a36257c19ea18e201ceca42b900350e121ea8986c167fbdd9da385ced29c61e38a1b5c76b6c320d59e21d4dd7f32767520e31aef4654
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.58.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/types@npm:8.58.0"
checksum: 10/c68eac0bc25812fdbb2ed4a121e42bfca9f24f3c6be95f6a9c4e7b9af767f1bcfacd6d496e358166143e0a1801dc7d042ce1b5e69946ac2768d9114ff6b8d375
"@typescript-eslint/types@npm:8.58.1, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/types@npm:8.58.1"
checksum: 10/447e1351af8a47297096f063b327c69b1c986af89e39cb39e142bb35d7bec2ce8f34f31edcf62d1beb2e09a38e2029b12b50b335dae4e7c9ff49bd82f9127523
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/typescript-estree@npm:8.58.0"
"@typescript-eslint/typescript-estree@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/typescript-estree@npm:8.58.1"
dependencies:
"@typescript-eslint/project-service": "npm:8.58.0"
"@typescript-eslint/tsconfig-utils": "npm:8.58.0"
"@typescript-eslint/types": "npm:8.58.0"
"@typescript-eslint/visitor-keys": "npm:8.58.0"
"@typescript-eslint/project-service": "npm:8.58.1"
"@typescript-eslint/tsconfig-utils": "npm:8.58.1"
"@typescript-eslint/types": "npm:8.58.1"
"@typescript-eslint/visitor-keys": "npm:8.58.1"
debug: "npm:^4.4.3"
minimatch: "npm:^10.2.2"
semver: "npm:^7.7.3"
@@ -4794,32 +4794,32 @@ __metadata:
ts-api-utils: "npm:^2.5.0"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/4d6c4175e8a4d5c097393d161016836cc322f090c3f69fd751f5bbc25afce64df9ea0c97cee8b36ac060e06dc2cca2a4de7a0c7e04e19727cc4bd98ab3291fed
checksum: 10/107510b484148a8a9a5874f5451b9a6649609607ee5e67de36cded786157987a5262b145398b1bd1935afab66134532369a4d6abb53c6f5b7744e3ace0b13f07
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/utils@npm:8.58.0"
"@typescript-eslint/utils@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/utils@npm:8.58.1"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.9.1"
"@typescript-eslint/scope-manager": "npm:8.58.0"
"@typescript-eslint/types": "npm:8.58.0"
"@typescript-eslint/typescript-estree": "npm:8.58.0"
"@typescript-eslint/scope-manager": "npm:8.58.1"
"@typescript-eslint/types": "npm:8.58.1"
"@typescript-eslint/typescript-estree": "npm:8.58.1"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/936433b761a990147612d78bb4afc79244239541b4a4061fbbc2de1810b40ec7f78eb4e9181e5d9c5ab7acbd9bf49fc6195dbb1d823370f717f07ad492ad6c7e
checksum: 10/c51a5e116d1a09d0eb701c5884d5b9b8c22f79c427cb4c46357e4bcb7dfdfd9beba92e5d518572f42111b7335541a4ccefe3c05595fc3d666c1b62ddd1522e54
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.58.0":
version: 8.58.0
resolution: "@typescript-eslint/visitor-keys@npm:8.58.0"
"@typescript-eslint/visitor-keys@npm:8.58.1":
version: 8.58.1
resolution: "@typescript-eslint/visitor-keys@npm:8.58.1"
dependencies:
"@typescript-eslint/types": "npm:8.58.0"
"@typescript-eslint/types": "npm:8.58.1"
eslint-visitor-keys: "npm:^5.0.0"
checksum: 10/50b0779e19079dedf3723323a4dfa398c639b3da48f2fcf071c22ca69342e03592f1726d68ea59b9b5a51f14ab112eabc5c93fd2579c84b02a3320042ae20066
checksum: 10/e9f34741da6fc0cb8e9eb67828ea4427ac2004a33ce8d1e1e9ba038471f9ed68405eca871651bb2efa793a467bc5233a4310c5571ad1497cb2a84a600e1733a8
languageName: node
linkType: hard
@@ -5066,12 +5066,12 @@ __metadata:
languageName: node
linkType: hard
"@vitest/coverage-v8@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/coverage-v8@npm:4.1.2"
"@vitest/coverage-v8@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/coverage-v8@npm:4.1.4"
dependencies:
"@bcoe/v8-coverage": "npm:^1.0.2"
"@vitest/utils": "npm:4.1.2"
"@vitest/utils": "npm:4.1.4"
ast-v8-to-istanbul: "npm:^1.0.0"
istanbul-lib-coverage: "npm:^3.2.2"
istanbul-lib-report: "npm:^3.0.1"
@@ -5081,34 +5081,34 @@ __metadata:
std-env: "npm:^4.0.0-rc.1"
tinyrainbow: "npm:^3.1.0"
peerDependencies:
"@vitest/browser": 4.1.2
vitest: 4.1.2
"@vitest/browser": 4.1.4
vitest: 4.1.4
peerDependenciesMeta:
"@vitest/browser":
optional: true
checksum: 10/2a38252da937894dfd47a20839714cd49deb8ea0b8289fe25ba17b6677b99dc9b695e4c689b1d6532f19e0d1b81dbac2cf555f82a0ae75abf490dd4107407206
checksum: 10/75c7bfa08d4a410dce09688a7bb1c06b782f90b785a51aea424806621dc90ce21663cf2e8f6f28e3c3c1be708932c5274408cd7cfda51e39dd3e0ae523cca133
languageName: node
linkType: hard
"@vitest/expect@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/expect@npm:4.1.2"
"@vitest/expect@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/expect@npm:4.1.4"
dependencies:
"@standard-schema/spec": "npm:^1.1.0"
"@types/chai": "npm:^5.2.2"
"@vitest/spy": "npm:4.1.2"
"@vitest/utils": "npm:4.1.2"
"@vitest/spy": "npm:4.1.4"
"@vitest/utils": "npm:4.1.4"
chai: "npm:^6.2.2"
tinyrainbow: "npm:^3.1.0"
checksum: 10/536c5a8903927e324bbb66967be4e0ec2ec4ff6234f0b8fe20987841b0705c931c7e3ce2e61c7665f4ded65ba736de6cda8d2d37ee114efeedb187ca5d597ea1
checksum: 10/3317bc42e4ee39cfa2102a9f08f0c7975817a74d9503a14e0b1715e5b8c4ab31c5646c07ef8d2d3f71bdf6f1b3053949b175df9c8457e0c0bb3f38b9e031f259
languageName: node
linkType: hard
"@vitest/mocker@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/mocker@npm:4.1.2"
"@vitest/mocker@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/mocker@npm:4.1.4"
dependencies:
"@vitest/spy": "npm:4.1.2"
"@vitest/spy": "npm:4.1.4"
estree-walker: "npm:^3.0.3"
magic-string: "npm:^0.30.21"
peerDependencies:
@@ -5119,56 +5119,56 @@ __metadata:
optional: true
vite:
optional: true
checksum: 10/1d7976e19ef168357aba2ca41cd8db86236a98dfb2209bd3152a3a20e9a5b8cbfd8f73356c43a934b384d3b4c7a63835fb1037d3f56a7824faa838331eaa214e
checksum: 10/f07f8877635eb03f63981d0d3348bb82fabe7607bbb6b259045bf0b64fae79150b1f399aa7ce42926e4769dc8cde9b7d79d1f665eae2d17b22ecc9ec54663698
languageName: node
linkType: hard
"@vitest/pretty-format@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/pretty-format@npm:4.1.2"
"@vitest/pretty-format@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/pretty-format@npm:4.1.4"
dependencies:
tinyrainbow: "npm:^3.1.0"
checksum: 10/a07a6023c52b25be5c75fc05bb3317629390cc1b50eae6cbea91ba4c13193ec88e54abaa56b46b40ddb8a6a4558d667f2ba0e1cf2ee2d0e32b463244f3002aa7
checksum: 10/e06d63ce4f797ad578ee19aeec996f72835a7274ee2eb75dce12d7b45debcda72d054f58b6f4e5dac4424681dc13dbad7ac023c6017fc60406cabea5a352e4c3
languageName: node
linkType: hard
"@vitest/runner@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/runner@npm:4.1.2"
"@vitest/runner@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/runner@npm:4.1.4"
dependencies:
"@vitest/utils": "npm:4.1.2"
"@vitest/utils": "npm:4.1.4"
pathe: "npm:^2.0.3"
checksum: 10/13fd019a63ee3225420474cbd1ca0ae7c5c2dcdd241f2a958ca45731c10de36131f15303ae8ab1196133ec4e955b7c6de658c7b5e19736d550f310c8195fa9b2
checksum: 10/a852477adc6254e1d304bcba9b137f98f09a7001a557e8e4f4404518e3ade58a16ab459e83cf223e38cc37dc4b04d1248a14df56b056a0ae68fc54b19a1226fb
languageName: node
linkType: hard
"@vitest/snapshot@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/snapshot@npm:4.1.2"
"@vitest/snapshot@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/snapshot@npm:4.1.4"
dependencies:
"@vitest/pretty-format": "npm:4.1.2"
"@vitest/utils": "npm:4.1.2"
"@vitest/pretty-format": "npm:4.1.4"
"@vitest/utils": "npm:4.1.4"
magic-string: "npm:^0.30.21"
pathe: "npm:^2.0.3"
checksum: 10/9d124412dbe44db43ca5277180bf5fe5dad7373218a177830bba631b53d225f7d4de368a20d6f5740ec07402e9e4dd179609db2b2f691d2d8b02f1bdbfd8c1a3
checksum: 10/e957cc95274a9663cd59e5b34c99b6e4e5cd989f04dadf9e3cec6c7bc64b4d167229644f31fd44c19c7acbbcb7cbbbb50e8084dbf1e0322ee411a697d80d490a
languageName: node
linkType: hard
"@vitest/spy@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/spy@npm:4.1.2"
checksum: 10/e20e417ac430fee34e4be58802b2eb31e1c1163296a8921c0878be14e1ae77c7a7cae1b9b515d56fe623e05ee21b092aff7eb5e0d412f656650b72ecd02bb30a
"@vitest/spy@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/spy@npm:4.1.4"
checksum: 10/516e465413fc6a22e0c7e99871f3b9703277c309e94e7247bbdb83a8e807e2da968cf7a30c61503afd6b565787e822786b8aad443210eba5488192a36730f3ab
languageName: node
linkType: hard
"@vitest/utils@npm:4.1.2":
version: 4.1.2
resolution: "@vitest/utils@npm:4.1.2"
"@vitest/utils@npm:4.1.4":
version: 4.1.4
resolution: "@vitest/utils@npm:4.1.4"
dependencies:
"@vitest/pretty-format": "npm:4.1.2"
"@vitest/pretty-format": "npm:4.1.4"
convert-source-map: "npm:^2.0.0"
tinyrainbow: "npm:^3.1.0"
checksum: 10/854decf0eb639758d012c9aa53c3d7aed547e37c05ece6704d5f53035be77f704a24973ed95089926e1768c0b55902d42c4438660788e7a0f0e80d0fda1c713b
checksum: 10/f599ae744f0ff45edda90d0c52eea9809b7367adca39fc985f85880322236d089dfdf6625f04913f03a25a160eccbbc0b16dd3201ccc0ae48087992b1ea755d5
languageName: node
linkType: hard
@@ -6990,10 +6990,10 @@ __metadata:
languageName: node
linkType: hard
"diff@npm:^8.0.3":
version: 8.0.3
resolution: "diff@npm:8.0.3"
checksum: 10/52f957e1fa53db4616ff5f4811b92b22b97a160c12a2f86f22debd4181227b0f6751aa8fd711d6a8fcf4618acb13b86bc702e6d9d6d6ed82acfd00c9cb26ace2
"diff@npm:^8.0.4":
version: 8.0.4
resolution: "diff@npm:8.0.4"
checksum: 10/b4036ceda0d1e10683a2313079ed52c5e6b09553ae29da87bce81d98714d9725dbf3c0f6f7c3b1f16eec049fe17087e38ee329e732580fa62f6ec1c2487b2435
languageName: node
linkType: hard
@@ -8936,7 +8936,7 @@ __metadata:
"@types/tar": "npm:7.0.87"
"@types/webspeechapi": "npm:0.0.29"
"@vibrant/color": "npm:4.0.4"
"@vitest/coverage-v8": "npm:4.1.2"
"@vitest/coverage-v8": "npm:4.1.4"
"@webcomponents/scoped-custom-element-registry": "npm:0.0.10"
"@webcomponents/webcomponentsjs": "npm:2.8.0"
babel-loader: "npm:10.1.1"
@@ -9002,7 +9002,7 @@ __metadata:
node-vibrant: "npm:4.0.4"
object-hash: "npm:3.0.0"
pinst: "npm:3.0.0"
prettier: "npm:3.8.1"
prettier: "npm:3.8.2"
punycode: "npm:2.3.1"
qr-scanner: "npm:1.4.2"
qrcode: "npm:1.5.4"
@@ -9010,7 +9010,7 @@ __metadata:
rrule: "npm:2.8.1"
rspack-manifest-plugin: "npm:5.2.1"
serve: "npm:14.2.6"
sinon: "npm:21.0.3"
sinon: "npm:21.1.0"
sortablejs: "patch:sortablejs@npm%3A1.15.6#~/.yarn/patches/sortablejs-npm-1.15.6-3235a8f83b.patch"
stacktrace-js: "npm:2.0.2"
superstruct: "npm:2.0.2"
@@ -9019,9 +9019,9 @@ __metadata:
tinykeys: "npm:3.0.0"
ts-lit-plugin: "npm:2.0.2"
typescript: "npm:6.0.2"
typescript-eslint: "npm:8.58.0"
typescript-eslint: "npm:8.58.1"
vite-tsconfig-paths: "npm:6.1.1"
vitest: "npm:4.1.2"
vitest: "npm:4.1.4"
webpack-stats-plugin: "npm:1.1.3"
webpackbar: "npm:7.0.0"
weekstart: "npm:2.0.0"
@@ -11794,12 +11794,12 @@ __metadata:
languageName: node
linkType: hard
"prettier@npm:3.8.1":
version: 3.8.1
resolution: "prettier@npm:3.8.1"
"prettier@npm:3.8.2":
version: 3.8.2
resolution: "prettier@npm:3.8.2"
bin:
prettier: bin/prettier.cjs
checksum: 10/3da1cf8c1ef9bea828aa618553696c312e951f810bee368f6887109b203f18ee869fe88f66e65f9cf60b7cb1f2eae859892c860a300c062ff8ec69c381fc8dbd
checksum: 10/fd784175bc600c07eb2c44d7ec4ee7133f95f26492adad61b6a15c06f438b858181faf096ab74163d7f49500ad80cff4479c6abb084e161a2e85a9df5974ecd1
languageName: node
linkType: hard
@@ -12834,16 +12834,16 @@ __metadata:
languageName: node
linkType: hard
"sinon@npm:21.0.3":
version: 21.0.3
resolution: "sinon@npm:21.0.3"
"sinon@npm:21.1.0":
version: 21.1.0
resolution: "sinon@npm:21.1.0"
dependencies:
"@sinonjs/commons": "npm:^3.0.1"
"@sinonjs/fake-timers": "npm:^15.1.1"
"@sinonjs/samsam": "npm:^9.0.3"
diff: "npm:^8.0.3"
supports-color: "npm:^7.2.0"
checksum: 10/af1018ce4ae854ef30435fd6dd130c1fbb067561fba6fbc3930fd5da807267851f2a7e7abb2902b0882b878f50d281f2a88f88f559a49d7c4190712a2da536c5
"@sinonjs/fake-timers": "npm:^15.3.0"
"@sinonjs/samsam": "npm:^10.0.0"
diff: "npm:^8.0.4"
supports-color: "npm:^10.2.2"
checksum: 10/cb9b174fbb0f5efb8004d93fc0e0a6152118cbbfe66ef45446ec40924483fa899e5b2196b3eed0da140549cc630ab29955d4935605fe1a79f150b8aa2c30723b
languageName: node
linkType: hard
@@ -13396,6 +13396,13 @@ __metadata:
languageName: node
linkType: hard
"supports-color@npm:^10.2.2":
version: 10.2.2
resolution: "supports-color@npm:10.2.2"
checksum: 10/bd132705fc07213a4024131fb061f0a46a48b49ecaf434bb029c0a5aa0339b969236d496d63969e3c248a90fdcac16d4f9c5bf187e7be0bfb5e804ed13bef6b0
languageName: node
linkType: hard
"supports-color@npm:^5.3.0":
version: 5.5.0
resolution: "supports-color@npm:5.5.0"
@@ -13405,7 +13412,7 @@ __metadata:
languageName: node
linkType: hard
"supports-color@npm:^7.1.0, supports-color@npm:^7.2.0":
"supports-color@npm:^7.1.0":
version: 7.2.0
resolution: "supports-color@npm:7.2.0"
dependencies:
@@ -13936,18 +13943,18 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.58.0":
version: 8.58.0
resolution: "typescript-eslint@npm:8.58.0"
"typescript-eslint@npm:8.58.1":
version: 8.58.1
resolution: "typescript-eslint@npm:8.58.1"
dependencies:
"@typescript-eslint/eslint-plugin": "npm:8.58.0"
"@typescript-eslint/parser": "npm:8.58.0"
"@typescript-eslint/typescript-estree": "npm:8.58.0"
"@typescript-eslint/utils": "npm:8.58.0"
"@typescript-eslint/eslint-plugin": "npm:8.58.1"
"@typescript-eslint/parser": "npm:8.58.1"
"@typescript-eslint/typescript-estree": "npm:8.58.1"
"@typescript-eslint/utils": "npm:8.58.1"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/e30c9a094c9cf49265db626c0570a1af21b117fe9ce695cd7f0e7962bd8ac1a4e5b5fb1a1e0cb5413f237cd32804be56b11fb72ee2cfede775768c28617b1a0b
checksum: 10/b65fba0a594870fe5c456e6650a9050adf83b72c3d57983e3924778e217399e057c4c185abbe21396f288113d85c475377ee0d7ed6ac24538ef4ea0a7332c0df
languageName: node
linkType: hard
@@ -14445,17 +14452,17 @@ __metadata:
languageName: node
linkType: hard
"vitest@npm:4.1.2":
version: 4.1.2
resolution: "vitest@npm:4.1.2"
"vitest@npm:4.1.4":
version: 4.1.4
resolution: "vitest@npm:4.1.4"
dependencies:
"@vitest/expect": "npm:4.1.2"
"@vitest/mocker": "npm:4.1.2"
"@vitest/pretty-format": "npm:4.1.2"
"@vitest/runner": "npm:4.1.2"
"@vitest/snapshot": "npm:4.1.2"
"@vitest/spy": "npm:4.1.2"
"@vitest/utils": "npm:4.1.2"
"@vitest/expect": "npm:4.1.4"
"@vitest/mocker": "npm:4.1.4"
"@vitest/pretty-format": "npm:4.1.4"
"@vitest/runner": "npm:4.1.4"
"@vitest/snapshot": "npm:4.1.4"
"@vitest/spy": "npm:4.1.4"
"@vitest/utils": "npm:4.1.4"
es-module-lexer: "npm:^2.0.0"
expect-type: "npm:^1.3.0"
magic-string: "npm:^0.30.21"
@@ -14473,10 +14480,12 @@ __metadata:
"@edge-runtime/vm": "*"
"@opentelemetry/api": ^1.9.0
"@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0
"@vitest/browser-playwright": 4.1.2
"@vitest/browser-preview": 4.1.2
"@vitest/browser-webdriverio": 4.1.2
"@vitest/ui": 4.1.2
"@vitest/browser-playwright": 4.1.4
"@vitest/browser-preview": 4.1.4
"@vitest/browser-webdriverio": 4.1.4
"@vitest/coverage-istanbul": 4.1.4
"@vitest/coverage-v8": 4.1.4
"@vitest/ui": 4.1.4
happy-dom: "*"
jsdom: "*"
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -14493,6 +14502,10 @@ __metadata:
optional: true
"@vitest/browser-webdriverio":
optional: true
"@vitest/coverage-istanbul":
optional: true
"@vitest/coverage-v8":
optional: true
"@vitest/ui":
optional: true
happy-dom:
@@ -14503,7 +14516,7 @@ __metadata:
optional: false
bin:
vitest: vitest.mjs
checksum: 10/6b037387e59d403f6570f887f6ac96b81ff6e768dbd02d32a812ddff5bdebef022dd6d9f20b84fb9535866e0c5dbdf80e6705cc428b6a8f8a8e67e1335235848
checksum: 10/c5608c506ae9ab3d0baa7445290c240941ad54a93eb853a005b2fe518efb1b28282945e0565ca16a624cca5b23af0c33ee34fbc2c38e6664ea54b08b9a22a653
languageName: node
linkType: hard