- ${this.hass.formatEntityAttributeName(
- this.stateObj,
- "latest_version"
- )}
+
+ ${this.stateObj.attributes.in_progress
+ ? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) &&
+ this.stateObj.attributes.update_percentage !== null
+ ? html`
`
+ : html`
`
+ : nothing}
+
${this.stateObj.attributes.title}
+ ${this._error
+ ? html`
${this._error}`
+ : nothing}
+
+
+ ${this.hass.formatEntityAttributeName(
+ this.stateObj,
+ "installed_version"
+ )}
+
+
+ ${this.stateObj.attributes.installed_version ??
+ this.hass.localize("state.default.unavailable")}
+
-
- ${this.stateObj.attributes.latest_version ??
- this.hass.localize("state.default.unavailable")}
+
+
+ ${this.hass.formatEntityAttributeName(
+ this.stateObj,
+ "latest_version"
+ )}
+
+
+ ${this.stateObj.attributes.latest_version ??
+ this.hass.localize("state.default.unavailable")}
+
-
- ${this.stateObj.attributes.release_url
- ? html`
`
- : ""}
- ${supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES) &&
- !this._error
- ? this._releaseNotes === undefined
- ? html`
-
+ ${this.stateObj.attributes.release_url
+ ? html`
`
- : html`
-
-
- `
- : this.stateObj.attributes.release_summary
- ? html`
-
`
- : ""}
- ${supportsFeature(this.stateObj, UpdateEntityFeature.BACKUP)
- ? html`
-
-
- `
- : ""}
-
- ${this.stateObj.state === BINARY_STATE_OFF &&
- this.stateObj.attributes.skipped_version
- ? html`
-
- ${this.hass.localize(
- "ui.dialogs.more_info_control.update.clear_skipped"
- )}
-
- `
- : html`
-
- ${this.hass.localize(
- "ui.dialogs.more_info_control.update.skip"
- )}
-
- `}
- ${supportsFeature(this.stateObj, UpdateEntityFeature.INSTALL)
+ : nothing}
+ ${supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES) &&
+ !this._error
+ ? this._releaseNotes === undefined
+ ? html`
+
+ ${this._markdownLoading ? this._renderLoader() : nothing}
+ `
+ : html`
+
+
+ ${this._markdownLoading ? this._renderLoader() : nothing}
+ `
+ : this.stateObj.attributes.release_summary
+ ? html`
+
+
+ ${this._markdownLoading ? this._renderLoader() : nothing}
+ `
+ : nothing}
+
+
+ `;
+ }
+
+ private _renderLoader() {
+ return html`
+
+
`;
}
protected firstUpdated(): void {
if (supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES)) {
- updateReleaseNotes(this.hass, this.stateObj!.entity_id)
- .then((result) => {
- this._releaseNotes = result;
- })
- .catch((err) => {
- this._error = err.message;
- });
+ this._fetchReleaseNotes();
+ }
+ }
+
+ private async _markdownLoaded() {
+ if (this._markdownLoading) {
+ this._markdownLoading = false;
+ }
+ }
+
+ private async _fetchReleaseNotes() {
+ try {
+ this._releaseNotes = await updateReleaseNotes(
+ this.hass,
+ this.stateObj!.entity_id
+ );
+ } catch (err: any) {
+ this._error = err.message;
}
}
@@ -183,9 +225,11 @@ class MoreInfoUpdate extends LitElement {
if (!supportsFeature(this.stateObj!, UpdateEntityFeature.BACKUP)) {
return null;
}
- const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
- if (checkbox) {
- return checkbox.checked;
+ const createBackupSwitch = this.shadowRoot?.getElementById(
+ "create-backup"
+ ) as HaSwitch;
+ if (createBackupSwitch) {
+ return createBackupSwitch.checked;
}
return true;
}
@@ -234,6 +278,12 @@ class MoreInfoUpdate extends LitElement {
static get styles(): CSSResultGroup {
return css`
+ :host {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ justify-content: space-between;
+ }
hr {
border-color: var(--divider-color);
border-bottom: none;
@@ -248,26 +298,44 @@ class MoreInfoUpdate extends LitElement {
flex-direction: row;
justify-content: space-between;
}
- .actions {
+
+ .footer {
border-top: 1px solid var(--divider-color);
background: var(
--ha-dialog-surface-background,
var(--mdc-theme-surface, #fff)
);
- margin: 8px 0 0;
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
position: sticky;
bottom: 0;
- padding: 12px 0;
- margin-bottom: -24px;
- z-index: 1;
+ margin: 0 -24px -24px -24px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow: hidden;
+ z-index: 10;
}
- .actions mwc-button {
- margin: 0 4px 4px;
+ ha-settings-row {
+ width: 100%;
+ padding: 0 24px;
+ box-sizing: border-box;
+ margin-bottom: -16px;
+ margin-top: -4px;
}
+
+ .actions {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ box-sizing: border-box;
+ padding: 12px;
+ z-index: 1;
+ gap: 8px;
+ }
+
a {
color: var(--primary-color);
}
@@ -282,6 +350,16 @@ class MoreInfoUpdate extends LitElement {
}
ha-markdown {
direction: ltr;
+ padding-bottom: 16px;
+ box-sizing: border-box;
+ }
+ ha-markdown.hidden {
+ display: none;
+ }
+ .loader {
+ height: 80px;
+ box-sizing: border-box;
+ padding-bottom: 16px;
}
`;
}
diff --git a/src/dialogs/more-info/controls/more-info-valve.ts b/src/dialogs/more-info/controls/more-info-valve.ts
index f7bae2a9bef5..63e23d737a50 100644
--- a/src/dialogs/more-info/controls/more-info-valve.ts
+++ b/src/dialogs/more-info/controls/more-info-valve.ts
@@ -83,10 +83,11 @@ class MoreInfoValve extends LitElement {
supportsFeature(this.stateObj, ValveEntityFeature.CLOSE) ||
supportsFeature(this.stateObj, ValveEntityFeature.STOP);
- const supportsOpenCloseWithoutStop =
+ const supportsOpenCloseOnly =
supportsFeature(this.stateObj, ValveEntityFeature.OPEN) &&
supportsFeature(this.stateObj, ValveEntityFeature.CLOSE) &&
- !supportsFeature(this.stateObj, ValveEntityFeature.STOP);
+ !supportsFeature(this.stateObj, ValveEntityFeature.STOP) &&
+ !supportsPosition;
return html`
@@ -89,7 +92,7 @@ export class MoreInfoInfo extends LitElement {
.entityId=${this.entityId}
>`}
({
+ mode_above: inputAboveIsEntity ? "input" : "value",
+ mode_below: inputBelowIsEntity ? "input" : "value",
+ ...condition,
+ })
+ );
+
private _schema = memoizeOne(
(
localize: LocalizeFunc,
@@ -233,31 +245,33 @@ export default class HaNumericStateCondition extends LitElement {
] as const
);
- public render() {
- const inputAboveIsEntity =
+ public willUpdate() {
+ this._inputAboveIsEntity =
this._inputAboveIsEntity ??
(typeof this.condition.above === "string" &&
((this.condition.above as string).startsWith("input_number.") ||
(this.condition.above as string).startsWith("number.") ||
(this.condition.above as string).startsWith("sensor.")));
- const inputBelowIsEntity =
+ this._inputBelowIsEntity =
this._inputBelowIsEntity ??
(typeof this.condition.below === "string" &&
((this.condition.below as string).startsWith("input_number.") ||
(this.condition.below as string).startsWith("number.") ||
(this.condition.below as string).startsWith("sensor.")));
+ }
+ public render() {
const schema = this._schema(
this.hass.localize,
- inputAboveIsEntity,
- inputBelowIsEntity
+ this._inputAboveIsEntity,
+ this._inputBelowIsEntity
);
- const data = {
- mode_above: inputAboveIsEntity ? "input" : "value",
- mode_below: inputBelowIsEntity ? "input" : "value",
- ...this.condition,
- };
+ const data = this._data(
+ this._inputAboveIsEntity!,
+ this._inputBelowIsEntity!,
+ this.condition
+ );
return html`
({
+ mode_above: inputAboveIsEntity ? "input" : "value",
+ mode_below: inputBelowIsEntity ? "input" : "value",
+ ...trigger,
+ entity_id: ensureArray(trigger.entity_id),
+ for: createDurationData(trigger.for),
+ })
+ );
+ public render() {
const schema = this._schema(
this.hass.localize,
this.trigger.entity_id,
- inputAboveIsEntity,
- inputBelowIsEntity
+ this._inputAboveIsEntity,
+ this._inputBelowIsEntity
);
- const data = {
- mode_above: inputAboveIsEntity ? "input" : "value",
- mode_below: inputBelowIsEntity ? "input" : "value",
- ...this.trigger,
- entity_id: ensureArray(this.trigger.entity_id),
- for: trgFor,
- };
+ const data = this._data(
+ this._inputAboveIsEntity!,
+ this._inputBelowIsEntity!,
+ this.trigger
+ );
return html`
- ${this.hass.localize(
- "ui.panel.config.cloud.account.remote.copy_link"
- )}
+ ${this.hass.localize("ui.panel.config.common.copy_link")}
diff --git a/src/panels/config/devices/device-detail/ha-device-entities-card.ts b/src/panels/config/devices/device-detail/ha-device-entities-card.ts
index 71a8b0acdf12..07993cbdb723 100644
--- a/src/panels/config/devices/device-detail/ha-device-entities-card.ts
+++ b/src/panels/config/devices/device-detail/ha-device-entities-card.ts
@@ -251,9 +251,7 @@ export class HaDeviceEntitiesCard extends LitElement {
display: block;
}
ha-icon {
- margin-left: 8px;
- margin-inline-start: 8px;
- margin-inline-end: initial;
+ margin-left: -8px;
}
.entity-id {
color: var(--secondary-text-color);
@@ -283,6 +281,9 @@ export class HaDeviceEntitiesCard extends LitElement {
.name {
font-size: 14px;
}
+ .name:dir(rtl) {
+ margin-inline-start: 8px;
+ }
.empty {
text-align: center;
}
@@ -302,6 +303,9 @@ export class HaDeviceEntitiesCard extends LitElement {
outline: none;
text-decoration: underline;
}
+ ha-list-item {
+ height: 40px;
+ }
`;
}
}
diff --git a/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts b/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts
index 59b425b47d7a..87b027166afe 100644
--- a/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts
+++ b/src/panels/config/entities/editor-tabs/settings/entity-settings-helper-tab.ts
@@ -100,8 +100,8 @@ export class EntitySettingsHelperTab extends LitElement {
.entry=${this.entry}
.disabled=${this._submitting}
@change=${this._entityRegistryChanged}
- hideName
- hideIcon
+ hide-name
+ hide-icon
>
diff --git a/src/panels/config/entities/entity-registry-settings-editor.ts b/src/panels/config/entities/entity-registry-settings-editor.ts
index da34ca591b62..158d281f7778 100644
--- a/src/panels/config/entities/entity-registry-settings-editor.ts
+++ b/src/panels/config/entities/entity-registry-settings-editor.ts
@@ -44,6 +44,7 @@ import {
CAMERA_ORIENTATIONS,
CAMERA_SUPPORT_STREAM,
CameraPreferences,
+ fetchCameraCapabilities,
fetchCameraPrefs,
STREAM_TYPE_HLS,
updateCameraPrefs,
@@ -145,9 +146,9 @@ export class EntityRegistrySettingsEditor extends LitElement {
@property({ type: Object }) public entry!: ExtEntityRegistryEntry;
- @property({ type: Boolean }) public hideName = false;
+ @property({ type: Boolean, attribute: "hide-name" }) public hideName = false;
- @property({ type: Boolean }) public hideIcon = false;
+ @property({ type: Boolean, attribute: "hide-icon" }) public hideIcon = false;
@property({ type: Boolean }) public disabled = false;
@@ -236,13 +237,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
if (domain === "camera" && isComponentLoaded(this.hass, "stream")) {
const stateObj: HassEntity | undefined =
this.hass.states[this.entry.entity_id];
- if (
- stateObj &&
- supportsFeature(stateObj, CAMERA_SUPPORT_STREAM) &&
- // The stream component for HLS streams supports a server-side pre-load
- // option that client initiated WebRTC streams do not
- stateObj.attributes.frontend_stream_type === STREAM_TYPE_HLS
- ) {
+ if (stateObj && supportsFeature(stateObj, CAMERA_SUPPORT_STREAM)) {
this._fetchCameraPrefs();
}
}
@@ -1396,6 +1391,19 @@ export class EntityRegistrySettingsEditor extends LitElement {
}
private async _fetchCameraPrefs() {
+ const capabilities = await fetchCameraCapabilities(
+ this.hass,
+ this.entry.entity_id
+ );
+
+ // The stream component for HLS streams supports a server-side pre-load
+ // option that client initiated WebRTC streams do not
+
+ if (!capabilities.frontend_stream_types.includes(STREAM_TYPE_HLS)) {
+ this._cameraPrefs = undefined;
+ return;
+ }
+
this._cameraPrefs = await fetchCameraPrefs(this.hass, this.entry.entity_id);
}
diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts
index e828e7b6e101..1859f5578d75 100644
--- a/src/panels/config/entities/ha-config-entities.ts
+++ b/src/panels/config/entities/ha-config-entities.ts
@@ -1069,8 +1069,7 @@ ${
}
if (
changedProps.has("_entitySources") ||
- (changedProps.has("hass") && !oldHass) ||
- !oldHass.states[entityId]
+ (changedProps.has("hass") && (!oldHass || !oldHass.states[entityId]))
) {
changed = true;
}
diff --git a/src/panels/config/labels/ha-config-labels.ts b/src/panels/config/labels/ha-config-labels.ts
index db2f60663106..7d0e60e22d42 100644
--- a/src/panels/config/labels/ha-config-labels.ts
+++ b/src/panels/config/labels/ha-config-labels.ts
@@ -103,6 +103,7 @@ export class HaConfigLabels extends LitElement {
style="
background-color: ${computeCssColor(label.color)};
border-radius: 10px;
+ outline: 1px solid var(--outline-color);
width: 20px;
height: 20px;"
>
`
diff --git a/src/panels/config/logs/dialog-download-logs.ts b/src/panels/config/logs/dialog-download-logs.ts
index cd711fa5a2b4..fdb5e7242ea4 100644
--- a/src/panels/config/logs/dialog-download-logs.ts
+++ b/src/panels/config/logs/dialog-download-logs.ts
@@ -65,7 +65,11 @@ class DownloadLogsDialog extends LitElement {
@@ -104,9 +108,14 @@ class DownloadLogsDialog extends LitElement {
private async _dowloadLogs() {
const provider = this._dialogParams!.provider;
+ const boot = this._dialogParams!.boot;
const timeString = new Date().toISOString().replace(/:/g, "-");
- const downloadUrl = getHassioLogDownloadLinesUrl(provider, this._lineCount);
+ const downloadUrl = getHassioLogDownloadLinesUrl(
+ provider,
+ this._lineCount,
+ boot
+ );
const logFileName =
provider !== "core"
? `${provider}_${timeString}.log`
diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts
index 41bd9eea19f6..ae17a7a16466 100644
--- a/src/panels/config/logs/error-log-card.ts
+++ b/src/panels/config/logs/error-log-card.ts
@@ -1,5 +1,10 @@
import "@material/mwc-list/mwc-list-item";
-import { mdiArrowCollapseDown, mdiDownload, mdiRefresh } from "@mdi/js";
+import {
+ mdiArrowCollapseDown,
+ mdiDownload,
+ mdiMenuDown,
+ mdiRefresh,
+} from "@mdi/js";
import {
css,
CSSResultGroup,
@@ -22,12 +27,17 @@ import "../../../components/ha-button";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-circular-progress";
+import "../../../components/chips/ha-assist-chip";
+import "../../../components/ha-menu";
+import "../../../components/ha-md-menu-item";
+import "../../../components/ha-md-divider";
import { getSignedPath } from "../../../data/auth";
import { fetchErrorLog, getErrorLogDownloadUrl } from "../../../data/error_log";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import {
+ fetchHassioBoots,
fetchHassioLogs,
fetchHassioLogsFollow,
getHassioLogDownloadUrl,
@@ -40,6 +50,7 @@ import { atLeastVersion } from "../../../common/config/version";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { debounce } from "../../../common/util/debounce";
import { showDownloadLogsDialog } from "./show-dialog-download-logs";
+import type { HaMenu } from "../../../components/ha-menu";
const NUMBER_OF_LINES = 100;
@@ -64,6 +75,8 @@ class ErrorLogCard extends LitElement {
@query("ha-ansi-to-html") private _ansiToHtmlElement?: HaAnsiToHtml;
+ @query("#boots-menu") private _bootsMenu?: HaMenu;
+
@state() private _firstCursor?: string;
@state() private _scrolledToBottomController =
@@ -92,6 +105,10 @@ class ErrorLogCard extends LitElement {
@state() private _numberOfLines?: number;
+ @state() private _boot = 0;
+
+ @state() private _boots?: number[];
+
protected render(): TemplateResult {
return html`
@@ -105,6 +122,60 @@ class ErrorLogCard extends LitElement {
this.hass.localize("ui.panel.config.logs.show_full_logs")}
${hasCloud || !isComponentLoaded(this.hass, "cloud")
? ""
: html`
@@ -180,40 +211,65 @@ class ConfigUrlForm extends LitElement {
`
: ""}
-
-
- ${this.hass.localize("ui.panel.config.url.internal_url_label")}
-
-
-
+ ${this.hass.localize("ui.panel.config.url.internal_url_label")}
+
+
+
+ ${this.hass.localize(
"ui.panel.config.url.internal_url_automatic"
)}
- >
-
-
+
+
+ ${this.hass.localize(
+ "ui.panel.config.url.internal_url_automatic_description"
+ )}
+
+
+
+
+
+
+
`
+ }
+ >
+ ${!this._showCustomInternalUrl || !canEdit
+ ? html`
+
+ `
+ : nothing}
+
+
+
+ ${this.hass.localize("ui.panel.config.common.copy_link")}
+
-
- ${!this._showCustomInternalUrl
- ? ""
- : html`
-
- `}
${
// If the user has configured a cert, show an error if
httpUseHttps && // there is no internal url configured
@@ -253,46 +309,47 @@ class ConfigUrlForm extends LitElement {
protected override firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
- this._showCustomInternalUrl = this._internalUrlValue !== null;
-
if (isComponentLoaded(this.hass, "cloud")) {
fetchCloudStatus(this.hass).then((cloudStatus) => {
this._cloudStatus = cloudStatus;
- if (cloudStatus.logged_in) {
- this._showCustomExternalUrl = this._externalUrlValue !== null;
- } else {
- this._showCustomExternalUrl = true;
- }
+ this._showCustomExternalUrl = !(
+ this._cloudStatus.logged_in && !this.hass.config.external_url
+ );
});
} else {
this._cloudStatus = null;
- this._showCustomExternalUrl = true;
}
+ this._fetchUrls();
}
- private get _internalUrlValue() {
- return this._internal_url !== undefined
- ? this._internal_url
- : this.hass.config.internal_url;
+ private _toggleCloud(ev: Event) {
+ this._cloudChecked = (ev.currentTarget as HaSwitch).checked;
+ this._showCustomExternalUrl = !this._cloudChecked;
}
- private get _externalUrlValue() {
- return this._external_url !== undefined
- ? this._external_url
- : this.hass.config.external_url;
+ private _toggleInternalAutomatic(ev: Event) {
+ this._showCustomInternalUrl = !(ev.currentTarget as HaSwitch).checked;
}
- private _toggleCloud(ev) {
- this._showCustomExternalUrl = !ev.currentTarget.checked;
+ private _toggleUnmaskedInternalUrl() {
+ this._unmaskedInternalUrl = !this._unmaskedInternalUrl;
}
- private _toggleInternalAutomatic(ev) {
- this._showCustomInternalUrl = !ev.currentTarget.checked;
+ private _toggleUnmaskedExternalUrl() {
+ this._unmaskedExternalUrl = !this._unmaskedExternalUrl;
+ }
+
+ private async _copyURL(ev) {
+ const url = ev.currentTarget.url;
+ await copyToClipboard(url);
+ showToast(this, {
+ message: this.hass.localize("ui.common.copied_clipboard"),
+ });
}
private _handleChange(ev: ValueChangedEvent
) {
const target = ev.currentTarget as HaTextField;
- this[`_${target.name}`] = target.value || null;
+ this[`_${target.name}`] = target.value || "";
}
private async _save() {
@@ -307,6 +364,7 @@ class ConfigUrlForm extends LitElement {
? this._internal_url || null
: null,
});
+ await this._fetchUrls();
} catch (err: any) {
this._error = err.message || err;
} finally {
@@ -314,6 +372,19 @@ class ConfigUrlForm extends LitElement {
}
}
+ private async _fetchUrls() {
+ this._urls = await getNetworkUrls(this.hass);
+ this._cloudChecked =
+ this._urls?.cloud === this._urls?.external &&
+ !this.hass.config.external_url;
+ this._showCustomInternalUrl = !!this.hass.config.internal_url;
+ this._showCustomExternalUrl = !(
+ this._cloudStatus?.logged_in && !this.hass.config.external_url
+ );
+ this._internal_url = this._urls?.internal ?? "";
+ this._external_url = this._urls?.external ?? "";
+ }
+
static get styles(): CSSResultGroup {
return css`
.description {
@@ -351,6 +422,31 @@ class ConfigUrlForm extends LitElement {
color: var(--primary-color);
text-decoration: none;
}
+
+ .url-container {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-top: 8px;
+ }
+ .textfield-container {
+ position: relative;
+ flex: 1;
+ }
+ .textfield-container ha-textfield {
+ display: block;
+ }
+ .toggle-unmasked-url {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ inset-inline-start: initial;
+ inset-inline-end: 8px;
+ --mdc-icon-button-size: 40px;
+ --mdc-icon-size: 20px;
+ color: var(--secondary-text-color);
+ direction: var(--direction);
+ }
`;
}
}
diff --git a/src/panels/config/person/ha-config-person.ts b/src/panels/config/person/ha-config-person.ts
index 27e065e54a72..c4c658b02340 100644
--- a/src/panels/config/person/ha-config-person.ts
+++ b/src/panels/config/person/ha-config-person.ts
@@ -292,6 +292,9 @@ export class HaConfigPerson extends LitElement {
align-items: center;
justify-content: space-around;
}
+ mwc-list:has(+ .empty) {
+ display: none;
+ }
`;
}
}
diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts
index 0f0880058164..989957098f40 100644
--- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts
+++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts
@@ -11,7 +11,7 @@ import {
mdiUnfoldMoreHorizontal,
} from "@mdi/js";
-import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
+import { HassEntity } from "home-assistant-js-websocket";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
@@ -33,7 +33,6 @@ import "../../../components/ha-dialog";
import { HaMenu } from "../../../components/ha-menu";
import "../../../components/ha-md-menu-item";
import "../../../components/search-input-outlined";
-import { subscribeEntityRegistry } from "../../../data/entity_registry";
import {
StatisticsMetaData,
StatisticsValidationResult,
@@ -42,7 +41,6 @@ import {
updateStatisticsIssues,
validateStatistics,
} from "../../../data/recorder";
-import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { showConfirmationDialog } from "../../lovelace/custom-card-helpers";
@@ -76,7 +74,7 @@ type DisplayedStatisticData = StatisticData & {
};
@customElement("developer-tools-statistics")
-class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
+class HaPanelDevStatistics extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow = false;
@@ -107,8 +105,6 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
@query("#sort-by-menu") private _sortByMenu!: HaMenu;
- private _disabledEntities = new Set();
-
private _toggleGroupBy() {
this._groupByMenu.open = !this._groupByMenu.open;
}
@@ -612,25 +608,6 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
}
}
- public hassSubscribe(): UnsubscribeFunc[] {
- return [
- subscribeEntityRegistry(this.hass.connection!, (entities) => {
- const disabledEntities = new Set();
- for (const confEnt of entities) {
- if (!confEnt.disabled_by) {
- continue;
- }
- disabledEntities.add(confEnt.entity_id);
- }
- // If the disabled entities changed, re-validate the statistics
- if (disabledEntities !== this._disabledEntities) {
- this._disabledEntities = disabledEntities;
- this._validateStatistics();
- }
- }),
- ];
- }
-
private async _validateStatistics() {
const [statisticIds, issues] = await Promise.all([
getStatisticIds(this.hass),
@@ -641,24 +618,17 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
const statsIds = new Set();
- this._data = statisticIds
- .filter(
- (statistic) => !this._disabledEntities.has(statistic.statistic_id)
- )
- .map((statistic) => {
- statsIds.add(statistic.statistic_id);
- return {
- ...statistic,
- state: this.hass.states[statistic.statistic_id],
- issues: issues[statistic.statistic_id],
- };
- });
+ this._data = statisticIds.map((statistic) => {
+ statsIds.add(statistic.statistic_id);
+ return {
+ ...statistic,
+ state: this.hass.states[statistic.statistic_id],
+ issues: issues[statistic.statistic_id],
+ };
+ });
Object.keys(issues).forEach((statisticId) => {
- if (
- !statsIds.has(statisticId) &&
- !this._disabledEntities.has(statisticId)
- ) {
+ if (!statsIds.has(statisticId)) {
this._data.push({
statistic_id: statisticId,
statistics_unit_of_measurement: "",
diff --git a/src/panels/energy/ha-panel-energy.ts b/src/panels/energy/ha-panel-energy.ts
index c16b3842c113..200e3e576ed7 100644
--- a/src/panels/energy/ha-panel-energy.ts
+++ b/src/panels/energy/ha-panel-energy.ts
@@ -18,6 +18,7 @@ import { HomeAssistant } from "../../types";
import "../lovelace/components/hui-energy-period-selector";
import { Lovelace } from "../lovelace/types";
import "../lovelace/views/hui-view";
+import "../lovelace/views/hui-view-container";
import { navigate } from "../../common/navigate";
import {
getEnergyDataCollection,
@@ -108,14 +109,18 @@ class PanelEnergy extends LitElement {
-
+
+
-
+
`;
}
@@ -389,23 +394,19 @@ class PanelEnergy extends LitElement {
line-height: 20px;
flex-grow: 1;
}
- #view {
+ hui-view-container {
position: relative;
display: flex;
- padding-top: calc(var(--header-height) + env(safe-area-inset-top));
min-height: 100vh;
box-sizing: border-box;
+ padding-top: calc(var(--header-height) + env(safe-area-inset-top));
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-inline-start: env(safe-area-inset-left);
padding-inline-end: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
- background: var(
- --lovelace-background,
- var(--primary-background-color)
- );
}
- #view > * {
+ hui-view {
flex: 1 1 100%;
max-width: 100%;
}
diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts
index ae3bd5279881..c50cb0c38eb8 100644
--- a/src/panels/lovelace/cards/hui-map-card.ts
+++ b/src/panels/lovelace/cards/hui-map-card.ts
@@ -50,6 +50,11 @@ interface MapEntityConfig extends EntityConfig {
focus?: boolean;
}
+interface GeoEntity {
+ entity_id: string;
+ focus: boolean;
+}
+
@customElement("hui-map-card")
class HuiMapCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -332,23 +337,32 @@ class HuiMapCard extends LitElement implements LovelaceCard {
return color;
}
- private _getSourceEntities(states?: HassEntities): string[] {
+ private _getSourceEntities(states?: HassEntities): GeoEntity[] {
if (!states || !this._config?.geo_location_sources) {
return [];
}
- const geoEntities: string[] = [];
+ const sourceObjs = this._config.geo_location_sources.map((source) =>
+ typeof source === "string" ? { source } : source
+ );
+
+ const geoEntities: GeoEntity[] = [];
// Calculate visible geo location sources
- const includesAll = this._config.geo_location_sources.includes("all");
+ const allSource = sourceObjs.find((s) => s.source === "all");
for (const stateObj of Object.values(states)) {
+ const sourceObj = sourceObjs.find(
+ (s) => s.source === stateObj.attributes.source
+ );
if (
computeDomain(stateObj.entity_id) === "geo_location" &&
- (includesAll ||
- this._config.geo_location_sources.includes(
- stateObj.attributes.source
- ))
+ (allSource || sourceObj)
) {
- geoEntities.push(stateObj.entity_id);
+ geoEntities.push({
+ entity_id: stateObj.entity_id,
+ focus: sourceObj
+ ? (sourceObj.focus ?? true)
+ : (allSource?.focus ?? true),
+ });
}
}
return geoEntities;
@@ -364,8 +378,9 @@ class HuiMapCard extends LitElement implements LovelaceCard {
name: entityConf.name,
})),
...this._getSourceEntities(this.hass?.states).map((entity) => ({
- entity_id: entity,
- color: this._getColor(entity),
+ entity_id: entity.entity_id,
+ focus: entity.focus,
+ color: this._getColor(entity.entity_id),
})),
];
}
diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts
index c14f07cf790d..6ee537db30ba 100644
--- a/src/panels/lovelace/cards/types.ts
+++ b/src/panels/lovelace/cards/types.ts
@@ -298,6 +298,11 @@ export interface LogbookCardConfig extends LovelaceCardConfig {
theme?: string;
}
+interface GeoLocationSourceConfig {
+ source: string;
+ focus?: boolean;
+}
+
export interface MapCardConfig extends LovelaceCardConfig {
type: "map";
title?: string;
@@ -307,7 +312,7 @@ export interface MapCardConfig extends LovelaceCardConfig {
default_zoom?: number;
entities?: Array
;
hours_to_show?: number;
- geo_location_sources?: string[];
+ geo_location_sources?: Array;
dark_mode?: boolean;
theme_mode?: ThemeMode;
}
diff --git a/src/panels/lovelace/common/confirm-action.ts b/src/panels/lovelace/common/confirm-action.ts
new file mode 100644
index 000000000000..2b783cb3f3a6
--- /dev/null
+++ b/src/panels/lovelace/common/confirm-action.ts
@@ -0,0 +1,25 @@
+import type { ConfirmationRestrictionConfig } from "../../../data/lovelace/config/action";
+import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
+import { HomeAssistant } from "../../../types";
+
+export const confirmAction = async (
+ node: HTMLElement,
+ hass: HomeAssistant,
+ config: ConfirmationRestrictionConfig,
+ action: string
+): Promise => {
+ if (
+ config.exemptions &&
+ config.exemptions.some((e) => e.user === hass!.user?.id)
+ ) {
+ return true;
+ }
+
+ return showConfirmationDialog(node, {
+ text:
+ config.text ||
+ hass.localize("ui.panel.lovelace.cards.actions.action_confirmation", {
+ action,
+ }),
+ });
+};
diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts
index c5c5d8cc677d..0a9a39be8e38 100644
--- a/src/panels/lovelace/components/hui-card-options.ts
+++ b/src/panels/lovelace/components/hui-card-options.ts
@@ -28,7 +28,10 @@ import "../../../components/ha-icon-button";
import "../../../components/ha-list-item";
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import { saveConfig } from "../../../data/lovelace/config/types";
-import { isStrategyView } from "../../../data/lovelace/config/view";
+import {
+ isStrategyView,
+ type LovelaceViewConfig,
+} from "../../../data/lovelace/config/view";
import {
showAlertDialog,
showPromptDialog,
@@ -40,12 +43,14 @@ import { computeCardSize } from "../common/compute-card-size";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import {
addCard,
+ addSection,
deleteCard,
moveCardToContainer,
moveCardToIndex,
} from "../editor/config-util";
import {
LovelaceCardPath,
+ type LovelaceContainerPath,
findLovelaceItems,
getLovelaceContainerPath,
parseLovelaceCardPath,
@@ -53,6 +58,7 @@ import {
import { showSelectViewDialog } from "../editor/select-view/show-select-view-dialog";
import { Lovelace, LovelaceCard } from "../types";
import { SECTIONS_VIEW_LAYOUT } from "../views/const";
+import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
@customElement("hui-card-options")
export class HuiCardOptions extends LitElement {
@@ -353,34 +359,79 @@ export class HuiCardOptions extends LitElement {
allowDashboardChange: true,
header: this.hass!.localize("ui.panel.lovelace.editor.move_card.header"),
viewSelectedCallback: async (urlPath, selectedDashConfig, viewIndex) => {
- const view = selectedDashConfig.views[viewIndex];
+ const fromView = selectedDashConfig.views[this.path![0]];
+ let toView = selectedDashConfig.views[viewIndex];
+ let newConfig = selectedDashConfig;
- if (!isStrategyView(view) && view.type === SECTIONS_VIEW_LAYOUT) {
+ if (isStrategyView(toView)) {
showAlertDialog(this, {
title: this.hass!.localize(
"ui.panel.lovelace.editor.move_card.error_title"
),
text: this.hass!.localize(
- "ui.panel.lovelace.editor.move_card.error_text_section"
+ "ui.panel.lovelace.editor.move_card.error_text_strategy"
),
warning: true,
});
return;
}
+ const isSectionsView = toView.type === SECTIONS_VIEW_LAYOUT;
+
+ let toPath: LovelaceContainerPath = [viewIndex];
+
+ // If the view is a section view and has no "imported cards" section, adds a default section.
+ if (isSectionsView) {
+ const importedCardHeading = fromView.title
+ ? this.hass!.localize(
+ "ui.panel.lovelace.editor.section.imported_card_section_title_view",
+ { view_title: fromView.title }
+ )
+ : this.hass!.localize(
+ "ui.panel.lovelace.editor.section.imported_card_section_title_default"
+ );
+
+ let sectionIndex = toView.sections
+ ? toView.sections.findIndex(
+ (s) =>
+ "cards" in s &&
+ s.cards?.some(
+ (c) =>
+ c.type === "heading" && c.heading === importedCardHeading
+ )
+ )
+ : -1;
+ if (sectionIndex === -1) {
+ const newSection: LovelaceSectionConfig = {
+ type: "grid",
+ cards: [
+ {
+ type: "heading",
+ heading: importedCardHeading,
+ },
+ ],
+ };
+ newConfig = addSection(selectedDashConfig, viewIndex, newSection);
+ toView = newConfig.views[viewIndex] as LovelaceViewConfig;
+ sectionIndex = toView.sections!.length - 1;
+ }
+ toPath = [viewIndex, sectionIndex];
+ }
+
if (urlPath === this.lovelace!.urlPath) {
this.lovelace!.saveConfig(
- moveCardToContainer(this.lovelace!.config, this.path!, [viewIndex])
+ moveCardToContainer(newConfig, this.path!, toPath)
);
showSaveSuccessToast(this, this.hass!);
return;
}
try {
const { cardIndex } = parseLovelaceCardPath(this.path!);
+ const card = this._cards[cardIndex];
await saveConfig(
this.hass!,
urlPath,
- addCard(selectedDashConfig, [viewIndex], this._cards[cardIndex])
+ addCard(newConfig, toPath, card)
);
this.lovelace!.saveConfig(
deleteCard(this.lovelace!.config, this.path!)
diff --git a/src/panels/lovelace/components/hui-input-list-editor.ts b/src/panels/lovelace/components/hui-input-list-editor.ts
deleted file mode 100644
index 6616753fc81f..000000000000
--- a/src/panels/lovelace/components/hui-input-list-editor.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { mdiClose } from "@mdi/js";
-import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
-import { customElement, property } from "lit/decorators";
-import { fireEvent } from "../../../common/dom/fire_event";
-import "../../../components/ha-icon-button";
-import "../../../components/ha-textfield";
-import { HomeAssistant } from "../../../types";
-import { EditorTarget } from "../editor/types";
-
-@customElement("hui-input-list-editor")
-export class HuiInputListEditor extends LitElement {
- @property({ type: Array }) public value?: string[];
-
- @property({ attribute: false }) public hass?: HomeAssistant;
-
- @property() public inputLabel?: string;
-
- protected render() {
- if (!this.value) {
- return nothing;
- }
-
- return html`
- ${this.value.map(
- (listEntry, index) => html`
-
-
-
- `
- )}
-
- `;
- }
-
- private _addEntry(ev: Event): void {
- const target = ev.target! as EditorTarget;
- if (target.value === "") {
- return;
- }
- const newEntries = this.value!.concat(target.value as string);
- target.value = "";
- fireEvent(this, "value-changed", {
- value: newEntries,
- });
- (ev.target! as LitElement).blur();
- }
-
- private _valueChanged(ev: Event): void {
- ev.stopPropagation();
- const target = ev.target! as EditorTarget;
- const newEntries = this.value!.concat();
- newEntries[target.index!] = target.value!;
- fireEvent(this, "value-changed", {
- value: newEntries,
- });
- }
-
- private _handleKeyDown(ev: KeyboardEvent) {
- if (ev.key === "Enter") {
- ev.stopPropagation();
- this._consolidateEntries(ev);
- }
- }
-
- private _consolidateEntries(ev: Event): void {
- const target = ev.target! as EditorTarget;
- if (target.value === "") {
- const newEntries = this.value!.concat();
- newEntries.splice(target.index!, 1);
- fireEvent(this, "value-changed", {
- value: newEntries,
- });
- }
- }
-
- private _removeEntry(ev: Event): void {
- const parent = (ev.currentTarget as any).parentElement;
- const newEntries = this.value!.concat();
- newEntries.splice(parent.index!, 1);
- fireEvent(this, "value-changed", {
- value: newEntries,
- });
- }
-
- static get styles(): CSSResultGroup {
- return css`
- ha-icon-button {
- margin-right: -24px;
- margin-inline-end: -24px;
- margin-inline-start: initial;
- color: var(--secondary-text-color);
- }
- ha-textfield {
- display: block;
- }
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hui-input-list-editor": HuiInputListEditor;
- }
-}
diff --git a/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts
index e8e0c70f3c43..3f3280a91b53 100644
--- a/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-entities-card-editor.ts
@@ -81,13 +81,7 @@ const callServiceEntitiesRowConfigStruct = object({
const conditionalEntitiesRowConfigStruct = object({
type: literal("conditional"),
row: any(),
- conditions: array(
- object({
- entity: string(),
- state: optional(string()),
- state_not: optional(string()),
- })
- ),
+ conditions: array(any()),
});
const dividerEntitiesRowConfigStruct = object({
diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
index 7aafbb59006e..41a32456e758 100644
--- a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
@@ -15,15 +15,17 @@ import {
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { hasLocation } from "../../../../common/entity/has_location";
+import { computeDomain } from "../../../../common/entity/compute_domain";
import "../../../../components/ha-form/ha-form";
import { SchemaUnion } from "../../../../components/ha-form/types";
+import type { SelectSelector } from "../../../../data/selector";
import "../../../../components/ha-formfield";
import "../../../../components/ha-switch";
+import "../../../../components/ha-selector/ha-selector-select";
import { HomeAssistant, ValueChangedEvent } from "../../../../types";
import { DEFAULT_HOURS_TO_SHOW, DEFAULT_ZOOM } from "../../cards/hui-map-card";
import { MapCardConfig } from "../../cards/types";
import "../../components/hui-entity-editor";
-import "../../components/hui-input-list-editor";
import { EntityConfig } from "../../entity-rows/types";
import { LovelaceCardEditor } from "../../types";
import { processEditorEntities } from "../process-editor-entities";
@@ -42,6 +44,14 @@ export const mapEntitiesConfigStruct = union([
string(),
]);
+const geoSourcesConfigStruct = union([
+ object({
+ source: string(),
+ focus: optional(boolean()),
+ }),
+ string(),
+]);
+
const cardConfigStruct = assign(
baseLovelaceCardConfig,
object({
@@ -51,7 +61,7 @@ const cardConfigStruct = assign(
dark_mode: optional(boolean()),
entities: array(mapEntitiesConfigStruct),
hours_to_show: optional(number()),
- geo_location_sources: optional(array(string())),
+ geo_location_sources: optional(array(geoSourcesConfigStruct)),
auto_fit: optional(boolean()),
theme_mode: optional(string()),
})
@@ -67,6 +77,8 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
@state() private _configEntities?: EntityConfig[];
+ @state() private _possibleGeoSources?: { value: string; label?: string }[];
+
private _schema = memoizeOne(
(localize: LocalizeFunc) =>
[
@@ -135,8 +147,12 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
: [];
}
+ private _geoSourcesStrings = memoizeOne((sources): string[] | undefined =>
+ sources?.map((s) => (typeof s === "string" ? s : s.source))
+ );
+
get _geo_location_sources(): string[] {
- return this._config!.geo_location_sources || [];
+ return this._geoSourcesStrings(this._config!.geo_location_sources) || [];
}
protected render() {
@@ -166,17 +182,40 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
)}
-
+ .selector=${this._selectSchema(
+ this._possibleGeoSources,
+ this.hass.localize
+ )}
+ >
`;
}
+ private _selectSchema = memoizeOne(
+ (options, localize: LocalizeFunc): SelectSelector => ({
+ select: {
+ sort: true,
+ multiple: true,
+ custom_value: true,
+ options: options.length
+ ? options
+ : [
+ {
+ value: "",
+ label: localize(
+ "ui.panel.lovelace.editor.card.map.no_geo_location_sources"
+ ),
+ },
+ ],
+ },
+ })
+ );
+
private _entitiesValueChanged(ev: EntitiesEditorEvent): void {
if (ev.detail && ev.detail.entities) {
this._config = { ...this._config!, entities: ev.detail.entities };
@@ -201,9 +240,16 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
this._config = { ...this._config };
delete this._config.geo_location_sources;
} else {
+ const newSources = value.map(
+ (newSource) =>
+ this._config!.geo_location_sources?.find(
+ (oldSource) =>
+ typeof oldSource === "object" && oldSource.source === newSource
+ ) || newSource
+ );
this._config = {
...this._config,
- geo_location_sources: value,
+ geo_location_sources: newSources,
};
}
fireEvent(this, "config-changed", { config: this._config });
@@ -213,6 +259,25 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
+ protected willUpdate() {
+ if (this.hass && !this._possibleGeoSources) {
+ const sources: Record = {};
+ Object.entries(this.hass.states).forEach(([entity_id, stateObj]) => {
+ const domain = computeDomain(entity_id);
+ if (domain === "geo_location" && stateObj.attributes.source) {
+ sources[stateObj.attributes.source] = stateObj.attributes.attribution;
+ }
+ });
+
+ this._possibleGeoSources = Object.entries(sources).map(
+ ([source, attribution]) => ({
+ value: source,
+ label: attribution || source,
+ })
+ );
+ }
+ }
+
private _computeLabelCallback = (
schema: SchemaUnion>
) => {
diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts
index 004bba358bba..7990f21eaab7 100644
--- a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts
@@ -48,6 +48,7 @@ const cardConfigStruct = assign(
vertical: optional(boolean()),
tap_action: optional(actionConfigStruct),
icon_tap_action: optional(actionConfigStruct),
+ hold_action: optional(actionConfigStruct),
features: optional(array(any())),
})
);
@@ -156,6 +157,14 @@ export class HuiTileCardEditor
},
},
},
+ {
+ name: "hold_action",
+ selector: {
+ ui_action: {
+ default_action: "none",
+ },
+ },
+ },
],
},
] as const satisfies readonly HaFormSchema[]
diff --git a/src/panels/lovelace/editor/structs/action-struct.ts b/src/panels/lovelace/editor/structs/action-struct.ts
index d1f8d90c483a..ed918e6d1f8b 100644
--- a/src/panels/lovelace/editor/structs/action-struct.ts
+++ b/src/panels/lovelace/editor/structs/action-struct.ts
@@ -16,7 +16,7 @@ const actionConfigStructUser = object({
user: string(),
});
-const actionConfigStructConfirmation = union([
+export const actionConfigStructConfirmation = union([
boolean(),
object({
text: optional(string()),
diff --git a/src/panels/lovelace/editor/structs/entities-struct.ts b/src/panels/lovelace/editor/structs/entities-struct.ts
index 837f621b19f8..870e097b85f3 100644
--- a/src/panels/lovelace/editor/structs/entities-struct.ts
+++ b/src/panels/lovelace/editor/structs/entities-struct.ts
@@ -1,6 +1,9 @@
import { union, object, string, optional, boolean, enums } from "superstruct";
import { TIMESTAMP_RENDERING_FORMATS } from "../../components/types";
-import { actionConfigStruct } from "./action-struct";
+import {
+ actionConfigStruct,
+ actionConfigStructConfirmation,
+} from "./action-struct";
export const entitiesConfigStruct = union([
object({
@@ -14,6 +17,7 @@ export const entitiesConfigStruct = union([
tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
+ confirmation: optional(actionConfigStructConfirmation),
}),
string(),
]);
diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts
index dadd34449ea2..ef0a34233356 100644
--- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts
+++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts
@@ -167,7 +167,6 @@ export class HuiUnusedEntities extends LitElement {
static get styles(): CSSResultGroup {
return css`
:host {
- background: var(--lovelace-background);
overflow: hidden;
}
.container {
diff --git a/src/panels/lovelace/entity-rows/hui-button-entity-row.ts b/src/panels/lovelace/entity-rows/hui-button-entity-row.ts
index 1b9d5cf583e7..c649b2664b1a 100644
--- a/src/panels/lovelace/entity-rows/hui-button-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-button-entity-row.ts
@@ -14,6 +14,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { ActionRowConfig, LovelaceRow } from "./types";
+import { confirmAction } from "../common/confirm-action";
@customElement("hui-button-entity-row")
class HuiButtonEntityRow extends LitElement implements LovelaceRow {
@@ -69,11 +70,21 @@ class HuiButtonEntityRow extends LitElement implements LovelaceRow {
`;
}
- private _pressButton(ev): void {
+ private async _pressButton(ev): Promise {
ev.stopPropagation();
- this.hass.callService("button", "press", {
- entity_id: this._config!.entity,
- });
+ if (
+ !this._config?.confirmation ||
+ (await confirmAction(
+ this,
+ this.hass,
+ this._config.confirmation,
+ this.hass.localize("ui.card.button.press")
+ ))
+ ) {
+ this.hass.callService("button", "press", {
+ entity_id: this._config!.entity,
+ });
+ }
}
}
diff --git a/src/panels/lovelace/entity-rows/hui-input-button-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-button-entity-row.ts
index 97b2a08dba6e..84697b89e5b5 100644
--- a/src/panels/lovelace/entity-rows/hui-input-button-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-input-button-entity-row.ts
@@ -14,6 +14,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { ActionRowConfig, LovelaceRow } from "./types";
+import { confirmAction } from "../common/confirm-action";
@customElement("hui-input-button-entity-row")
class HuiInputButtonEntityRow extends LitElement implements LovelaceRow {
@@ -69,11 +70,21 @@ class HuiInputButtonEntityRow extends LitElement implements LovelaceRow {
`;
}
- private _pressButton(ev): void {
+ private async _pressButton(ev): Promise {
ev.stopPropagation();
- this.hass.callService("input_button", "press", {
- entity_id: this._config!.entity,
- });
+ if (
+ !this._config?.confirmation ||
+ (await confirmAction(
+ this,
+ this.hass,
+ this._config.confirmation,
+ this.hass.localize("ui.card.button.press")
+ ))
+ ) {
+ this.hass.callService("input_button", "press", {
+ entity_id: this._config!.entity,
+ });
+ }
}
}
diff --git a/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts b/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts
index 128a80808a7c..454225cd6ee8 100644
--- a/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts
@@ -13,16 +13,17 @@ import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
-import { EntityConfig, LovelaceRow } from "./types";
+import { ConfirmableRowConfig, LovelaceRow } from "./types";
import { callProtectedLockService } from "../../../data/lock";
+import { confirmAction } from "../common/confirm-action";
@customElement("hui-lock-entity-row")
class HuiLockEntityRow extends LitElement implements LovelaceRow {
@property({ attribute: false }) public hass?: HomeAssistant;
- @state() private _config?: EntityConfig;
+ @state() private _config?: ConfirmableRowConfig;
- public setConfig(config: EntityConfig): void {
+ public setConfig(config: ConfirmableRowConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
@@ -73,15 +74,21 @@ class HuiLockEntityRow extends LitElement implements LovelaceRow {
`;
}
- private _callService(ev): void {
+ private async _callService(ev): Promise {
ev.stopPropagation();
const stateObj = this.hass!.states[this._config!.entity];
- callProtectedLockService(
- this,
- this.hass!,
- stateObj,
- stateObj.state === "locked" ? "unlock" : "lock"
- );
+ const action = stateObj.state === "locked" ? "unlock" : "lock";
+ if (
+ !this._config?.confirmation ||
+ (await confirmAction(
+ this,
+ this.hass!,
+ this._config.confirmation,
+ this.hass!.localize(`ui.card.lock.${action}`)
+ ))
+ ) {
+ callProtectedLockService(this, this.hass!, stateObj, action);
+ }
}
}
diff --git a/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts b/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts
index 5d1646d9b29c..27ddae5b70f5 100644
--- a/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-scene-entity-row.ts
@@ -16,6 +16,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { ActionRowConfig, LovelaceRow } from "./types";
+import { confirmAction } from "../common/confirm-action";
@customElement("hui-scene-entity-row")
class HuiSceneEntityRow extends LitElement implements LovelaceRow {
@@ -73,9 +74,19 @@ class HuiSceneEntityRow extends LitElement implements LovelaceRow {
`;
}
- private _callService(ev: Event): void {
+ private async _callService(ev: Event): Promise {
ev.stopPropagation();
- activateScene(this.hass, this._config!.entity);
+ if (
+ !this._config?.confirmation ||
+ (await confirmAction(
+ this,
+ this.hass,
+ this._config.confirmation,
+ this._config.action_name || this.hass.localize("ui.card.scene.activate")
+ ))
+ ) {
+ activateScene(this.hass, this._config!.entity);
+ }
}
}
diff --git a/src/panels/lovelace/entity-rows/hui-script-entity-row.ts b/src/panels/lovelace/entity-rows/hui-script-entity-row.ts
index 87602c939f22..e884f405074f 100644
--- a/src/panels/lovelace/entity-rows/hui-script-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-script-entity-row.ts
@@ -16,6 +16,7 @@ import "../components/hui-generic-entity-row";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { ActionRowConfig, LovelaceRow } from "./types";
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
+import { confirmAction } from "../common/confirm-action";
@customElement("hui-script-entity-row")
class HuiScriptEntityRow extends LitElement implements LovelaceRow {
@@ -91,12 +92,20 @@ class HuiScriptEntityRow extends LitElement implements LovelaceRow {
this._callService("turn_off");
}
- private _runScript(ev): void {
+ private async _runScript(ev): Promise {
ev.stopPropagation();
if (hasScriptFields(this.hass!, this._config!.entity)) {
showMoreInfoDialog(this, { entityId: this._config!.entity });
- } else {
+ } else if (
+ !this._config?.confirmation ||
+ (await confirmAction(
+ this,
+ this.hass!,
+ this._config.confirmation,
+ this._config.action_name || this.hass!.localize("ui.card.script.run")
+ ))
+ ) {
this._callService("turn_on");
}
}
diff --git a/src/panels/lovelace/entity-rows/types.ts b/src/panels/lovelace/entity-rows/types.ts
index d5c20534183c..b8dd9c8d4c7b 100644
--- a/src/panels/lovelace/entity-rows/types.ts
+++ b/src/panels/lovelace/entity-rows/types.ts
@@ -1,4 +1,7 @@
-import type { ActionConfig } from "../../../data/lovelace/config/action";
+import type {
+ ActionConfig,
+ ConfirmationRestrictionConfig,
+} from "../../../data/lovelace/config/action";
import type { HomeAssistant } from "../../../types";
import type { LegacyStateFilter } from "../common/evaluate-filter";
import type { Condition } from "../common/validate-condition";
@@ -11,7 +14,12 @@ export interface EntityConfig {
icon?: string;
image?: string;
}
-export interface ActionRowConfig extends EntityConfig {
+
+export interface ConfirmableRowConfig extends EntityConfig {
+ confirmation?: ConfirmationRestrictionConfig;
+}
+
+export interface ActionRowConfig extends ConfirmableRowConfig {
action_name?: string;
}
export interface EntityFilterEntityConfig extends EntityConfig {
diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts
index 939d063e8203..3c749fe1a87f 100644
--- a/src/panels/lovelace/hui-root.ts
+++ b/src/panels/lovelace/hui-root.ts
@@ -62,6 +62,7 @@ import {
fetchDashboards,
updateDashboard,
} from "../../data/lovelace/dashboard";
+import { getPanelTitle } from "../../data/panel";
import {
showAlertDialog,
showConfirmationDialog,
@@ -80,8 +81,8 @@ import { getLovelaceStrategy } from "./strategies/get-strategy";
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
import type { Lovelace } from "./types";
import "./views/hui-view";
+import "./views/hui-view-container";
import type { HUIView } from "./views/hui-view";
-import { getPanelTitle } from "../../data/panel";
@customElement("hui-root")
class HUIRoot extends LitElement {
@@ -291,6 +292,8 @@ class HUIRoot extends LitElement {
? getPanelTitle(this.hass, this.panel)
: undefined;
+ const background = curViewConfig?.background || this.config.background;
+
return html`
`;
}
@@ -937,21 +947,6 @@ class HUIRoot extends LitElement {
view.hass = this.hass;
view.narrow = this.narrow;
- const configBackground = viewConfig.background || this.config.background;
-
- const backgroundStyle =
- typeof configBackground === "string"
- ? configBackground
- : configBackground?.image
- ? `center / cover no-repeat url('${configBackground.image}')`
- : undefined;
-
- if (backgroundStyle) {
- root.style.setProperty("--lovelace-background", backgroundStyle);
- } else {
- root.style.removeProperty("--lovelace-background");
- }
-
root.appendChild(view);
}
@@ -1063,30 +1058,26 @@ class HUIRoot extends LitElement {
mwc-button.warning:not([disabled]) {
color: var(--error-color);
}
- #view {
+ hui-view-container {
position: relative;
display: flex;
- padding-top: calc(var(--header-height) + env(safe-area-inset-top));
min-height: 100vh;
box-sizing: border-box;
+ padding-top: calc(var(--header-height) + env(safe-area-inset-top));
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-inline-start: env(safe-area-inset-left);
padding-inline-end: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
- background: var(
- --lovelace-background,
- var(--primary-background-color)
- );
}
- #view > * {
+ hui-view-container > * {
flex: 1 1 100%;
max-width: 100%;
}
/**
* In edit mode we have the tab bar on a new line *
*/
- .edit-mode #view {
+ .edit-mode hui-view-container {
padding-top: calc(
var(--header-height) + 48px + env(safe-area-inset-top)
);
diff --git a/src/panels/lovelace/views/hui-view-container.ts b/src/panels/lovelace/views/hui-view-container.ts
new file mode 100644
index 000000000000..73da5a04c1f4
--- /dev/null
+++ b/src/panels/lovelace/views/hui-view-container.ts
@@ -0,0 +1,142 @@
+import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
+import { customElement, property, state } from "lit/decorators";
+import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
+import { listenMediaQuery } from "../../../common/dom/media_query";
+import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
+import type { HomeAssistant } from "../../../types";
+
+type BackgroundConfig = LovelaceViewConfig["background"];
+
+@customElement("hui-view-container")
+class HuiViewContainer extends LitElement {
+ @property({ attribute: false }) hass?: HomeAssistant;
+
+ @property({ attribute: false }) background?: BackgroundConfig;
+
+ @property({ attribute: false }) theme?: LovelaceViewConfig["theme"];
+
+ @state() themeBackground?: string;
+
+ private _unsubMediaQuery?: () => void;
+
+ public connectedCallback(): void {
+ super.connectedCallback();
+ this._setUpMediaQuery();
+ this._applyTheme();
+ }
+
+ public disconnectedCallback(): void {
+ super.disconnectedCallback();
+ this._clearmediaQuery();
+ }
+
+ private _clearmediaQuery() {
+ if (this._unsubMediaQuery) {
+ this._unsubMediaQuery();
+ this._unsubMediaQuery = undefined;
+ }
+ }
+
+ private _setUpMediaQuery() {
+ this._unsubMediaQuery = listenMediaQuery(
+ "(prefers-color-scheme: dark)",
+ this._applyTheme.bind(this)
+ );
+ }
+
+ private _isFixedBackground(background?: BackgroundConfig) {
+ if (typeof background === "string") {
+ return background.includes(" fixed");
+ }
+ return false;
+ }
+
+ private _computeBackgroundProperty(background?: BackgroundConfig) {
+ if (typeof background === "object" && background.image) {
+ return `center / cover no-repeat url('${background.image}')`;
+ }
+ if (typeof background === "string") {
+ return background;
+ }
+ return null;
+ }
+
+ protected willUpdate(changedProperties: PropertyValues) {
+ super.willUpdate(changedProperties);
+ if (changedProperties.has("hass") && this.hass) {
+ const oldHass = changedProperties.get("hass");
+ if (
+ !oldHass ||
+ this.hass.themes !== oldHass.themes ||
+ this.hass.selectedTheme !== oldHass.selectedTheme
+ ) {
+ this._applyTheme();
+ return;
+ }
+ }
+
+ if (changedProperties.has("theme") || changedProperties.has("background")) {
+ this._applyTheme();
+ }
+ }
+
+ render() {
+ return html``;
+ }
+
+ private _applyTheme() {
+ if (this.hass) {
+ applyThemesOnElement(this, this.hass?.themes, this.theme);
+ }
+
+ const computedStyles = getComputedStyle(this);
+ const themeBackground = computedStyles.getPropertyValue(
+ "--lovelace-background"
+ );
+
+ const fixedBackground = this._isFixedBackground(
+ this.background || themeBackground
+ );
+ const viewBackground = this._computeBackgroundProperty(this.background);
+ this.toggleAttribute("fixed-background", fixedBackground);
+ this.style.setProperty("--view-background", viewBackground);
+ }
+
+ static get styles(): CSSResultGroup {
+ return css`
+ :host {
+ display: relative;
+ }
+ /* Fixed background hack for Safari iOS */
+ :host([fixed-background]) ::slotted(*):before {
+ display: block;
+ content: "";
+ z-index: -1;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 100%;
+ width: 100%;
+ background: var(
+ --view-background,
+ var(--lovelace-background, var(--primary-background-color))
+ );
+ background-attachment: scroll !important;
+ }
+ :host(:not(fixed-background)) {
+ background: var(
+ --view-background,
+ var(--lovelace-background, var(--primary-background-color))
+ );
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hui-view-container": HuiViewContainer;
+ }
+}
diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts
index 397be95d51bd..12aa24eb8e60 100644
--- a/src/panels/lovelace/views/hui-view.ts
+++ b/src/panels/lovelace/views/hui-view.ts
@@ -1,6 +1,5 @@
import { PropertyValues, ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
-import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { HASSDomEvent } from "../../../common/dom/fire_event";
import "../../../components/entity/ha-state-label-badge";
import "../../../components/ha-svg-icon";
@@ -84,8 +83,6 @@ export class HUIView extends ReactiveElement {
private _layoutElement?: LovelaceViewElement;
- private _viewConfigTheme?: string;
-
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card");
element.hass = this.hass;
@@ -138,11 +135,6 @@ export class HUIView extends ReactiveElement {
return this;
}
- public connectedCallback(): void {
- super.connectedCallback();
- this._applyTheme();
- }
-
public willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties);
@@ -194,18 +186,6 @@ export class HUIView extends ReactiveElement {
});
this._layoutElement.hass = this.hass;
-
- const oldHass = changedProperties.get("hass") as
- | this["hass"]
- | undefined;
-
- if (
- !oldHass ||
- this.hass.themes !== oldHass.themes ||
- this.hass.selectedTheme !== oldHass.selectedTheme
- ) {
- this._applyTheme();
- }
}
if (changedProperties.has("narrow")) {
this._layoutElement.narrow = this.narrow;
@@ -237,28 +217,6 @@ export class HUIView extends ReactiveElement {
}
}
- private _applyTheme() {
- applyThemesOnElement(this, this.hass.themes, this._viewConfigTheme);
- if (this._viewConfigTheme) {
- // Set lovelace background color to root element, so it will be placed under the header too
- const computedStyles = getComputedStyle(this);
- let lovelaceBackground = computedStyles.getPropertyValue(
- "--lovelace-background"
- );
- if (!lovelaceBackground) {
- lovelaceBackground = computedStyles.getPropertyValue(
- "--primary-background-color"
- );
- }
- if (lovelaceBackground) {
- this.parentElement?.style.setProperty(
- "--lovelace-background",
- lovelaceBackground
- );
- }
- }
- }
-
private async _initializeConfig() {
let viewConfig = this.lovelace.config.views[this.index];
let isStrategy = false;
@@ -296,9 +254,6 @@ export class HUIView extends ReactiveElement {
this._layoutElement!.badges = this._badges;
this._layoutElement!.sections = this._sections;
- applyThemesOnElement(this, this.hass.themes, viewConfig.theme);
- this._viewConfigTheme = viewConfig.theme;
-
if (addLayoutElement) {
while (this.lastChild) {
this.removeChild(this.lastChild);
diff --git a/src/translations/en.json b/src/translations/en.json
index 11c77f0f77a9..223d64e39867 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -1191,6 +1191,7 @@
"skip": "Skip",
"clear_skipped": "Clear skipped",
"install": "Install",
+ "update": "Update",
"create_backup": "Create backup before updating",
"auto_update_enabled_title": "Can not skip version",
"auto_update_enabled_text": "Automatic updates for this item have been enabled; skipping it is, therefore, unavailable. You can either install this update now or wait for Home Assistant to do it automatically."
@@ -1929,7 +1930,10 @@
"multiselect": {
"failed": "Failed to update {number} items."
},
- "learn_more": "Learn more"
+ "learn_more": "Learn more",
+ "show_url": "Show full URL",
+ "hide_url": "Hide URL",
+ "copy_link": "Copy link"
},
"updates": {
"caption": "Updates",
@@ -2397,7 +2401,9 @@
"enable_remote": "[%key:ui::common::enable%]",
"internal_url_automatic": "Automatic",
"internal_url_https_error_title": "Invalid local network URL",
- "internal_url_https_error_description": "You have configured an HTTPS certificate in Home Assistant. This means that your internal URL needs to be set to a domain covered by the certficate."
+ "internal_url_https_error_description": "You have configured an HTTPS certificate in Home Assistant. This means that your internal URL needs to be set to a domain covered by the certficate.",
+ "internal_url_automatic_description": "Use the configured network settings",
+ "internal_url_placeholder": "http://:8123"
},
"hardware": {
"caption": "Hardware",
@@ -2490,6 +2496,9 @@
"scroll_down_button": "New logs - Click to scroll",
"provider_not_found": "Log provider not found",
"provider_not_available": "Logs for ''{provider}'' are not available on your system.",
+ "current": "Current",
+ "previous": "Previous",
+ "startups_ago": "{boot} startups ago",
"detail": {
"logger": "Logger",
"source": "Source",
@@ -2861,6 +2870,7 @@
"copy_to_clipboard": "Copy to clipboard",
"search_in": "Search · {group}",
"unknown_entity": "unknown entity",
+ "edit_unknown_device": "Editor not available for unknown device",
"triggers": {
"name": "Triggers",
"header": "When",
@@ -3910,9 +3920,6 @@
"info": "Home Assistant Cloud provides a secure remote access to your instance while away from home. For more information on remote access and these settings visit our security documentation.",
"info_instance_will_be_available": "Your instance will be available at your Nabu Casa URL.",
"link_learn_how_it_works": "Learn how it works",
- "show_url": "Show full URL",
- "hide_url": "Hide URL",
- "copy_link": "Copy link",
"security_options": "Security options",
"external_activation": "Allow external activation of remote access",
"external_activation_secondary": "If you disable remote access on this page, having this setting enabled allows you to reactivate it remotely via your Nabu Casa account.",
@@ -5696,8 +5703,8 @@
},
"move_card": {
"header": "Choose a view to move the card to",
- "error_title": "Impossible to move the card",
- "error_text_section": "Moving a card to a section view is not supported yet. Use copy/cut/paste instead."
+ "strategy_error_title": "Impossible to move the card",
+ "strategy_error_text_strategy": "Moving a card to an auto generated view is not supported."
},
"change_position": {
"title": "Change card position",
@@ -5715,7 +5722,9 @@
"add_badge": "Add badge",
"add_card": "[%key:ui::panel::lovelace::editor::edit_card::add%]",
"create_section": "Create section",
- "default_section_title": "New section"
+ "default_section_title": "New section",
+ "imported_card_section_title_view": "Imported cards from ''{view_title}'' view",
+ "imported_card_section_title_default": "Imported cards from another view"
},
"delete_section": {
"title": "Delete section",
@@ -6081,6 +6090,7 @@
"map": {
"name": "Map",
"geo_location_sources": "Geolocation sources",
+ "no_geo_location_sources": "No geolocation sources available",
"dark_mode": "Dark mode?",
"appearance": "Appearance",
"theme_mode": "Theme Mode",
@@ -7664,6 +7674,7 @@
},
"backup": {
"search": "[%key:ui::panel::config::backup::picker::search%]",
+ "loading_backups": "Loading backups. This can take a few seconds.",
"no_backups": "You don't have any backups yet.",
"create_blocked_not_running": "Creating a backup is not possible right now because the system is in \"{state}\" state.",
"restore_blocked_not_running": "Restoring a backup is not possible right now because the system is in \"{state}\" state.",
diff --git a/src/util/url.ts b/src/util/url.ts
new file mode 100644
index 000000000000..ed3da5bb5926
--- /dev/null
+++ b/src/util/url.ts
@@ -0,0 +1,9 @@
+export function obfuscateUrl(url: string) {
+ if (url.endsWith(".ui.nabu.casa")) {
+ return "https://•••••••••••••••••.ui.nabu.casa";
+ }
+ // hide any words that look like they might be a hostname or IP address
+ return url.replace(/(?<=:\/\/)[\w-]+|(?<=\.)[\w-]+/g, (match) =>
+ "•".repeat(match.length)
+ );
+}
diff --git a/yarn.lock b/yarn.lock
index 8f8c64437907..5a866a60dd77 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -28,7 +28,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.25.9":
+"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.25.9, @babel/code-frame@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/code-frame@npm:7.26.0"
dependencies:
@@ -39,37 +39,37 @@ __metadata:
languageName: node
linkType: hard
-"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.9":
+"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.9, @babel/compat-data@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/compat-data@npm:7.26.0"
checksum: 10/e847d58222eb567da4bcc2c8e4e44b508d1a34626922858fe12edeb73b5f3c486e7e77a351725b4347525d623dc5046b8a6355df76f368560ca6cbac10fef2c5
languageName: node
linkType: hard
-"@babel/core@npm:7.25.9, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.0, @babel/core@npm:^7.24.4":
- version: 7.25.9
- resolution: "@babel/core@npm:7.25.9"
+"@babel/core@npm:7.26.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.0, @babel/core@npm:^7.24.4":
+ version: 7.26.0
+ resolution: "@babel/core@npm:7.26.0"
dependencies:
"@ampproject/remapping": "npm:^2.2.0"
- "@babel/code-frame": "npm:^7.25.9"
- "@babel/generator": "npm:^7.25.9"
+ "@babel/code-frame": "npm:^7.26.0"
+ "@babel/generator": "npm:^7.26.0"
"@babel/helper-compilation-targets": "npm:^7.25.9"
- "@babel/helper-module-transforms": "npm:^7.25.9"
- "@babel/helpers": "npm:^7.25.9"
- "@babel/parser": "npm:^7.25.9"
+ "@babel/helper-module-transforms": "npm:^7.26.0"
+ "@babel/helpers": "npm:^7.26.0"
+ "@babel/parser": "npm:^7.26.0"
"@babel/template": "npm:^7.25.9"
"@babel/traverse": "npm:^7.25.9"
- "@babel/types": "npm:^7.25.9"
+ "@babel/types": "npm:^7.26.0"
convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3"
semver: "npm:^6.3.1"
- checksum: 10/92cc69d9d59a5eb057527e69c41db46f05d0a8eeeb5ebab3f34e5ad040b74f34f20a4d97c3f3ede6476537cac93d2b46e3915b572269d2a039301dab068fd2e8
+ checksum: 10/65767bfdb1f02e80d3af4f138066670ef8fdd12293de85ef151758a901c191c797e86d2e99b11c4cdfca33c72385ecaf38bbd7fa692791ec44c77763496b9b93
languageName: node
linkType: hard
-"@babel/generator@npm:^7.25.9":
+"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/generator@npm:7.26.0"
dependencies:
@@ -179,7 +179,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-module-transforms@npm:^7.25.9":
+"@babel/helper-module-transforms@npm:^7.25.9, @babel/helper-module-transforms@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/helper-module-transforms@npm:7.26.0"
dependencies:
@@ -286,7 +286,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helpers@npm:^7.25.9":
+"@babel/helpers@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/helpers@npm:7.26.0"
dependencies:
@@ -410,7 +410,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/plugin-syntax-import-assertions@npm:^7.12.1, @babel/plugin-syntax-import-assertions@npm:^7.25.9":
+"@babel/plugin-syntax-import-assertions@npm:^7.12.1, @babel/plugin-syntax-import-assertions@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/plugin-syntax-import-assertions@npm:7.26.0"
dependencies:
@@ -421,7 +421,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/plugin-syntax-import-attributes@npm:^7.25.9":
+"@babel/plugin-syntax-import-attributes@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/plugin-syntax-import-attributes@npm:7.26.0"
dependencies:
@@ -548,7 +548,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/plugin-transform-class-static-block@npm:^7.25.9":
+"@babel/plugin-transform-class-static-block@npm:^7.26.0":
version: 7.26.0
resolution: "@babel/plugin-transform-class-static-block@npm:7.26.0"
dependencies:
@@ -940,6 +940,18 @@ __metadata:
languageName: node
linkType: hard
+"@babel/plugin-transform-regexp-modifiers@npm:^7.26.0":
+ version: 7.26.0
+ resolution: "@babel/plugin-transform-regexp-modifiers@npm:7.26.0"
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin": "npm:^7.25.9"
+ "@babel/helper-plugin-utils": "npm:^7.25.9"
+ peerDependencies:
+ "@babel/core": ^7.0.0
+ checksum: 10/726deca486bbd4b176f8a966eb0f4aabc19d9def3b8dabb8b3a656778eca0df1fda3f3c92b213aa5a184232fdafd5b7bd73b4e24ca4345c498ef6baff2bda4e1
+ languageName: node
+ linkType: hard
+
"@babel/plugin-transform-reserved-words@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/plugin-transform-reserved-words@npm:7.25.9"
@@ -1085,11 +1097,11 @@ __metadata:
languageName: node
linkType: hard
-"@babel/preset-env@npm:7.25.9, @babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.13.0":
- version: 7.25.9
- resolution: "@babel/preset-env@npm:7.25.9"
+"@babel/preset-env@npm:7.26.0, @babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.13.0":
+ version: 7.26.0
+ resolution: "@babel/preset-env@npm:7.26.0"
dependencies:
- "@babel/compat-data": "npm:^7.25.9"
+ "@babel/compat-data": "npm:^7.26.0"
"@babel/helper-compilation-targets": "npm:^7.25.9"
"@babel/helper-plugin-utils": "npm:^7.25.9"
"@babel/helper-validator-option": "npm:^7.25.9"
@@ -1099,8 +1111,8 @@ __metadata:
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.25.9"
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.25.9"
"@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2"
- "@babel/plugin-syntax-import-assertions": "npm:^7.25.9"
- "@babel/plugin-syntax-import-attributes": "npm:^7.25.9"
+ "@babel/plugin-syntax-import-assertions": "npm:^7.26.0"
+ "@babel/plugin-syntax-import-attributes": "npm:^7.26.0"
"@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6"
"@babel/plugin-transform-arrow-functions": "npm:^7.25.9"
"@babel/plugin-transform-async-generator-functions": "npm:^7.25.9"
@@ -1108,7 +1120,7 @@ __metadata:
"@babel/plugin-transform-block-scoped-functions": "npm:^7.25.9"
"@babel/plugin-transform-block-scoping": "npm:^7.25.9"
"@babel/plugin-transform-class-properties": "npm:^7.25.9"
- "@babel/plugin-transform-class-static-block": "npm:^7.25.9"
+ "@babel/plugin-transform-class-static-block": "npm:^7.26.0"
"@babel/plugin-transform-classes": "npm:^7.25.9"
"@babel/plugin-transform-computed-properties": "npm:^7.25.9"
"@babel/plugin-transform-destructuring": "npm:^7.25.9"
@@ -1141,6 +1153,7 @@ __metadata:
"@babel/plugin-transform-private-property-in-object": "npm:^7.25.9"
"@babel/plugin-transform-property-literals": "npm:^7.25.9"
"@babel/plugin-transform-regenerator": "npm:^7.25.9"
+ "@babel/plugin-transform-regexp-modifiers": "npm:^7.26.0"
"@babel/plugin-transform-reserved-words": "npm:^7.25.9"
"@babel/plugin-transform-shorthand-properties": "npm:^7.25.9"
"@babel/plugin-transform-spread": "npm:^7.25.9"
@@ -1159,7 +1172,7 @@ __metadata:
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.0.0-0
- checksum: 10/7a9ca1a19949426498fdffc37eed919f9581226ea45b18be764d29ce81bbf8c8f2f37152300c3524b7a3861831d33f10d481810f3011dff702f780d3f79aa789
+ checksum: 10/a7a80314f845deea713985a6316361c476621c76cfe5c6c28e8b9558f01634b49bbfdd3581ef94b5d6cff5c2b8830468aa53a73f5b5c1224db2dfea5db7e676f
languageName: node
linkType: hard
@@ -1176,9 +1189,9 @@ __metadata:
languageName: node
linkType: hard
-"@babel/preset-typescript@npm:7.25.9":
- version: 7.25.9
- resolution: "@babel/preset-typescript@npm:7.25.9"
+"@babel/preset-typescript@npm:7.26.0":
+ version: 7.26.0
+ resolution: "@babel/preset-typescript@npm:7.26.0"
dependencies:
"@babel/helper-plugin-utils": "npm:^7.25.9"
"@babel/helper-validator-option": "npm:^7.25.9"
@@ -1187,16 +1200,16 @@ __metadata:
"@babel/plugin-transform-typescript": "npm:^7.25.9"
peerDependencies:
"@babel/core": ^7.0.0-0
- checksum: 10/bcb730ffc777e941eb34ade5052815b7091a0f1c49c6ae9515ebc3ffbfb52b2195dff3d289498b767e9ee898fc30ed100496f4f336a8be51725f2b4a73d7227a
+ checksum: 10/81a60826160163a3daae017709f42147744757b725b50c9024ef3ee5a402ee45fd2e93eaecdaaa22c81be91f7940916249cfb7711366431cfcacc69c95878c03
languageName: node
linkType: hard
-"@babel/runtime@npm:7.25.9, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
- version: 7.25.9
- resolution: "@babel/runtime@npm:7.25.9"
+"@babel/runtime@npm:7.26.0, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
+ version: 7.26.0
+ resolution: "@babel/runtime@npm:7.26.0"
dependencies:
regenerator-runtime: "npm:^0.14.0"
- checksum: 10/8d904cfcb433374b3bb90369452751c94ae69547cdd3679950de4527ac5d04195b9c4a1840482a6f3a84694cb22a6403a7f98b826d60cd945918223a4a6b479c
+ checksum: 10/9f4ea1c1d566c497c052d505587554e782e021e6ccd302c2ad7ae8291c8e16e3f19d4a7726fb64469e057779ea2081c28b7dbefec6d813a22f08a35712c0f699
languageName: node
linkType: hard
@@ -1400,150 +1413,150 @@ __metadata:
languageName: node
linkType: hard
-"@formatjs/ecma402-abstract@npm:2.2.0":
- version: 2.2.0
- resolution: "@formatjs/ecma402-abstract@npm:2.2.0"
+"@formatjs/ecma402-abstract@npm:2.2.1":
+ version: 2.2.1
+ resolution: "@formatjs/ecma402-abstract@npm:2.2.1"
dependencies:
- "@formatjs/fast-memoize": "npm:2.2.1"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/f836dfa8787c8d0d2022f7c77fd15025a402ee519f9cb6e68dfce2b937956059e4f96b6059c40506c7260d93bdfd0517001b568cdbea8db08b439ce5d21f437e
+ "@formatjs/fast-memoize": "npm:2.2.2"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/8c281e14cb5f12b8697225be6b0ac13d057911e257d3c23928aad985b535df90b7bb2a235aab22753a6e57aef98f00b826514fc3703e69018ccc98c8d9848f38
languageName: node
linkType: hard
-"@formatjs/fast-memoize@npm:2.2.1":
- version: 2.2.1
- resolution: "@formatjs/fast-memoize@npm:2.2.1"
+"@formatjs/fast-memoize@npm:2.2.2":
+ version: 2.2.2
+ resolution: "@formatjs/fast-memoize@npm:2.2.2"
dependencies:
- tslib: "npm:^2.7.0"
- checksum: 10/7bb12904d4c93cfae17006388b26d97cc0e32fef5af510f80974437382cd13a88b439b4407d96b6646af1806977cf34113f3dc8f1c96b28f73856700ee7857fd
+ tslib: "npm:2"
+ checksum: 10/c6e958753eb41bb0875734762a44126a0d570706a31b32bb409e759cd372184c28e294b02fce0b0f0999c171ef717d513eaf7936862c498d78428b97db446ff8
languageName: node
linkType: hard
-"@formatjs/icu-messageformat-parser@npm:2.8.0":
- version: 2.8.0
- resolution: "@formatjs/icu-messageformat-parser@npm:2.8.0"
+"@formatjs/icu-messageformat-parser@npm:2.9.1":
+ version: 2.9.1
+ resolution: "@formatjs/icu-messageformat-parser@npm:2.9.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/icu-skeleton-parser": "npm:1.8.4"
- tslib: "npm:^2.7.0"
- checksum: 10/26464d14b19da2cb2b4c946ac619cac68936e0613324b3c5d317aaa5192089c3c56f53673c0a8549bb66d4939d60c617a010e7998e73f93755d2a46a82d8b255
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/icu-skeleton-parser": "npm:1.8.5"
+ tslib: "npm:2"
+ checksum: 10/f52c7c55b1dfc141910089a0494abd98d1c13c0a359cfb3bfa0668a5e2015c0c579bf161978fdb3ab40fa9a7374a37ac062f8710ed285429bf60abde4a5d1183
languageName: node
linkType: hard
-"@formatjs/icu-skeleton-parser@npm:1.8.4":
- version: 1.8.4
- resolution: "@formatjs/icu-skeleton-parser@npm:1.8.4"
+"@formatjs/icu-skeleton-parser@npm:1.8.5":
+ version: 1.8.5
+ resolution: "@formatjs/icu-skeleton-parser@npm:1.8.5"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- tslib: "npm:^2.7.0"
- checksum: 10/15610bdbf7b8405191a41f044ccd2ba152364690b20a23bec32b98764e3de44029bb03cb897ef0bce0c2f1643ba2333dc79e99724f2619a69154c13617eb27af
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ tslib: "npm:2"
+ checksum: 10/5b9c57f80b751483bef8897ff9607a9eb215fd7a8d8ae9fa5c631edf6d16fa4532c853395f20b7f3f38d6d4d1a35b98cd06421291203c7ad333f52077ef2a406
languageName: node
linkType: hard
-"@formatjs/intl-datetimeformat@npm:6.15.0":
- version: 6.15.0
- resolution: "@formatjs/intl-datetimeformat@npm:6.15.0"
+"@formatjs/intl-datetimeformat@npm:6.16.1":
+ version: 6.16.1
+ resolution: "@formatjs/intl-datetimeformat@npm:6.16.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/847c2c8acc6a9782e30fbb771639513ef3f73d1d96dbbff5042509925ca4e26c106c0fd87bf488bc93ceaaddad463c089f9d03d9a700075928c2fc937cec44b1
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/494868322d396e0eede6a27c16047858944f42fd3b45cf5d155f963df62e694b842ac0bef07e23aa73fa55cf143956d642d05ea62a3e762632101451975b5fc4
languageName: node
linkType: hard
-"@formatjs/intl-displaynames@npm:6.7.0":
- version: 6.7.0
- resolution: "@formatjs/intl-displaynames@npm:6.7.0"
+"@formatjs/intl-displaynames@npm:6.8.1":
+ version: 6.8.1
+ resolution: "@formatjs/intl-displaynames@npm:6.8.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/975c39d987920883cc520d3d950afeb52850fc36e5ebabbf006a579b1e5a99e1b5ef6d7126181f0d5449bf12ef8c63e1e8952c456475c77d41f0d021fdc8c6cb
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/627fc625e14b4d1bea5b2bf41e40050eb9775d0f66780e155719e21c062f9b3331d08b488ebcd3608c60999498af5a39e67cb5fd2a6d54a0e7395d7a63bfe643
languageName: node
linkType: hard
-"@formatjs/intl-enumerator@npm:1.7.0":
- version: 1.7.0
- resolution: "@formatjs/intl-enumerator@npm:1.7.0"
+"@formatjs/intl-enumerator@npm:1.8.1":
+ version: 1.8.1
+ resolution: "@formatjs/intl-enumerator@npm:1.8.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- tslib: "npm:^2.7.0"
- checksum: 10/de37c701e37cbb1af250df406b0282c4df1bf62d440b92d6d5c11dce88edd780b7ba7885fb9561de317a40bb0ab5169c63178effbeccb78124a7758d2c01a833
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ tslib: "npm:2"
+ checksum: 10/0e4250de905e757fb88d6ff072968c72ed3a39de8ddaed73c38c0099825f11530c9b8e224573ae6e46cf49f1318e463f40ba2cdfa25cb7415382ba952b570bdc
languageName: node
linkType: hard
-"@formatjs/intl-getcanonicallocales@npm:2.4.0":
- version: 2.4.0
- resolution: "@formatjs/intl-getcanonicallocales@npm:2.4.0"
+"@formatjs/intl-getcanonicallocales@npm:2.5.1":
+ version: 2.5.1
+ resolution: "@formatjs/intl-getcanonicallocales@npm:2.5.1"
dependencies:
- tslib: "npm:^2.7.0"
- checksum: 10/ec1dc0498c05189441bf1cfe5c89f9d5e9bad4e1c41ec735c8d3248852b885d39a24436cccfdcee8a2246158365f20e975af7854c83f947c38b40496d29e1428
+ tslib: "npm:2"
+ checksum: 10/5e83c0b3574333e5027c3c4f74ea20800e50e36fb8efa69361457b57f618738f478b5d22777ba30a2b7a15bdff60101d8119169c909b33577244747d52e59614
languageName: node
linkType: hard
-"@formatjs/intl-listformat@npm:7.6.0":
- version: 7.6.0
- resolution: "@formatjs/intl-listformat@npm:7.6.0"
+"@formatjs/intl-listformat@npm:7.7.1":
+ version: 7.7.1
+ resolution: "@formatjs/intl-listformat@npm:7.7.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/3024b00f5965385c816a890eccc5891fc7b9d843dbc6a17dd560c9db8c5e45fc1656258375f9e06e61266869e6303a685fc9e3d7f9e8ba078244db8198f48259
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/a64581f1d2e8e0c0c83c5d56334a3e3786ed251e1a882d7610d2588d8602eacb32c9167032891e2796c30df3437c9ce52c7284786dca6f1f44250301060169ea
languageName: node
linkType: hard
-"@formatjs/intl-locale@npm:4.1.0":
- version: 4.1.0
- resolution: "@formatjs/intl-locale@npm:4.1.0"
+"@formatjs/intl-locale@npm:4.2.1":
+ version: 4.2.1
+ resolution: "@formatjs/intl-locale@npm:4.2.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-enumerator": "npm:1.7.0"
- "@formatjs/intl-getcanonicallocales": "npm:2.4.0"
- tslib: "npm:^2.7.0"
- checksum: 10/dd598576957ee92399572a3a327a557fd6089763e4d372abdddd23cbce0b49f963d4a4d7e017beaa4488827a1671f15567da057c8d0bf967bc1eefc8d3c7977c
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-enumerator": "npm:1.8.1"
+ "@formatjs/intl-getcanonicallocales": "npm:2.5.1"
+ tslib: "npm:2"
+ checksum: 10/4cba0fbeded2c7c5806528806f176cb833c43765bf1717470f4e001ab42581d5f0b52bf1893afef9597fba96dc3d4659507e490030f231523d460ec6686b9562
languageName: node
linkType: hard
-"@formatjs/intl-localematcher@npm:0.5.5":
- version: 0.5.5
- resolution: "@formatjs/intl-localematcher@npm:0.5.5"
+"@formatjs/intl-localematcher@npm:0.5.6":
+ version: 0.5.6
+ resolution: "@formatjs/intl-localematcher@npm:0.5.6"
dependencies:
- tslib: "npm:^2.7.0"
- checksum: 10/179069eb3a23510e17f118efa3b6a9873f18683977cb813e57ef08cba09bbac73fce13f42c557deb62d3da4f32a8c7ad7d522aae4f1b41625835ce564e59e750
+ tslib: "npm:2"
+ checksum: 10/14eac6bb25dcfeedd7960f44dec5a137999729da00b294ddf1133abe760ced4342f37734bc750b4c47f8dd8d5633a7da38d274503f80d7e965bb1f6fb6f2988c
languageName: node
linkType: hard
-"@formatjs/intl-numberformat@npm:8.13.0":
- version: 8.13.0
- resolution: "@formatjs/intl-numberformat@npm:8.13.0"
+"@formatjs/intl-numberformat@npm:8.14.1":
+ version: 8.14.1
+ resolution: "@formatjs/intl-numberformat@npm:8.14.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/c1c5ebcf559137978332045e56d228b4851a7ab96063a07a9ceef51e4f41c034f549d9c0ab21fa7d11f5b19f97a9ca5114d4367636bf22f7a23fd7e22c5e2412
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/51152d1b9607a35c64e6089e44b90c7ec90be3b1925ba47ffc559ddb4fd72afae76e83af3d436831ea0fc47dc0e9fee9cd3d576280440f2dce03cb6bd24e0bed
languageName: node
linkType: hard
-"@formatjs/intl-pluralrules@npm:5.2.17":
- version: 5.2.17
- resolution: "@formatjs/intl-pluralrules@npm:5.2.17"
+"@formatjs/intl-pluralrules@npm:5.3.1":
+ version: 5.3.1
+ resolution: "@formatjs/intl-pluralrules@npm:5.3.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/fabbff5b30a31e68579fa6005578ebf17cc398b4914459c863f1ed6fa29f81c112365a182b8aa2329fdbbbec5a3c3f5a931dd67f1fc1e8a5b46c55442e59dfb3
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/fc83c3547a9f0af6331c2970f265234fde967848ff738730f2e87ce816636d8778ead1185f5ecccc692cb8b63c11412dc85deac9d3425f44fe3a6a6c30c8b776
languageName: node
linkType: hard
-"@formatjs/intl-relativetimeformat@npm:11.3.0":
- version: 11.3.0
- resolution: "@formatjs/intl-relativetimeformat@npm:11.3.0"
+"@formatjs/intl-relativetimeformat@npm:11.4.1":
+ version: 11.4.1
+ resolution: "@formatjs/intl-relativetimeformat@npm:11.4.1"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/intl-localematcher": "npm:0.5.5"
- tslib: "npm:^2.7.0"
- checksum: 10/675f8a15eab8e073f1bb61e1b13e206472aa92b82979592ae2f31a434631cab049e67e2d957223b1b0a3ea45c0e80143f1bb5b3f9c762ef77fac2082567390f9
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/intl-localematcher": "npm:0.5.6"
+ tslib: "npm:2"
+ checksum: 10/80817403301baed257fbd8c793b9ed077a2e6dd0414a6895b5bfde3619aebc818f30535da9b560a6186fac783cf09561c495d2c6568a980bd635736194655af5
languageName: node
linkType: hard
@@ -8694,13 +8707,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "home-assistant-frontend@workspace:."
dependencies:
- "@babel/core": "npm:7.25.9"
+ "@babel/core": "npm:7.26.0"
"@babel/helper-define-polyfill-provider": "npm:0.6.2"
"@babel/plugin-proposal-decorators": "npm:7.25.9"
"@babel/plugin-transform-runtime": "npm:7.25.9"
- "@babel/preset-env": "npm:7.25.9"
- "@babel/preset-typescript": "npm:7.25.9"
- "@babel/runtime": "npm:7.25.9"
+ "@babel/preset-env": "npm:7.26.0"
+ "@babel/preset-typescript": "npm:7.26.0"
+ "@babel/runtime": "npm:7.26.0"
"@braintree/sanitize-url": "npm:7.1.0"
"@bundle-stats/plugin-webpack-filter": "npm:4.16.0"
"@codemirror/autocomplete": "npm:6.18.1"
@@ -8711,14 +8724,14 @@ __metadata:
"@codemirror/state": "npm:6.4.1"
"@codemirror/view": "npm:6.34.1"
"@egjs/hammerjs": "npm:2.0.17"
- "@formatjs/intl-datetimeformat": "npm:6.15.0"
- "@formatjs/intl-displaynames": "npm:6.7.0"
- "@formatjs/intl-getcanonicallocales": "npm:2.4.0"
- "@formatjs/intl-listformat": "npm:7.6.0"
- "@formatjs/intl-locale": "npm:4.1.0"
- "@formatjs/intl-numberformat": "npm:8.13.0"
- "@formatjs/intl-pluralrules": "npm:5.2.17"
- "@formatjs/intl-relativetimeformat": "npm:11.3.0"
+ "@formatjs/intl-datetimeformat": "npm:6.16.1"
+ "@formatjs/intl-displaynames": "npm:6.8.1"
+ "@formatjs/intl-getcanonicallocales": "npm:2.5.1"
+ "@formatjs/intl-listformat": "npm:7.7.1"
+ "@formatjs/intl-locale": "npm:4.2.1"
+ "@formatjs/intl-numberformat": "npm:8.14.1"
+ "@formatjs/intl-pluralrules": "npm:5.3.1"
+ "@formatjs/intl-relativetimeformat": "npm:11.4.1"
"@fullcalendar/core": "npm:6.1.15"
"@fullcalendar/daygrid": "npm:6.1.15"
"@fullcalendar/interaction": "npm:6.1.15"
@@ -8849,7 +8862,7 @@ __metadata:
husky: "npm:9.1.6"
idb-keyval: "npm:6.2.1"
instant-mocha: "npm:1.5.3"
- intl-messageformat: "npm:10.7.1"
+ intl-messageformat: "npm:10.7.3"
js-yaml: "npm:4.1.0"
jszip: "npm:3.10.1"
leaflet: "npm:1.9.4"
@@ -9299,15 +9312,15 @@ __metadata:
languageName: node
linkType: hard
-"intl-messageformat@npm:10.7.1":
- version: 10.7.1
- resolution: "intl-messageformat@npm:10.7.1"
+"intl-messageformat@npm:10.7.3":
+ version: 10.7.3
+ resolution: "intl-messageformat@npm:10.7.3"
dependencies:
- "@formatjs/ecma402-abstract": "npm:2.2.0"
- "@formatjs/fast-memoize": "npm:2.2.1"
- "@formatjs/icu-messageformat-parser": "npm:2.8.0"
- tslib: "npm:^2.7.0"
- checksum: 10/e8bf10555a2352043552aca9a437a5890602e000f9895cec1e0736edda89e2e711b2dfa1672f1b92907da3550b9dcfe36842e19c9a0abffaef449caf99219313
+ "@formatjs/ecma402-abstract": "npm:2.2.1"
+ "@formatjs/fast-memoize": "npm:2.2.2"
+ "@formatjs/icu-messageformat-parser": "npm:2.9.1"
+ tslib: "npm:2"
+ checksum: 10/e387f7f37a295d9d386af0c6392ba135a4580e86177161f1f400d470fed1f8c7b3cb6c724cbc2f50a7ded2e20f202977d8bf5e2bbc626f72016a5b5b6752b76d
languageName: node
linkType: hard
@@ -13773,7 +13786,7 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.2, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.7.0":
+"tslib@npm:2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.2, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0":
version: 2.8.0
resolution: "tslib@npm:2.8.0"
checksum: 10/1bc7c43937477059b4d26f2dbde7e49ef0fb4f38f3014e0603eaea76d6a885742c8b1762af45949145e5e7408a736d20ded949da99dabc8ccba1fc5531d2d927