mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-17 21:29:35 +00:00
Compare commits
50 Commits
20250531.1
...
move-defau
Author | SHA1 | Date | |
---|---|---|---|
![]() |
365db51526 | ||
![]() |
8eb7fe8b0a | ||
![]() |
c8c2966d34 | ||
![]() |
a8768a5d9d | ||
![]() |
02bb7086e7 | ||
![]() |
42d8b2ae19 | ||
![]() |
e08f4a6bba | ||
![]() |
2e6c35d977 | ||
![]() |
17305a818b | ||
![]() |
08389dad04 | ||
![]() |
ab6ace46b5 | ||
![]() |
535dedbbc4 | ||
![]() |
412eb0c647 | ||
![]() |
87c8ebd493 | ||
![]() |
6e49f89126 | ||
![]() |
a099e65a9d | ||
![]() |
11e4a9f056 | ||
![]() |
b617299eee | ||
![]() |
768f27b1b9 | ||
![]() |
5ed816df6d | ||
![]() |
6692ac7517 | ||
![]() |
65499db0cb | ||
![]() |
11a1eabf61 | ||
![]() |
b30fa122ba | ||
![]() |
6730d08b85 | ||
![]() |
67003d6fd1 | ||
![]() |
414d46be65 | ||
![]() |
1485d1a1de | ||
![]() |
fd13e41524 | ||
![]() |
77f7ca0368 | ||
![]() |
7471250a07 | ||
![]() |
8b0a63d791 | ||
![]() |
57ffa814ed | ||
![]() |
16e20456e2 | ||
![]() |
9c0ce41ebb | ||
![]() |
b458a1d7c6 | ||
![]() |
0eaeeb1141 | ||
![]() |
b7e63e697f | ||
![]() |
06db0f4b98 | ||
![]() |
d33636c6fb | ||
![]() |
bbb546159c | ||
![]() |
e8fc36026a | ||
![]() |
38f8c804af | ||
![]() |
7c5bf26240 | ||
![]() |
189067d14b | ||
![]() |
e79e0f77b8 | ||
![]() |
b226e5c697 | ||
![]() |
52ad31601c | ||
![]() |
cba3e4df7f | ||
![]() |
3532cfa974 |
@@ -1 +1 @@
|
||||
yarn run lint-staged --relative --shell "/bin/bash"
|
||||
yarn run lint-staged --relative
|
||||
|
32
package.json
32
package.json
@@ -26,7 +26,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.27.1",
|
||||
"@babel/runtime": "7.27.4",
|
||||
"@braintree/sanitize-url": "7.1.1",
|
||||
"@codemirror/autocomplete": "6.18.6",
|
||||
"@codemirror/commands": "6.8.1",
|
||||
@@ -34,7 +34,7 @@
|
||||
"@codemirror/legacy-modes": "6.5.1",
|
||||
"@codemirror/search": "6.5.11",
|
||||
"@codemirror/state": "6.5.2",
|
||||
"@codemirror/view": "6.36.8",
|
||||
"@codemirror/view": "6.37.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "6.18.0",
|
||||
"@formatjs/intl-displaynames": "6.8.11",
|
||||
@@ -111,7 +111,7 @@
|
||||
"fuse.js": "7.1.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"gulp-zopfli-green": "6.0.2",
|
||||
"hls.js": "1.6.2",
|
||||
"hls.js": "1.6.4",
|
||||
"home-assistant-js-websocket": "9.5.0",
|
||||
"idb-keyval": "6.2.2",
|
||||
"intl-messageformat": "10.7.16",
|
||||
@@ -149,20 +149,20 @@
|
||||
"xss": "1.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.27.1",
|
||||
"@babel/core": "7.27.4",
|
||||
"@babel/helper-define-polyfill-provider": "0.6.4",
|
||||
"@babel/plugin-transform-runtime": "7.27.1",
|
||||
"@babel/plugin-transform-runtime": "7.27.4",
|
||||
"@babel/preset-env": "7.27.2",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.20.1",
|
||||
"@lokalise/node-api": "14.7.0",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.20.2",
|
||||
"@lokalise/node-api": "14.8.0",
|
||||
"@octokit/auth-oauth-device": "8.0.1",
|
||||
"@octokit/plugin-retry": "8.0.1",
|
||||
"@octokit/rest": "21.1.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@rsdoctor/rspack-plugin": "1.1.2",
|
||||
"@rspack/cli": "1.3.11",
|
||||
"@rspack/core": "1.3.11",
|
||||
"@rspack/cli": "1.3.12",
|
||||
"@rspack/core": "1.3.12",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.21",
|
||||
"@types/chromecast-caf-receiver": "6.0.22",
|
||||
"@types/chromecast-caf-sender": "1.0.11",
|
||||
"@types/color-name": "2.0.0",
|
||||
"@types/glob": "8.1.0",
|
||||
@@ -184,7 +184,7 @@
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.3",
|
||||
"del": "8.0.0",
|
||||
"eslint": "9.27.0",
|
||||
"eslint": "9.28.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "10.1.5",
|
||||
"eslint-import-resolver-webpack": "0.13.10",
|
||||
@@ -196,7 +196,7 @@
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.3.0",
|
||||
"glob": "11.0.2",
|
||||
"gulp": "5.0.0",
|
||||
"gulp": "5.0.1",
|
||||
"gulp-brotli": "3.0.0",
|
||||
"gulp-json-transform": "0.5.0",
|
||||
"gulp-rename": "2.0.0",
|
||||
@@ -204,7 +204,7 @@
|
||||
"husky": "9.1.7",
|
||||
"jsdom": "26.1.0",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "15.5.2",
|
||||
"lint-staged": "16.1.0",
|
||||
"lit-analyzer": "2.0.3",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.template": "4.5.0",
|
||||
@@ -218,7 +218,7 @@
|
||||
"terser-webpack-plugin": "5.3.14",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.32.1",
|
||||
"typescript-eslint": "8.33.0",
|
||||
"vite-tsconfig-paths": "5.1.4",
|
||||
"vitest": "3.1.4",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
@@ -232,7 +232,7 @@
|
||||
"clean-css": "5.3.3",
|
||||
"@lit/reactive-element": "2.1.0",
|
||||
"@fullcalendar/daygrid": "6.1.17",
|
||||
"globals": "16.1.0",
|
||||
"globals": "16.2.0",
|
||||
"tslib": "2.8.1",
|
||||
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch"
|
||||
},
|
||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20250531.1"
|
||||
version = "20250430.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
@@ -229,20 +229,14 @@ export class StateHistoryChartLine extends LitElement {
|
||||
minYAxis = ({ min }) => Math.min(min, this.minYAxis!);
|
||||
}
|
||||
} else if (this.logarithmicScale) {
|
||||
minYAxis = ({ min }) => {
|
||||
const value = min > 0 ? min * 0.95 : min * 1.05;
|
||||
return Math.abs(value) < 1 ? value : Math.floor(value);
|
||||
};
|
||||
minYAxis = ({ min }) => Math.floor(min > 0 ? min * 0.95 : min * 1.05);
|
||||
}
|
||||
if (typeof maxYAxis === "number") {
|
||||
if (this.fitYData) {
|
||||
maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!);
|
||||
}
|
||||
} else if (this.logarithmicScale) {
|
||||
maxYAxis = ({ max }) => {
|
||||
const value = max > 0 ? max * 1.05 : max * 0.95;
|
||||
return Math.abs(value) < 1 ? value : Math.ceil(value);
|
||||
};
|
||||
maxYAxis = ({ max }) => Math.ceil(max > 0 ? max * 1.05 : max * 0.95);
|
||||
}
|
||||
this._chartOptions = {
|
||||
xAxis: {
|
||||
@@ -759,10 +753,10 @@ export class StateHistoryChartLine extends LitElement {
|
||||
if (this.logarithmicScale) {
|
||||
// log(0) is -Infinity, so we need to set a minimum value
|
||||
if (typeof value === "number") {
|
||||
return Math.max(value, Number.EPSILON);
|
||||
return Math.max(value, 0.1);
|
||||
}
|
||||
if (typeof value === "function") {
|
||||
return (values: any) => Math.max(value(values), Number.EPSILON);
|
||||
return (values: any) => Math.max(value(values), 0.1);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
@@ -241,20 +241,14 @@ export class StatisticsChart extends LitElement {
|
||||
minYAxis = ({ min }) => Math.min(min, this.minYAxis!);
|
||||
}
|
||||
} else if (this.logarithmicScale) {
|
||||
minYAxis = ({ min }) => {
|
||||
const value = min > 0 ? min * 0.95 : min * 1.05;
|
||||
return Math.abs(value) < 1 ? value : Math.floor(value);
|
||||
};
|
||||
minYAxis = ({ min }) => Math.floor(min > 0 ? min * 0.95 : min * 1.05);
|
||||
}
|
||||
if (typeof maxYAxis === "number") {
|
||||
if (this.fitYData) {
|
||||
maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!);
|
||||
}
|
||||
} else if (this.logarithmicScale) {
|
||||
maxYAxis = ({ max }) => {
|
||||
const value = max > 0 ? max * 1.05 : max * 0.95;
|
||||
return Math.abs(value) < 1 ? value : Math.ceil(value);
|
||||
};
|
||||
maxYAxis = ({ max }) => Math.ceil(max > 0 ? max * 1.05 : max * 0.95);
|
||||
}
|
||||
const endTime = this.endTime ?? new Date();
|
||||
let startTime = this.startTime;
|
||||
@@ -625,10 +619,10 @@ export class StatisticsChart extends LitElement {
|
||||
if (this.logarithmicScale) {
|
||||
// log(0) is -Infinity, so we need to set a minimum value
|
||||
if (typeof value === "number") {
|
||||
return Math.max(value, Number.EPSILON);
|
||||
return Math.max(value, 0.1);
|
||||
}
|
||||
if (typeof value === "function") {
|
||||
return (values: any) => Math.max(value(values), Number.EPSILON);
|
||||
return (values: any) => Math.max(value(values), 0.1);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
@@ -345,10 +345,8 @@ export class HaComboBox extends LitElement {
|
||||
// @ts-ignore
|
||||
this._comboBox._closeOnBlurIsPrevented = true;
|
||||
}
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
if (newValue !== this.value) {
|
||||
fireEvent(this, "value-changed", { value: newValue || undefined });
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ const createPanelNavigationItem = (hass: HomeAssistant, panel: PanelInfo) => ({
|
||||
path: `/${panel.url_path}`,
|
||||
icon: panel.icon ?? "mdi:view-dashboard",
|
||||
title:
|
||||
panel.url_path === hass.defaultPanel
|
||||
panel.url_path === hass.sidebar.defaultPanel
|
||||
? hass.localize("panel.states")
|
||||
: hass.localize(`panel.${panel.title}`) ||
|
||||
panel.title ||
|
||||
|
@@ -50,6 +50,7 @@ import type { HaMdListItem } from "./ha-md-list-item";
|
||||
import "./ha-spinner";
|
||||
import "./ha-svg-icon";
|
||||
import "./user/ha-user-badge";
|
||||
import { DEFAULT_PANEL } from "../data/panel";
|
||||
|
||||
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
||||
|
||||
@@ -140,9 +141,9 @@ const defaultPanelSorter = (
|
||||
export const computePanels = memoizeOne(
|
||||
(
|
||||
panels: HomeAssistant["panels"],
|
||||
defaultPanel: HomeAssistant["defaultPanel"],
|
||||
panelsOrder: string[],
|
||||
hiddenPanels: string[],
|
||||
defaultPanel: HomeAssistant["sidebar"]["defaultPanel"],
|
||||
panelsOrder: HomeAssistant["sidebar"]["panelOrder"],
|
||||
hiddenPanels: HomeAssistant["sidebar"]["hiddenPanels"],
|
||||
locale: HomeAssistant["locale"]
|
||||
): [PanelInfo[], PanelInfo[]] => {
|
||||
if (!panels) {
|
||||
@@ -195,10 +196,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() private _issuesCount = 0;
|
||||
|
||||
@state() private _panelOrder?: string[];
|
||||
|
||||
@state() private _hiddenPanels?: string[];
|
||||
|
||||
private _mouseLeaveTimeout?: number;
|
||||
|
||||
private _tooltipHideTimeout?: number;
|
||||
@@ -213,18 +210,32 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
this.hass.connection,
|
||||
"sidebar",
|
||||
({ value }) => {
|
||||
this._panelOrder = value?.panelOrder;
|
||||
this._hiddenPanels = value?.hiddenPanels;
|
||||
let panelOrder = value?.panelOrder;
|
||||
let hiddenPanels = value?.hiddenPanels;
|
||||
let defaultPanel = value?.defaultPanel;
|
||||
|
||||
// fallback to old localStorage values
|
||||
if (!this._panelOrder) {
|
||||
if (!panelOrder) {
|
||||
const storedOrder = localStorage.getItem("sidebarPanelOrder");
|
||||
this._panelOrder = storedOrder ? JSON.parse(storedOrder) : [];
|
||||
panelOrder = storedOrder ? JSON.parse(storedOrder) : [];
|
||||
}
|
||||
if (!this._hiddenPanels) {
|
||||
if (!hiddenPanels) {
|
||||
const storedHidden = localStorage.getItem("sidebarHiddenPanels");
|
||||
this._hiddenPanels = storedHidden ? JSON.parse(storedHidden) : [];
|
||||
hiddenPanels = storedHidden ? JSON.parse(storedHidden) : [];
|
||||
}
|
||||
if (!defaultPanel) {
|
||||
const storedDefault = localStorage.getItem("defaultPanel");
|
||||
defaultPanel = storedDefault
|
||||
? JSON.parse(storedDefault)
|
||||
: DEFAULT_PANEL;
|
||||
}
|
||||
|
||||
fireEvent(this, "hass-set-sidebar-data", {
|
||||
...value,
|
||||
defaultPanel: defaultPanel as string,
|
||||
panelOrder: panelOrder as string[],
|
||||
hiddenPanels: hiddenPanels as string[],
|
||||
});
|
||||
}
|
||||
),
|
||||
subscribeNotifications(this.hass.connection, (notifications) => {
|
||||
@@ -275,8 +286,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
changedProps.has("_updatesCount") ||
|
||||
changedProps.has("_issuesCount") ||
|
||||
changedProps.has("_notifications") ||
|
||||
changedProps.has("_hiddenPanels") ||
|
||||
changedProps.has("_panelOrder")
|
||||
(changedProps.has("hass") &&
|
||||
changedProps.get("hass")?.sidebar !== this.hass.sidebar)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -295,7 +306,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
hass.localize !== oldHass.localize ||
|
||||
hass.locale !== oldHass.locale ||
|
||||
hass.states !== oldHass.states ||
|
||||
hass.defaultPanel !== oldHass.defaultPanel ||
|
||||
hass.sidebar !== oldHass.sidebar ||
|
||||
hass.connected !== oldHass.connected
|
||||
);
|
||||
}
|
||||
@@ -365,7 +376,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _renderAllPanels(selectedPanel: string) {
|
||||
if (!this._panelOrder || !this._hiddenPanels) {
|
||||
if (!this.hass.sidebar.panelOrder || !this.hass.sidebar.hiddenPanels) {
|
||||
return html`
|
||||
<ha-fade-in .delay=${500}
|
||||
><ha-spinner size="small"></ha-spinner
|
||||
@@ -375,9 +386,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
|
||||
const [beforeSpacer, afterSpacer] = computePanels(
|
||||
this.hass.panels,
|
||||
this.hass.defaultPanel,
|
||||
this._panelOrder,
|
||||
this._hiddenPanels,
|
||||
this.hass.sidebar.defaultPanel,
|
||||
this.hass.sidebar.panelOrder,
|
||||
this.hass.sidebar.hiddenPanels,
|
||||
this.hass.locale
|
||||
);
|
||||
|
||||
@@ -402,11 +413,11 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
return panels.map((panel) =>
|
||||
this._renderPanel(
|
||||
panel.url_path,
|
||||
panel.url_path === this.hass.defaultPanel
|
||||
panel.url_path === this.hass.sidebar.defaultPanel
|
||||
? panel.title || this.hass.localize("panel.states")
|
||||
: this.hass.localize(`panel.${panel.title}`) || panel.title,
|
||||
panel.icon,
|
||||
panel.url_path === this.hass.defaultPanel && !panel.icon
|
||||
panel.url_path === this.hass.sidebar.defaultPanel && !panel.icon
|
||||
? PANEL_ICONS.lovelace
|
||||
: panel.url_path in PANEL_ICONS
|
||||
? PANEL_ICONS[panel.url_path]
|
||||
|
@@ -12,6 +12,8 @@ class HaEntityMarker extends LitElement {
|
||||
|
||||
@property({ attribute: "entity-name" }) public entityName?: string;
|
||||
|
||||
@property({ attribute: "entity-unit" }) public entityUnit?: string;
|
||||
|
||||
@property({ attribute: "entity-picture" }) public entityPicture?: string;
|
||||
|
||||
@property({ attribute: "entity-color" }) public entityColor?: string;
|
||||
@@ -37,7 +39,16 @@ class HaEntityMarker extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.hass?.states[this.entityId]}
|
||||
></ha-state-icon>`
|
||||
: this.entityName}
|
||||
: !this.entityUnit
|
||||
? this.entityName
|
||||
: html`
|
||||
${this.entityName}
|
||||
<span
|
||||
class="unit"
|
||||
style="display: ${this.entityUnit ? "initial" : "none"}"
|
||||
>${this.entityUnit}</span
|
||||
>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -72,6 +83,9 @@ class HaEntityMarker extends LitElement {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.unit {
|
||||
margin-left: 2px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -56,6 +56,7 @@ export interface HaMapEntity {
|
||||
color: string;
|
||||
label_mode?: "name" | "state" | "attribute" | "icon";
|
||||
attribute?: string;
|
||||
unit?: string;
|
||||
name?: string;
|
||||
focus?: boolean;
|
||||
}
|
||||
@@ -549,6 +550,12 @@ export class HaMap extends ReactiveElement {
|
||||
typeof entity !== "string" && entity.label_mode === "icon";
|
||||
entityMarker.entityId = getEntityId(entity);
|
||||
entityMarker.entityName = entityName;
|
||||
entityMarker.entityUnit =
|
||||
typeof entity !== "string" &&
|
||||
entity.unit &&
|
||||
entity.label_mode === "attribute"
|
||||
? entity.unit
|
||||
: "";
|
||||
entityMarker.entityPicture =
|
||||
entityPicture && (typeof entity === "string" || !entity.label_mode)
|
||||
? this.hass.hassUrl(entityPicture)
|
||||
|
@@ -679,9 +679,7 @@ export const getEnergyDataCollection = (
|
||||
const period =
|
||||
preferredPeriod === "today" && hour === "0" ? "yesterday" : preferredPeriod;
|
||||
|
||||
const [start, end] = calcDateRange(hass, period);
|
||||
collection.start = calcDate(start, startOfDay, hass.locale, hass.config);
|
||||
collection.end = calcDate(end, endOfDay, hass.locale, hass.config);
|
||||
[collection.start, collection.end] = calcDateRange(hass, period);
|
||||
|
||||
const scheduleUpdatePeriod = () => {
|
||||
collection._updatePeriodTimeout = window.setTimeout(
|
||||
|
@@ -8,6 +8,7 @@ export interface CoreFrontendUserData {
|
||||
export interface SidebarFrontendUserData {
|
||||
panelOrder: string[];
|
||||
hiddenPanels: string[];
|
||||
defaultPanel?: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { HomeAssistant, PanelInfo } from "../types";
|
||||
|
||||
/** Panel to show when no panel is picked. */
|
||||
@@ -10,16 +9,9 @@ export const getStorageDefaultPanelUrlPath = (): string => {
|
||||
return defaultPanel ? JSON.parse(defaultPanel) : DEFAULT_PANEL;
|
||||
};
|
||||
|
||||
export const setDefaultPanel = (
|
||||
element: HTMLElement,
|
||||
urlPath: string
|
||||
): void => {
|
||||
fireEvent(element, "hass-default-panel", { defaultPanel: urlPath });
|
||||
};
|
||||
|
||||
export const getDefaultPanel = (hass: HomeAssistant): PanelInfo =>
|
||||
hass.panels[hass.defaultPanel]
|
||||
? hass.panels[hass.defaultPanel]
|
||||
hass.panels[hass.sidebar.defaultPanel]
|
||||
? hass.panels[hass.sidebar.defaultPanel]
|
||||
: hass.panels[DEFAULT_PANEL];
|
||||
|
||||
export const getPanelNameTranslationKey = (panel: PanelInfo) => {
|
||||
|
@@ -6,6 +6,7 @@ const HAS_CUSTOM_PREVIEW = ["generic_camera", "template"];
|
||||
export interface GenericPreview {
|
||||
state: string;
|
||||
attributes: Record<string, any>;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const subscribePreviewGeneric = (
|
||||
|
@@ -58,6 +58,11 @@ export class FlowPreviewGeneric extends LitElement {
|
||||
}
|
||||
|
||||
private _setPreview = (preview: GenericPreview) => {
|
||||
if (preview.error) {
|
||||
this._error = preview.error;
|
||||
this._preview = undefined;
|
||||
return;
|
||||
}
|
||||
const now = new Date().toISOString();
|
||||
this._preview = {
|
||||
entity_id: `${this.stepId}.___flow_preview___`,
|
||||
@@ -80,6 +85,7 @@ export class FlowPreviewGeneric extends LitElement {
|
||||
if (this.flowType !== "config_flow" && this.flowType !== "options_flow") {
|
||||
return;
|
||||
}
|
||||
this._error = undefined;
|
||||
try {
|
||||
this._unsub = subscribePreviewGeneric(
|
||||
this.hass,
|
||||
@@ -89,6 +95,7 @@ export class FlowPreviewGeneric extends LitElement {
|
||||
this.stepData,
|
||||
this._setPreview
|
||||
);
|
||||
await this._unsub;
|
||||
fireEvent(this, "set-flow-errors", { errors: {} });
|
||||
} catch (err: any) {
|
||||
if (typeof err.message === "string") {
|
||||
|
@@ -235,9 +235,13 @@ class StepFlowCreateEntry extends LitElement {
|
||||
|
||||
fireEvent(this, "flow-update", { step: undefined });
|
||||
if (this.step.result && this.navigateToResult) {
|
||||
navigate(
|
||||
`/config/integrations/integration/${this.step.result.domain}#config_entry=${this.step.result.entry_id}`
|
||||
);
|
||||
if (this.devices.length === 1) {
|
||||
navigate(`/config/devices/device/${this.devices[0].id}`);
|
||||
} else {
|
||||
navigate(
|
||||
`/config/integrations/integration/${this.step.result.domain}#config_entry=${this.step.result.entry_id}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -96,7 +96,7 @@ class DialogEditSidebar extends LitElement {
|
||||
|
||||
const [beforeSpacer, afterSpacer] = computePanels(
|
||||
this.hass.panels,
|
||||
this.hass.defaultPanel,
|
||||
this.hass.sidebar.defaultPanel,
|
||||
this._order,
|
||||
this._hidden,
|
||||
this.hass.locale
|
||||
@@ -109,12 +109,12 @@ class DialogEditSidebar extends LitElement {
|
||||
].map((panel) => ({
|
||||
value: panel.url_path,
|
||||
label:
|
||||
panel.url_path === this.hass.defaultPanel
|
||||
panel.url_path === this.hass.sidebar.defaultPanel
|
||||
? panel.title || this.hass.localize("panel.states")
|
||||
: this.hass.localize(`panel.${panel.title}`) || panel.title || "?",
|
||||
icon: panel.icon || undefined,
|
||||
iconPath:
|
||||
panel.url_path === this.hass.defaultPanel && !panel.icon
|
||||
panel.url_path === this.hass.sidebar.defaultPanel && !panel.icon
|
||||
? PANEL_ICONS.lovelace
|
||||
: panel.url_path in PANEL_ICONS
|
||||
? PANEL_ICONS[panel.url_path]
|
||||
@@ -195,6 +195,7 @@ class DialogEditSidebar extends LitElement {
|
||||
await saveFrontendUserData(this.hass.connection, "sidebar", {
|
||||
panelOrder: this._order!,
|
||||
hiddenPanels: this._hidden!,
|
||||
defaultPanel: this.hass.sidebar.defaultPanel,
|
||||
});
|
||||
} catch (err: any) {
|
||||
this._error = err.message || err;
|
||||
|
@@ -4,6 +4,7 @@ import {
|
||||
mdiDeleteForever,
|
||||
mdiHospitalBox,
|
||||
mdiInformation,
|
||||
mdiPlus,
|
||||
mdiUpload,
|
||||
mdiWrench,
|
||||
} from "@mdi/js";
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
fetchZwaveIntegrationSettings,
|
||||
fetchZwaveIsAnyOTAFirmwareUpdateInProgress,
|
||||
fetchZwaveIsNodeFirmwareUpdateInProgress,
|
||||
fetchZwaveNetworkStatus,
|
||||
fetchZwaveNodeStatus,
|
||||
} from "../../../../../../data/zwave_js";
|
||||
import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box";
|
||||
@@ -24,6 +26,7 @@ import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/inte
|
||||
import { showZWaveJSUpdateFirmwareNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node";
|
||||
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||
import { showZWaveJSHardResetControllerDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-hard-reset-controller";
|
||||
import { showZWaveJSAddNodeDialog } from "../../../../integrations/integration-panels/zwave_js/add-node/show-dialog-zwave_js-add-node";
|
||||
|
||||
export const getZwaveDeviceActions = async (
|
||||
el: HTMLElement,
|
||||
@@ -160,6 +163,19 @@ export const getZwaveDeviceActions = async (
|
||||
}
|
||||
|
||||
if (nodeStatus.is_controller_node) {
|
||||
const networkStatus = await fetchZwaveNetworkStatus(hass, {
|
||||
entry_id: entryId,
|
||||
});
|
||||
actions.unshift({
|
||||
label: hass.localize("ui.panel.config.zwave_js.common.add_node"),
|
||||
icon: mdiPlus,
|
||||
action: async () => {
|
||||
showZWaveJSAddNodeDialog(el, {
|
||||
entry_id: entryId,
|
||||
longRangeSupported: networkStatus.controller?.supports_long_range,
|
||||
});
|
||||
},
|
||||
});
|
||||
actions.push({
|
||||
label: hass.localize(
|
||||
"ui.panel.config.zwave_js.device_info.hard_reset_controller"
|
||||
|
@@ -13,10 +13,11 @@ import type {
|
||||
LovelaceDashboardCreateParams,
|
||||
LovelaceDashboardMutableParams,
|
||||
} from "../../../../data/lovelace/dashboard";
|
||||
import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel";
|
||||
import { DEFAULT_PANEL } from "../../../../data/panel";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail";
|
||||
import { saveFrontendUserData } from "../../../../data/frontend";
|
||||
|
||||
@customElement("dialog-lovelace-dashboard-detail")
|
||||
export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
@@ -59,7 +60,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
if (!this._params || !this._data) {
|
||||
return nothing;
|
||||
}
|
||||
const defaultPanelUrlPath = this.hass.defaultPanel;
|
||||
const defaultPanelUrlPath = this.hass.sidebar.defaultPanel;
|
||||
const titleInvalid = !this._data.title || !this._data.title.trim();
|
||||
|
||||
return html`
|
||||
@@ -249,15 +250,17 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
private _toggleDefault() {
|
||||
private async _toggleDefault() {
|
||||
const urlPath = this._params?.urlPath;
|
||||
if (!urlPath) {
|
||||
return;
|
||||
}
|
||||
setDefaultPanel(
|
||||
this,
|
||||
urlPath === this.hass.defaultPanel ? DEFAULT_PANEL : urlPath
|
||||
);
|
||||
await saveFrontendUserData(this.hass!.connection, "sidebar", {
|
||||
panelOrder: this.hass!.sidebar.panelOrder,
|
||||
hiddenPanels: this.hass!.sidebar.hiddenPanels,
|
||||
defaultPanel:
|
||||
urlPath === this.hass.sidebar.defaultPanel ? DEFAULT_PANEL : urlPath,
|
||||
});
|
||||
}
|
||||
|
||||
private async _updateDashboard() {
|
||||
|
@@ -255,7 +255,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
const defaultMode = (
|
||||
this.hass.panels?.lovelace?.config as LovelacePanelConfig
|
||||
).mode;
|
||||
const defaultUrlPath = this.hass.defaultPanel;
|
||||
const defaultUrlPath = this.hass.sidebar.defaultPanel;
|
||||
const isDefault = defaultUrlPath === "lovelace";
|
||||
const result: DataTableItem[] = [
|
||||
{
|
||||
|
@@ -10,8 +10,6 @@ import {
|
||||
addYears,
|
||||
addMonths,
|
||||
addHours,
|
||||
startOfDay,
|
||||
addDays,
|
||||
} from "date-fns";
|
||||
import type {
|
||||
BarSeriesOption,
|
||||
@@ -284,10 +282,6 @@ export function getCompareTransform(start: Date, compareStart?: Date) {
|
||||
) {
|
||||
return (ts: Date) => addMonths(ts, compareMonthDiff);
|
||||
}
|
||||
const compareDayDiff = differenceInDays(start, compareStart);
|
||||
if (compareDayDiff !== 0 && start.getTime() === startOfDay(start).getTime()) {
|
||||
return (ts: Date) => addDays(ts, compareDayDiff);
|
||||
}
|
||||
const compareOffset = start.getTime() - compareStart.getTime();
|
||||
return (ts: Date) => addMilliseconds(ts, compareOffset);
|
||||
}
|
||||
|
@@ -418,11 +418,12 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
|
||||
.keypad {
|
||||
--keypad-columns: 3;
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--keypad-columns), auto);
|
||||
grid-auto-rows: auto;
|
||||
grid-gap: 16px;
|
||||
grid-gap: 24px;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
@@ -384,15 +384,22 @@ export class HuiAreaCard
|
||||
areaSensorEntityId = area.humidity_entity_id;
|
||||
break;
|
||||
}
|
||||
const areaEntity = areaSensorEntityId
|
||||
? this.hass.states[areaSensorEntityId]
|
||||
: undefined;
|
||||
const areaEntity =
|
||||
areaSensorEntityId &&
|
||||
this.hass.states[areaSensorEntityId] &&
|
||||
!isUnavailableState(this.hass.states[areaSensorEntityId].state)
|
||||
? this.hass.states[areaSensorEntityId]
|
||||
: undefined;
|
||||
if (
|
||||
areaEntity ||
|
||||
entitiesByDomain[domain].some(
|
||||
(entity) => entity.attributes.device_class === deviceClass
|
||||
)
|
||||
) {
|
||||
let value = areaEntity
|
||||
? this.hass.formatEntityState(areaEntity)
|
||||
: this._average(domain, deviceClass);
|
||||
if (!value) value = "—";
|
||||
sensors.push(html`
|
||||
<div class="sensor">
|
||||
<ha-domain-icon
|
||||
@@ -400,9 +407,7 @@ export class HuiAreaCard
|
||||
.domain=${domain}
|
||||
.deviceClass=${deviceClass}
|
||||
></ha-domain-icon>
|
||||
${areaEntity
|
||||
? this.hass.formatEntityState(areaEntity)
|
||||
: this._average(domain, deviceClass)}
|
||||
${value}
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
@@ -12,8 +12,7 @@ import {
|
||||
attachConditionMediaQueriesListeners,
|
||||
checkConditionsMet,
|
||||
} from "../common/validate-condition";
|
||||
import { tryCreateCardElement } from "../create-element/create-card-element";
|
||||
import { createErrorCardElement } from "../create-element/create-element-base";
|
||||
import { createCardElement } from "../create-element/create-card-element";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||
|
||||
declare global {
|
||||
@@ -72,23 +71,10 @@ export class HuiCard extends ReactiveElement {
|
||||
public getGridOptions(): LovelaceGridOptions {
|
||||
const elementOptions = this.getElementGridOptions();
|
||||
const configOptions = this.getConfigGridOptions();
|
||||
const mergedConfig = {
|
||||
return {
|
||||
...elementOptions,
|
||||
...configOptions,
|
||||
};
|
||||
|
||||
// If the element has fixed rows or columns, we use the values from the element
|
||||
if (elementOptions.fixed_rows) {
|
||||
mergedConfig.rows = elementOptions.rows;
|
||||
delete mergedConfig.min_rows;
|
||||
delete mergedConfig.max_rows;
|
||||
}
|
||||
if (elementOptions.fixed_columns) {
|
||||
mergedConfig.columns = elementOptions.columns;
|
||||
delete mergedConfig.min_columns;
|
||||
delete mergedConfig.max_columns;
|
||||
}
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
// options provided by the element
|
||||
@@ -133,15 +119,7 @@ export class HuiCard extends ReactiveElement {
|
||||
}
|
||||
|
||||
private _loadElement(config: LovelaceCardConfig) {
|
||||
try {
|
||||
this._element = tryCreateCardElement(config);
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : undefined;
|
||||
this._element = createErrorCardElement({
|
||||
type: "error",
|
||||
message: errorMessage,
|
||||
});
|
||||
}
|
||||
this._element = createCardElement(config);
|
||||
this._elementConfig = config;
|
||||
if (this.hass) {
|
||||
this._element.hass = this.hass;
|
||||
@@ -222,7 +200,6 @@ export class HuiCard extends ReactiveElement {
|
||||
this._element.preview = this.preview;
|
||||
// For backwards compatibility
|
||||
(this._element as any).editMode = this.preview;
|
||||
fireEvent(this, "card-updated");
|
||||
} catch (e: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(this.config?.type, e);
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||
import type { ErrorCardConfig } from "./types";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-svg-icon";
|
||||
|
||||
const ERROR_ICONS = {
|
||||
warning: mdiAlertOutline,
|
||||
@@ -30,10 +30,9 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
public getGridOptions(): LovelaceGridOptions {
|
||||
return {
|
||||
columns: 6,
|
||||
rows: this.preview ? "auto" : 1,
|
||||
rows: 1,
|
||||
min_rows: 1,
|
||||
min_columns: 6,
|
||||
fixed_rows: this.preview,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,24 +45,17 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
const error =
|
||||
this._config?.error ||
|
||||
this.hass?.localize("ui.errors.config.configuration_error");
|
||||
const showTitle =
|
||||
this.hass === undefined || this.hass?.user?.is_admin || this.preview;
|
||||
const showMessage = this.preview;
|
||||
const showTitle = this.hass === undefined || this.hass?.user?.is_admin;
|
||||
|
||||
return html`
|
||||
<ha-card class="${this.severity} ${showTitle ? "" : "no-title"}">
|
||||
<div class="header">
|
||||
<div class="icon">
|
||||
<slot name="icon">
|
||||
<ha-svg-icon .path=${ERROR_ICONS[this.severity]}></ha-svg-icon>
|
||||
</slot>
|
||||
</div>
|
||||
${showTitle
|
||||
? html`<div class="title"><slot>${error}</slot></div>`
|
||||
: nothing}
|
||||
<div class="icon">
|
||||
<slot name="icon">
|
||||
<ha-svg-icon .path=${ERROR_ICONS[this.severity]}></ha-svg-icon>
|
||||
</slot>
|
||||
</div>
|
||||
${showMessage && this._config?.message
|
||||
? html`<div class="message">${this._config.message}</div>`
|
||||
${showTitle
|
||||
? html`<div class="title"><slot>${error}</slot></div>`
|
||||
: nothing}
|
||||
</ha-card>
|
||||
`;
|
||||
@@ -73,6 +65,10 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
ha-card {
|
||||
height: 100%;
|
||||
border-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
ha-card::after {
|
||||
position: absolute;
|
||||
@@ -85,15 +81,6 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
content: "";
|
||||
border-radius: var(--ha-card-border-radius, 12px);
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
.message {
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
.no-title {
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -103,13 +90,13 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
text-overflow: ellipsis;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
ha-card.warning .icon {
|
||||
ha-card.warning > .icon {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
ha-card.warning::after {
|
||||
background-color: var(--warning-color);
|
||||
}
|
||||
ha-card.error .icon {
|
||||
ha-card.error > .icon {
|
||||
color: var(--error-color);
|
||||
}
|
||||
ha-card.error::after {
|
||||
|
@@ -45,6 +45,7 @@ export const DEFAULT_ZOOM = 14;
|
||||
interface MapEntityConfig extends EntityConfig {
|
||||
label_mode?: "state" | "attribute" | "name";
|
||||
attribute?: string;
|
||||
unit?: string;
|
||||
focus?: boolean;
|
||||
}
|
||||
|
||||
@@ -52,6 +53,7 @@ interface GeoEntity {
|
||||
entity_id: string;
|
||||
label_mode?: "state" | "attribute" | "name" | "icon";
|
||||
attribute?: string;
|
||||
unit?: string;
|
||||
focus: boolean;
|
||||
}
|
||||
|
||||
@@ -430,6 +432,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
entity_id: stateObj.entity_id,
|
||||
label_mode: sourceObj?.label_mode ?? allSource?.label_mode,
|
||||
attribute: sourceObj?.attribute ?? allSource?.attribute,
|
||||
unit: sourceObj?.unit ?? allSource?.unit,
|
||||
focus: sourceObj
|
||||
? (sourceObj.focus ?? true)
|
||||
: (allSource?.focus ?? true),
|
||||
@@ -446,6 +449,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
color: this._getColor(entityConf.entity),
|
||||
label_mode: entityConf.label_mode,
|
||||
attribute: entityConf.attribute,
|
||||
unit: entityConf.unit,
|
||||
focus: entityConf.focus,
|
||||
name: entityConf.name,
|
||||
})),
|
||||
|
@@ -323,6 +323,7 @@ interface GeoLocationSourceConfig {
|
||||
source: string;
|
||||
label_mode?: "name" | "state" | "attribute" | "icon";
|
||||
attribute?: string;
|
||||
unit?: string;
|
||||
focus?: boolean;
|
||||
}
|
||||
|
||||
|
@@ -297,17 +297,24 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _dateRangeChanged(ev) {
|
||||
const weekStartsOn = firstWeekdayIndex(this.hass.locale);
|
||||
this._startDate = calcDate(
|
||||
ev.detail.value.startDate,
|
||||
startOfDay,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
this.hass.config,
|
||||
{
|
||||
weekStartsOn,
|
||||
}
|
||||
);
|
||||
this._endDate = calcDate(
|
||||
ev.detail.value.endDate,
|
||||
endOfDay,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
this.hass.config,
|
||||
{
|
||||
weekStartsOn,
|
||||
}
|
||||
);
|
||||
|
||||
this._updateCollectionPeriod();
|
||||
|
@@ -40,6 +40,7 @@ export const mapEntitiesConfigStruct = union([
|
||||
entity: string(),
|
||||
label_mode: optional(string()),
|
||||
attribute: optional(string()),
|
||||
unit: optional(string()),
|
||||
focus: optional(boolean()),
|
||||
name: optional(string()),
|
||||
}),
|
||||
@@ -51,6 +52,7 @@ const geoSourcesConfigStruct = union([
|
||||
source: string(),
|
||||
label_mode: optional(string()),
|
||||
attribute: optional(string()),
|
||||
unit: optional(string()),
|
||||
focus: optional(boolean()),
|
||||
}),
|
||||
string(),
|
||||
|
@@ -139,7 +139,7 @@ export class HuiDialogSelectDashboard extends LitElement {
|
||||
...(this._params!.dashboards || (await fetchDashboards(this.hass))),
|
||||
];
|
||||
|
||||
const currentPath = this._fromUrlPath || this.hass.defaultPanel;
|
||||
const currentPath = this._fromUrlPath || this.hass.sidebar.defaultPanel;
|
||||
for (const dashboard of this._dashboards!) {
|
||||
if (dashboard.url_path !== currentPath) {
|
||||
this._toUrlPath = dashboard.url_path;
|
||||
|
@@ -77,7 +77,7 @@ export class HuiDialogSelectView extends LitElement {
|
||||
"ui.panel.lovelace.editor.select_view.dashboard_label"
|
||||
)}
|
||||
.disabled=${!this._dashboards.length}
|
||||
.value=${this._urlPath || this.hass.defaultPanel}
|
||||
.value=${this._urlPath || this.hass.sidebar.defaultPanel}
|
||||
@selected=${this._dashboardChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
|
@@ -57,7 +57,6 @@ import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../dialogs/generic/show-dialog-box";
|
||||
import { showMoreInfoDialog } from "../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import {
|
||||
QuickBarMode,
|
||||
showQuickBar,
|
||||
@@ -76,9 +75,9 @@ 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 "./views/hui-view-background";
|
||||
import "./views/hui-view-container";
|
||||
|
||||
@customElement("hui-root")
|
||||
class HUIRoot extends LitElement {
|
||||
@@ -491,16 +490,7 @@ class HUIRoot extends LitElement {
|
||||
} else if (searchParams.conversation === "1") {
|
||||
this._clearParam("conversation");
|
||||
this._showVoiceCommandDialog();
|
||||
} else if (searchParams["more-info-entity-id"]) {
|
||||
const entityId = searchParams["more-info-entity-id"];
|
||||
this._clearParam("more-info-entity-id");
|
||||
// Wait for the next render to ensure the view is fully loaded
|
||||
// because the more info dialog is closed when the url changes
|
||||
afterNextRender(() => {
|
||||
this._showMoreInfoDialog(entityId);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("scroll", this._handleWindowScroll, {
|
||||
passive: true,
|
||||
});
|
||||
@@ -740,10 +730,6 @@ class HUIRoot extends LitElement {
|
||||
showVoiceCommandDialog(this, this.hass, { pipeline_id: "last_used" });
|
||||
}
|
||||
|
||||
private _showMoreInfoDialog(entityId: string): void {
|
||||
showMoreInfoDialog(this, { entityId });
|
||||
}
|
||||
|
||||
private _handleEnableEditMode(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||
return;
|
||||
|
@@ -62,8 +62,6 @@ export interface LovelaceGridOptions {
|
||||
min_columns?: number;
|
||||
min_rows?: number;
|
||||
max_rows?: number;
|
||||
fixed_rows?: boolean;
|
||||
fixed_columns?: boolean;
|
||||
}
|
||||
|
||||
export interface LovelaceCard extends HTMLElement {
|
||||
|
@@ -6,8 +6,8 @@ import "../../components/ha-select";
|
||||
import "../../components/ha-settings-row";
|
||||
import type { LovelaceDashboard } from "../../data/lovelace/dashboard";
|
||||
import { fetchDashboards } from "../../data/lovelace/dashboard";
|
||||
import { setDefaultPanel } from "../../data/panel";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { saveFrontendUserData } from "../../data/frontend";
|
||||
|
||||
@customElement("ha-pick-dashboard-row")
|
||||
class HaPickDashboardRow extends LitElement {
|
||||
@@ -37,7 +37,7 @@ class HaPickDashboardRow extends LitElement {
|
||||
"ui.panel.profile.dashboard.dropdown_label"
|
||||
)}
|
||||
.disabled=${!this._dashboards?.length}
|
||||
.value=${this.hass.defaultPanel}
|
||||
.value=${this.hass.sidebar.defaultPanel}
|
||||
@selected=${this._dashboardChanged}
|
||||
naturalMenuWidth
|
||||
>
|
||||
@@ -71,12 +71,16 @@ class HaPickDashboardRow extends LitElement {
|
||||
this._dashboards = await fetchDashboards(this.hass);
|
||||
}
|
||||
|
||||
private _dashboardChanged(ev) {
|
||||
private async _dashboardChanged(ev) {
|
||||
const urlPath = ev.target.value;
|
||||
if (!urlPath || urlPath === this.hass.defaultPanel) {
|
||||
if (!urlPath || urlPath === this.hass.sidebar.defaultPanel) {
|
||||
return;
|
||||
}
|
||||
setDefaultPanel(this, urlPath);
|
||||
await saveFrontendUserData(this.hass!.connection, "sidebar", {
|
||||
panelOrder: this.hass!.sidebar.panelOrder,
|
||||
hiddenPanels: this.hass!.sidebar.hiddenPanels,
|
||||
defaultPanel: urlPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -167,6 +167,10 @@ class HaProfileSectionGeneral extends LitElement {
|
||||
)}
|
||||
</mwc-button>
|
||||
</ha-settings-row>
|
||||
<ha-pick-dashboard-row
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-pick-dashboard-row>
|
||||
${this.hass.user!.is_admin
|
||||
? html`
|
||||
<ha-advanced-mode-row
|
||||
@@ -200,10 +204,6 @@ class HaProfileSectionGeneral extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-pick-theme-row>
|
||||
<ha-pick-dashboard-row
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-pick-dashboard-row>
|
||||
${this.hass.dockedSidebar !== "auto" || !this.narrow
|
||||
? html`
|
||||
<ha-force-narrow-row
|
||||
|
@@ -59,7 +59,11 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
||||
services: null as any,
|
||||
user: null as any,
|
||||
panelUrl: (this as any)._panelUrl,
|
||||
defaultPanel: DEFAULT_PANEL,
|
||||
sidebar: {
|
||||
defaultPanel: DEFAULT_PANEL,
|
||||
hiddenPanels: [],
|
||||
panelOrder: [],
|
||||
},
|
||||
language,
|
||||
selectedLanguage: null,
|
||||
locale: {
|
||||
|
@@ -7,20 +7,16 @@ interface DockSidebarParams {
|
||||
dock: HomeAssistant["dockedSidebar"];
|
||||
}
|
||||
|
||||
interface DefaultPanelParams {
|
||||
defaultPanel: HomeAssistant["defaultPanel"];
|
||||
}
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"hass-dock-sidebar": DockSidebarParams;
|
||||
"hass-default-panel": DefaultPanelParams;
|
||||
"hass-set-sidebar-data": HomeAssistant["sidebar"];
|
||||
}
|
||||
// for add event listener
|
||||
interface HTMLElementEventMap {
|
||||
"hass-dock-sidebar": HASSDomEvent<DockSidebarParams>;
|
||||
"hass-default-panel": HASSDomEvent<DefaultPanelParams>;
|
||||
"hass-set-sidebar-data": HASSDomEvent<HomeAssistant["sidebar"]>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +28,10 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
||||
this._updateHass({ dockedSidebar: ev.detail.dock });
|
||||
storeState(this.hass!);
|
||||
});
|
||||
this.addEventListener("hass-default-panel", (ev) => {
|
||||
this._updateHass({ defaultPanel: ev.detail.defaultPanel });
|
||||
this.addEventListener("hass-set-sidebar-data", async (ev) => {
|
||||
this._updateHass({
|
||||
sidebar: ev.detail,
|
||||
});
|
||||
storeState(this.hass!);
|
||||
});
|
||||
}
|
||||
|
@@ -243,7 +243,11 @@ export interface HomeAssistant {
|
||||
vibrate: boolean;
|
||||
debugConnection: boolean;
|
||||
dockedSidebar: "docked" | "always_hidden" | "auto";
|
||||
defaultPanel: string;
|
||||
sidebar: {
|
||||
defaultPanel: string;
|
||||
panelOrder: string[];
|
||||
hiddenPanels: string[];
|
||||
};
|
||||
moreInfoEntityId: string | null;
|
||||
user?: CurrentUser;
|
||||
userData?: CoreFrontendUserData | null;
|
||||
|
@@ -8,7 +8,7 @@ const STORED_STATE = [
|
||||
"debugConnection",
|
||||
"suspendWhenHidden",
|
||||
"enableShortcuts",
|
||||
"defaultPanel",
|
||||
"sidebar",
|
||||
];
|
||||
|
||||
export function storeState(hass: HomeAssistant) {
|
||||
|
Reference in New Issue
Block a user