Merge branch 'picture-elements-position' of https://github.com/4ybaka/frontend into picture-elements-position
commit
29f585ad5a
|
@ -108,9 +108,9 @@ body:
|
||||||
render: yaml
|
render: yaml
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Javascript errors shown in your browser console/inspector
|
label: JavaScript errors shown in your browser console/inspector
|
||||||
description: >
|
description: >
|
||||||
If you come across any Javascript or other error logs, e.g., in your
|
If you come across any JavaScript or other error logs, e.g., in your
|
||||||
browser console/inspector please provide them.
|
browser console/inspector please provide them.
|
||||||
render: txt
|
render: txt
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
yarn run lint-staged --relative --shell "/bin/bash"
|
yarn run lint-staged --relative
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,4 +6,4 @@ enableGlobalCache: false
|
||||||
|
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-4.9.1.cjs
|
yarnPath: .yarn/releases/yarn-4.9.2.cjs
|
||||||
|
|
38
package.json
38
package.json
|
@ -26,15 +26,15 @@
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.27.3",
|
"@babel/runtime": "7.27.6",
|
||||||
"@braintree/sanitize-url": "7.1.1",
|
"@braintree/sanitize-url": "7.1.1",
|
||||||
"@codemirror/autocomplete": "6.18.6",
|
"@codemirror/autocomplete": "6.18.6",
|
||||||
"@codemirror/commands": "6.8.1",
|
"@codemirror/commands": "6.8.1",
|
||||||
"@codemirror/language": "6.11.0",
|
"@codemirror/language": "6.11.1",
|
||||||
"@codemirror/legacy-modes": "6.5.1",
|
"@codemirror/legacy-modes": "6.5.1",
|
||||||
"@codemirror/search": "6.5.11",
|
"@codemirror/search": "6.5.11",
|
||||||
"@codemirror/state": "6.5.2",
|
"@codemirror/state": "6.5.2",
|
||||||
"@codemirror/view": "6.37.0",
|
"@codemirror/view": "6.37.1",
|
||||||
"@egjs/hammerjs": "2.0.17",
|
"@egjs/hammerjs": "2.0.17",
|
||||||
"@formatjs/intl-datetimeformat": "6.18.0",
|
"@formatjs/intl-datetimeformat": "6.18.0",
|
||||||
"@formatjs/intl-displaynames": "6.8.11",
|
"@formatjs/intl-displaynames": "6.8.11",
|
||||||
|
@ -89,8 +89,8 @@
|
||||||
"@thomasloven/round-slider": "0.6.0",
|
"@thomasloven/round-slider": "0.6.0",
|
||||||
"@tsparticles/engine": "3.8.1",
|
"@tsparticles/engine": "3.8.1",
|
||||||
"@tsparticles/preset-links": "3.2.0",
|
"@tsparticles/preset-links": "3.2.0",
|
||||||
"@vaadin/combo-box": "24.7.7",
|
"@vaadin/combo-box": "24.7.8",
|
||||||
"@vaadin/vaadin-themable-mixin": "24.7.7",
|
"@vaadin/vaadin-themable-mixin": "24.7.8",
|
||||||
"@vibrant/color": "4.0.0",
|
"@vibrant/color": "4.0.0",
|
||||||
"@vue/web-component-wrapper": "1.3.0",
|
"@vue/web-component-wrapper": "1.3.0",
|
||||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
"barcode-detector": "3.0.4",
|
"barcode-detector": "3.0.4",
|
||||||
"color-name": "2.0.0",
|
"color-name": "2.0.0",
|
||||||
"comlink": "4.4.2",
|
"comlink": "4.4.2",
|
||||||
"core-js": "3.42.0",
|
"core-js": "3.43.0",
|
||||||
"cropperjs": "1.6.2",
|
"cropperjs": "1.6.2",
|
||||||
"date-fns": "4.1.0",
|
"date-fns": "4.1.0",
|
||||||
"date-fns-tz": "3.2.0",
|
"date-fns-tz": "3.2.0",
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
"fuse.js": "7.1.0",
|
"fuse.js": "7.1.0",
|
||||||
"google-timezones-json": "1.2.0",
|
"google-timezones-json": "1.2.0",
|
||||||
"gulp-zopfli-green": "6.0.2",
|
"gulp-zopfli-green": "6.0.2",
|
||||||
"hls.js": "1.6.2",
|
"hls.js": "1.6.5",
|
||||||
"home-assistant-js-websocket": "9.5.0",
|
"home-assistant-js-websocket": "9.5.0",
|
||||||
"idb-keyval": "6.2.2",
|
"idb-keyval": "6.2.2",
|
||||||
"intl-messageformat": "10.7.16",
|
"intl-messageformat": "10.7.16",
|
||||||
|
@ -149,20 +149,20 @@
|
||||||
"xss": "1.0.15"
|
"xss": "1.0.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.27.3",
|
"@babel/core": "7.27.4",
|
||||||
"@babel/helper-define-polyfill-provider": "0.6.4",
|
"@babel/helper-define-polyfill-provider": "0.6.4",
|
||||||
"@babel/plugin-transform-runtime": "7.27.3",
|
"@babel/plugin-transform-runtime": "7.27.4",
|
||||||
"@babel/preset-env": "7.27.2",
|
"@babel/preset-env": "7.27.2",
|
||||||
"@bundle-stats/plugin-webpack-filter": "4.20.1",
|
"@bundle-stats/plugin-webpack-filter": "4.20.2",
|
||||||
"@lokalise/node-api": "14.8.0",
|
"@lokalise/node-api": "14.8.0",
|
||||||
"@octokit/auth-oauth-device": "8.0.1",
|
"@octokit/auth-oauth-device": "8.0.1",
|
||||||
"@octokit/plugin-retry": "8.0.1",
|
"@octokit/plugin-retry": "8.0.1",
|
||||||
"@octokit/rest": "22.0.0",
|
"@octokit/rest": "22.0.0",
|
||||||
"@rsdoctor/rspack-plugin": "1.1.2",
|
"@rsdoctor/rspack-plugin": "1.1.3",
|
||||||
"@rspack/cli": "1.3.12",
|
"@rspack/cli": "1.3.12",
|
||||||
"@rspack/core": "1.3.12",
|
"@rspack/core": "1.3.12",
|
||||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
"@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/chromecast-caf-sender": "1.0.11",
|
||||||
"@types/color-name": "2.0.0",
|
"@types/color-name": "2.0.0",
|
||||||
"@types/glob": "8.1.0",
|
"@types/glob": "8.1.0",
|
||||||
|
@ -179,12 +179,12 @@
|
||||||
"@types/tar": "6.1.13",
|
"@types/tar": "6.1.13",
|
||||||
"@types/ua-parser-js": "0.7.39",
|
"@types/ua-parser-js": "0.7.39",
|
||||||
"@types/webspeechapi": "0.0.29",
|
"@types/webspeechapi": "0.0.29",
|
||||||
"@vitest/coverage-v8": "3.1.4",
|
"@vitest/coverage-v8": "3.2.3",
|
||||||
"babel-loader": "10.0.0",
|
"babel-loader": "10.0.0",
|
||||||
"babel-plugin-template-html-minifier": "4.1.0",
|
"babel-plugin-template-html-minifier": "4.1.0",
|
||||||
"browserslist-useragent-regexp": "4.1.3",
|
"browserslist-useragent-regexp": "4.1.3",
|
||||||
"del": "8.0.0",
|
"del": "8.0.0",
|
||||||
"eslint": "9.27.0",
|
"eslint": "9.28.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-prettier": "10.1.5",
|
"eslint-config-prettier": "10.1.5",
|
||||||
"eslint-import-resolver-webpack": "0.13.10",
|
"eslint-import-resolver-webpack": "0.13.10",
|
||||||
|
@ -196,7 +196,7 @@
|
||||||
"fancy-log": "2.0.0",
|
"fancy-log": "2.0.0",
|
||||||
"fs-extra": "11.3.0",
|
"fs-extra": "11.3.0",
|
||||||
"glob": "11.0.2",
|
"glob": "11.0.2",
|
||||||
"gulp": "5.0.0",
|
"gulp": "5.0.1",
|
||||||
"gulp-brotli": "3.0.0",
|
"gulp-brotli": "3.0.0",
|
||||||
"gulp-json-transform": "0.5.0",
|
"gulp-json-transform": "0.5.0",
|
||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
|
@ -204,7 +204,7 @@
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"jsdom": "26.1.0",
|
"jsdom": "26.1.0",
|
||||||
"jszip": "3.10.1",
|
"jszip": "3.10.1",
|
||||||
"lint-staged": "15.5.2",
|
"lint-staged": "16.1.0",
|
||||||
"lit-analyzer": "2.0.3",
|
"lit-analyzer": "2.0.3",
|
||||||
"lodash.merge": "4.6.2",
|
"lodash.merge": "4.6.2",
|
||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
|
@ -218,9 +218,9 @@
|
||||||
"terser-webpack-plugin": "5.3.14",
|
"terser-webpack-plugin": "5.3.14",
|
||||||
"ts-lit-plugin": "2.0.2",
|
"ts-lit-plugin": "2.0.2",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.8.3",
|
||||||
"typescript-eslint": "8.33.0",
|
"typescript-eslint": "8.34.0",
|
||||||
"vite-tsconfig-paths": "5.1.4",
|
"vite-tsconfig-paths": "5.1.4",
|
||||||
"vitest": "3.1.4",
|
"vitest": "3.2.3",
|
||||||
"webpack-stats-plugin": "1.1.3",
|
"webpack-stats-plugin": "1.1.3",
|
||||||
"webpackbar": "7.0.0",
|
"webpackbar": "7.0.0",
|
||||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||||
|
@ -236,5 +236,5 @@
|
||||||
"tslib": "2.8.1",
|
"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"
|
"@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"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.9.1"
|
"packageManager": "yarn@4.9.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,14 +229,20 @@ export class StateHistoryChartLine extends LitElement {
|
||||||
minYAxis = ({ min }) => Math.min(min, this.minYAxis!);
|
minYAxis = ({ min }) => Math.min(min, this.minYAxis!);
|
||||||
}
|
}
|
||||||
} else if (this.logarithmicScale) {
|
} else if (this.logarithmicScale) {
|
||||||
minYAxis = ({ min }) => Math.floor(min > 0 ? min * 0.95 : min * 1.05);
|
minYAxis = ({ min }) => {
|
||||||
|
const value = min > 0 ? min * 0.95 : min * 1.05;
|
||||||
|
return Math.abs(value) < 1 ? value : Math.floor(value);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (typeof maxYAxis === "number") {
|
if (typeof maxYAxis === "number") {
|
||||||
if (this.fitYData) {
|
if (this.fitYData) {
|
||||||
maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!);
|
maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!);
|
||||||
}
|
}
|
||||||
} else if (this.logarithmicScale) {
|
} else if (this.logarithmicScale) {
|
||||||
maxYAxis = ({ max }) => Math.ceil(max > 0 ? max * 1.05 : max * 0.95);
|
maxYAxis = ({ max }) => {
|
||||||
|
const value = max > 0 ? max * 1.05 : max * 0.95;
|
||||||
|
return Math.abs(value) < 1 ? value : Math.ceil(value);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
this._chartOptions = {
|
this._chartOptions = {
|
||||||
xAxis: {
|
xAxis: {
|
||||||
|
@ -753,10 +759,10 @@ export class StateHistoryChartLine extends LitElement {
|
||||||
if (this.logarithmicScale) {
|
if (this.logarithmicScale) {
|
||||||
// log(0) is -Infinity, so we need to set a minimum value
|
// log(0) is -Infinity, so we need to set a minimum value
|
||||||
if (typeof value === "number") {
|
if (typeof value === "number") {
|
||||||
return Math.max(value, 0.1);
|
return Math.max(value, Number.EPSILON);
|
||||||
}
|
}
|
||||||
if (typeof value === "function") {
|
if (typeof value === "function") {
|
||||||
return (values: any) => Math.max(value(values), 0.1);
|
return (values: any) => Math.max(value(values), Number.EPSILON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -241,14 +241,20 @@ export class StatisticsChart extends LitElement {
|
||||||
minYAxis = ({ min }) => Math.min(min, this.minYAxis!);
|
minYAxis = ({ min }) => Math.min(min, this.minYAxis!);
|
||||||
}
|
}
|
||||||
} else if (this.logarithmicScale) {
|
} else if (this.logarithmicScale) {
|
||||||
minYAxis = ({ min }) => Math.floor(min > 0 ? min * 0.95 : min * 1.05);
|
minYAxis = ({ min }) => {
|
||||||
|
const value = min > 0 ? min * 0.95 : min * 1.05;
|
||||||
|
return Math.abs(value) < 1 ? value : Math.floor(value);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (typeof maxYAxis === "number") {
|
if (typeof maxYAxis === "number") {
|
||||||
if (this.fitYData) {
|
if (this.fitYData) {
|
||||||
maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!);
|
maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!);
|
||||||
}
|
}
|
||||||
} else if (this.logarithmicScale) {
|
} else if (this.logarithmicScale) {
|
||||||
maxYAxis = ({ max }) => Math.ceil(max > 0 ? max * 1.05 : max * 0.95);
|
maxYAxis = ({ max }) => {
|
||||||
|
const value = max > 0 ? max * 1.05 : max * 0.95;
|
||||||
|
return Math.abs(value) < 1 ? value : Math.ceil(value);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const endTime = this.endTime ?? new Date();
|
const endTime = this.endTime ?? new Date();
|
||||||
let startTime = this.startTime;
|
let startTime = this.startTime;
|
||||||
|
@ -619,10 +625,10 @@ export class StatisticsChart extends LitElement {
|
||||||
if (this.logarithmicScale) {
|
if (this.logarithmicScale) {
|
||||||
// log(0) is -Infinity, so we need to set a minimum value
|
// log(0) is -Infinity, so we need to set a minimum value
|
||||||
if (typeof value === "number") {
|
if (typeof value === "number") {
|
||||||
return Math.max(value, 0.1);
|
return Math.max(value, Number.EPSILON);
|
||||||
}
|
}
|
||||||
if (typeof value === "function") {
|
if (typeof value === "function") {
|
||||||
return (values: any) => Math.max(value(values), 0.1);
|
return (values: any) => Math.max(value(values), Number.EPSILON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -509,7 +509,7 @@ export class HaAssistChat extends LitElement {
|
||||||
this.requestUpdate("_conversation");
|
this.requestUpdate("_conversation");
|
||||||
},
|
},
|
||||||
processEvent: (event: PipelineRunEvent) => {
|
processEvent: (event: PipelineRunEvent) => {
|
||||||
if (event.type === "intent-progress") {
|
if (event.type === "intent-progress" && event.data.chat_log_delta) {
|
||||||
const delta = event.data.chat_log_delta;
|
const delta = event.data.chat_log_delta;
|
||||||
|
|
||||||
// new message
|
// new message
|
||||||
|
|
|
@ -345,8 +345,10 @@ export class HaComboBox extends LitElement {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._comboBox._closeOnBlurIsPrevented = true;
|
this._comboBox._closeOnBlurIsPrevented = true;
|
||||||
}
|
}
|
||||||
|
if (!this.opened) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const newValue = ev.detail.value;
|
const newValue = ev.detail.value;
|
||||||
|
|
||||||
if (newValue !== this.value) {
|
if (newValue !== this.value) {
|
||||||
fireEvent(this, "value-changed", { value: newValue || undefined });
|
fireEvent(this, "value-changed", { value: newValue || undefined });
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,12 +626,15 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||||
this._tooltipHideTimeout = undefined;
|
this._tooltipHideTimeout = undefined;
|
||||||
}
|
}
|
||||||
const tooltip = this._tooltip;
|
const tooltip = this._tooltip;
|
||||||
const listbox = this.shadowRoot!.querySelector("ha-md-list")!;
|
const allListbox = this.shadowRoot!.querySelectorAll("ha-md-list")!;
|
||||||
let top = item.offsetTop + 11;
|
const listbox = [...allListbox].find((lb) => lb.contains(item));
|
||||||
if (listbox.contains(item)) {
|
|
||||||
top += listbox.offsetTop;
|
const top =
|
||||||
top -= listbox.scrollTop;
|
item.offsetTop +
|
||||||
}
|
11 +
|
||||||
|
(listbox?.offsetTop ?? 0) -
|
||||||
|
(listbox?.scrollTop ?? 0);
|
||||||
|
|
||||||
tooltip.innerText = (
|
tooltip.innerText = (
|
||||||
item.querySelector(".item-text") as HTMLElement
|
item.querySelector(".item-text") as HTMLElement
|
||||||
).innerText;
|
).innerText;
|
||||||
|
|
|
@ -134,7 +134,8 @@ export interface ConversationChatLogToolResultDelta {
|
||||||
interface PipelineIntentProgressEvent extends PipelineEventBase {
|
interface PipelineIntentProgressEvent extends PipelineEventBase {
|
||||||
type: "intent-progress";
|
type: "intent-progress";
|
||||||
data: {
|
data: {
|
||||||
chat_log_delta:
|
tts_start_streaming?: boolean;
|
||||||
|
chat_log_delta?:
|
||||||
| Partial<ConversationChatLogAssistantDelta>
|
| Partial<ConversationChatLogAssistantDelta>
|
||||||
// These always come in 1 chunk
|
// These always come in 1 chunk
|
||||||
| ConversationChatLogToolResultDelta;
|
| ConversationChatLogToolResultDelta;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
import type { EntityRegistryEntry } from "./entity_registry";
|
import type { EntityRegistryEntry } from "./entity_registry";
|
||||||
import type { FrontendLocaleData } from "./translation";
|
import type { FrontendLocaleData } from "./translation";
|
||||||
import { isTriggerList } from "./trigger";
|
import { isTriggerList } from "./trigger";
|
||||||
|
import { hasTemplate } from "../common/string/has-template";
|
||||||
|
|
||||||
const triggerTranslationBaseKey =
|
const triggerTranslationBaseKey =
|
||||||
"ui.panel.config.automation.editor.triggers.type";
|
"ui.panel.config.automation.editor.triggers.type";
|
||||||
|
@ -820,6 +821,12 @@ const tryDescribeCondition = (
|
||||||
entityRegistry: EntityRegistryEntry[],
|
entityRegistry: EntityRegistryEntry[],
|
||||||
ignoreAlias = false
|
ignoreAlias = false
|
||||||
) => {
|
) => {
|
||||||
|
if (typeof condition === "string" && hasTemplate(condition)) {
|
||||||
|
return hass.localize(
|
||||||
|
`${conditionsTranslationBaseKey}.template.description.full`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (condition.alias && !ignoreAlias) {
|
if (condition.alias && !ignoreAlias) {
|
||||||
return condition.alias;
|
return condition.alias;
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,7 +339,7 @@ export const computeBackupSize = (backup: BackupContent) =>
|
||||||
|
|
||||||
export type BackupType = "automatic" | "manual" | "addon_update";
|
export type BackupType = "automatic" | "manual" | "addon_update";
|
||||||
|
|
||||||
const BACKUP_TYPE_ORDER: BackupType[] = ["automatic", "manual", "addon_update"];
|
const BACKUP_TYPE_ORDER: BackupType[] = ["automatic", "addon_update", "manual"];
|
||||||
|
|
||||||
export const getBackupTypes = memoize((isHassio: boolean) =>
|
export const getBackupTypes = memoize((isHassio: boolean) =>
|
||||||
isHassio
|
isHassio
|
||||||
|
|
|
@ -679,7 +679,9 @@ export const getEnergyDataCollection = (
|
||||||
const period =
|
const period =
|
||||||
preferredPeriod === "today" && hour === "0" ? "yesterday" : preferredPeriod;
|
preferredPeriod === "today" && hour === "0" ? "yesterday" : preferredPeriod;
|
||||||
|
|
||||||
[collection.start, collection.end] = calcDateRange(hass, period);
|
const [start, end] = calcDateRange(hass, period);
|
||||||
|
collection.start = calcDate(start, startOfDay, hass.locale, hass.config);
|
||||||
|
collection.end = calcDate(end, endOfDay, hass.locale, hass.config);
|
||||||
|
|
||||||
const scheduleUpdatePeriod = () => {
|
const scheduleUpdatePeriod = () => {
|
||||||
collection._updatePeriodTimeout = window.setTimeout(
|
collection._updatePeriodTimeout = window.setTimeout(
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { migrateAutomationTrigger } from "./automation";
|
||||||
import type { BlueprintInput } from "./blueprint";
|
import type { BlueprintInput } from "./blueprint";
|
||||||
import { computeObjectId } from "../common/entity/compute_object_id";
|
import { computeObjectId } from "../common/entity/compute_object_id";
|
||||||
import { createSearchParam } from "../common/url/search-params";
|
import { createSearchParam } from "../common/url/search-params";
|
||||||
|
import { hasTemplate } from "../common/string/has-template";
|
||||||
|
|
||||||
export const MODES = ["single", "restart", "queued", "parallel"] as const;
|
export const MODES = ["single", "restart", "queued", "parallel"] as const;
|
||||||
export const MODES_MAX = ["queued", "parallel"] as const;
|
export const MODES_MAX = ["queued", "parallel"] as const;
|
||||||
|
@ -339,6 +340,9 @@ export const getScriptEditorInitData = () => {
|
||||||
|
|
||||||
export const getActionType = (action: Action): ActionType => {
|
export const getActionType = (action: Action): ActionType => {
|
||||||
// Check based on config_validation.py#determine_script_action
|
// Check based on config_validation.py#determine_script_action
|
||||||
|
if (typeof action === "string" && hasTemplate(action)) {
|
||||||
|
return "check_condition";
|
||||||
|
}
|
||||||
if ("delay" in action) {
|
if ("delay" in action) {
|
||||||
return "delay";
|
return "delay";
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import "./integration-badge";
|
||||||
import { onBoardingStyles } from "./styles";
|
import { onBoardingStyles } from "./styles";
|
||||||
|
|
||||||
const HIDDEN_DOMAINS = new Set([
|
const HIDDEN_DOMAINS = new Set([
|
||||||
|
"backup",
|
||||||
"google_translate",
|
"google_translate",
|
||||||
"hassio",
|
"hassio",
|
||||||
"met",
|
"met",
|
||||||
|
|
|
@ -233,6 +233,8 @@ export class CloudLogin extends LitElement {
|
||||||
text: this.hass.localize(
|
text: this.hass.localize(
|
||||||
"ui.panel.config.cloud.login.cloud_pipeline_text"
|
"ui.panel.config.cloud.login.cloud_pipeline_text"
|
||||||
),
|
),
|
||||||
|
confirmText: this.hass.localize("ui.common.yes"),
|
||||||
|
dismissText: this.hass.localize("ui.common.no"),
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
setAssistPipelinePreferred(this.hass, result.cloud_pipeline);
|
setAssistPipelinePreferred(this.hass, result.cloud_pipeline);
|
||||||
|
|
|
@ -127,16 +127,15 @@ export class HaConfigDevicePage extends LitElement {
|
||||||
|
|
||||||
@state() private _related?: RelatedResult;
|
@state() private _related?: RelatedResult;
|
||||||
|
|
||||||
// If a number, it's the request ID so we make sure we don't show older info
|
@state() private _diagnosticDownloadLinks: DeviceAction[] = [];
|
||||||
@state() private _diagnosticDownloadLinks?: number | DeviceAction[];
|
|
||||||
|
|
||||||
@state() private _deleteButtons?: DeviceAction[];
|
@state() private _deleteButtons: DeviceAction[] = [];
|
||||||
|
|
||||||
@state() private _deviceActions?: DeviceAction[];
|
@state() private _deviceActions: DeviceAction[] = [];
|
||||||
|
|
||||||
@state() private _deviceAlerts?: DeviceAlert[];
|
@state() private _deviceAlerts: DeviceAlert[] = [];
|
||||||
|
|
||||||
private _deviceAlertsTimeout?: number;
|
private _deviceAlertsActionsTimeout?: number;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
|
@ -255,42 +254,19 @@ export class HaConfigDevicePage extends LitElement {
|
||||||
public willUpdate(changedProps) {
|
public willUpdate(changedProps) {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
if (
|
if (changedProps.has("deviceId") || changedProps.has("entries")) {
|
||||||
changedProps.has("deviceId") ||
|
this._deviceActions = [];
|
||||||
changedProps.has("devices") ||
|
this._deviceAlerts = [];
|
||||||
changedProps.has("entries")
|
this._deleteButtons = [];
|
||||||
) {
|
this._diagnosticDownloadLinks = [];
|
||||||
this._diagnosticDownloadLinks = undefined;
|
this._fetchData();
|
||||||
this._deleteButtons = undefined;
|
|
||||||
this._deviceActions = undefined;
|
|
||||||
this._deviceAlerts = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
(this._diagnosticDownloadLinks &&
|
|
||||||
this._deleteButtons &&
|
|
||||||
this._deviceActions &&
|
|
||||||
this._deviceAlerts) ||
|
|
||||||
!this.deviceId ||
|
|
||||||
!this.entries
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._diagnosticDownloadLinks = Math.random();
|
|
||||||
this._deleteButtons = []; // To prevent re-rendering if no delete buttons
|
|
||||||
this._deviceActions = [];
|
|
||||||
this._deviceAlerts = [];
|
|
||||||
this._getDiagnosticButtons(this._diagnosticDownloadLinks);
|
|
||||||
this._getDeleteActions();
|
|
||||||
this._getDeviceActions();
|
|
||||||
clearTimeout(this._deviceAlertsTimeout);
|
|
||||||
this._getDeviceAlerts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
loadDeviceRegistryDetailDialog();
|
loadDeviceRegistryDetailDialog();
|
||||||
|
this._fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
|
@ -302,7 +278,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
clearTimeout(this._deviceAlertsTimeout);
|
clearTimeout(this._deviceAlertsActionsTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
@ -909,7 +885,18 @@ export class HaConfigDevicePage extends LitElement {
|
||||||
</hass-subpage>`;
|
</hass-subpage>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _getDiagnosticButtons(requestId: number): Promise<void> {
|
private _fetchData() {
|
||||||
|
if (this.deviceId && this.entries.length) {
|
||||||
|
this._getDiagnosticButtons();
|
||||||
|
this._getDeleteActions();
|
||||||
|
clearTimeout(this._deviceAlertsActionsTimeout);
|
||||||
|
this._getDeviceActions();
|
||||||
|
this._getDeviceAlerts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getDiagnosticButtons(): Promise<void> {
|
||||||
|
const deviceId = this.deviceId;
|
||||||
if (!isComponentLoaded(this.hass, "diagnostics")) {
|
if (!isComponentLoaded(this.hass, "diagnostics")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -951,7 +938,8 @@ export class HaConfigDevicePage extends LitElement {
|
||||||
|
|
||||||
links = links.filter(Boolean);
|
links = links.filter(Boolean);
|
||||||
|
|
||||||
if (this._diagnosticDownloadLinks !== requestId) {
|
if (this.deviceId !== deviceId) {
|
||||||
|
// abort if the device has changed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (links.length > 0) {
|
if (links.length > 0) {
|
||||||
|
@ -1176,12 +1164,12 @@ export class HaConfigDevicePage extends LitElement {
|
||||||
deviceAlerts.push(...alerts);
|
deviceAlerts.push(...alerts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._deviceAlerts = deviceAlerts;
|
||||||
if (deviceAlerts.length) {
|
if (deviceAlerts.length) {
|
||||||
this._deviceAlerts = deviceAlerts;
|
this._deviceAlertsActionsTimeout = window.setTimeout(() => {
|
||||||
this._deviceAlertsTimeout = window.setTimeout(
|
this._getDeviceAlerts();
|
||||||
() => this._getDeviceAlerts(),
|
this._getDeviceActions();
|
||||||
DEVICE_ALERTS_INTERVAL
|
}, DEVICE_ALERTS_INTERVAL);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,7 @@ export class HaIntegrationCard extends LitElement {
|
||||||
: "custom"}"
|
: "custom"}"
|
||||||
>
|
>
|
||||||
<ha-tooltip
|
<ha-tooltip
|
||||||
|
hoist
|
||||||
.placement=${computeRTL(this.hass) ? "right" : "left"}
|
.placement=${computeRTL(this.hass) ? "right" : "left"}
|
||||||
.content=${this.hass.localize(
|
.content=${this.hass.localize(
|
||||||
this.manifest.overwrites_built_in
|
this.manifest.overwrites_built_in
|
||||||
|
@ -177,6 +178,7 @@ export class HaIntegrationCard extends LitElement {
|
||||||
${this.manifest && this.manifest.iot_class?.startsWith("cloud_")
|
${this.manifest && this.manifest.iot_class?.startsWith("cloud_")
|
||||||
? html`<div class="icon cloud">
|
? html`<div class="icon cloud">
|
||||||
<ha-tooltip
|
<ha-tooltip
|
||||||
|
hoist
|
||||||
.placement=${computeRTL(this.hass) ? "right" : "left"}
|
.placement=${computeRTL(this.hass) ? "right" : "left"}
|
||||||
.content=${this.hass.localize(
|
.content=${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.depends_on_cloud"
|
"ui.panel.config.integrations.config_entry.depends_on_cloud"
|
||||||
|
@ -191,6 +193,7 @@ export class HaIntegrationCard extends LitElement {
|
||||||
!this.items.every((itm) => itm.source === "system")
|
!this.items.every((itm) => itm.source === "system")
|
||||||
? html`<div class="icon yaml">
|
? html`<div class="icon yaml">
|
||||||
<ha-tooltip
|
<ha-tooltip
|
||||||
|
hoist
|
||||||
.placement=${computeRTL(this.hass) ? "right" : "left"}
|
.placement=${computeRTL(this.hass) ? "right" : "left"}
|
||||||
.content=${this.hass.localize(
|
.content=${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.no_config_flow"
|
"ui.panel.config.integrations.config_entry.no_config_flow"
|
||||||
|
|
|
@ -429,7 +429,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||||
</ha-card>
|
</ha-card>
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h1>Third-Party Data Reporting</h1>
|
<h1>Third-party data reporting</h1>
|
||||||
${this._dataCollectionOptIn !== undefined
|
${this._dataCollectionOptIn !== undefined
|
||||||
? html`
|
? html`
|
||||||
<ha-switch
|
<ha-switch
|
||||||
|
|
|
@ -64,7 +64,9 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||||
(source) => source.type === "solar"
|
(source) => source.type === "solar"
|
||||||
);
|
);
|
||||||
const hasGas = prefs.energy_sources.some((source) => source.type === "gas");
|
const hasGas = prefs.energy_sources.some((source) => source.type === "gas");
|
||||||
|
const hasBattery = prefs.energy_sources.some(
|
||||||
|
(source) => source.type === "battery"
|
||||||
|
);
|
||||||
const hasWater = prefs.energy_sources.some(
|
const hasWater = prefs.energy_sources.some(
|
||||||
(source) => source.type === "water"
|
(source) => source.type === "water"
|
||||||
);
|
);
|
||||||
|
@ -74,8 +76,8 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||||
collection_key: "energy_dashboard",
|
collection_key: "energy_dashboard",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only include if we have a grid source.
|
// Only include if we have a grid or battery.
|
||||||
if (hasGrid) {
|
if (hasGrid || hasBattery) {
|
||||||
view.cards!.push({
|
view.cards!.push({
|
||||||
title: hass.localize("ui.panel.energy.cards.energy_usage_graph_title"),
|
title: hass.localize("ui.panel.energy.cards.energy_usage_graph_title"),
|
||||||
type: "energy-usage-graph",
|
type: "energy-usage-graph",
|
||||||
|
@ -110,8 +112,8 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only include if we have a grid.
|
// Only include if we have a grid or battery.
|
||||||
if (hasGrid) {
|
if (hasGrid || hasBattery) {
|
||||||
view.cards!.push({
|
view.cards!.push({
|
||||||
title: hass.localize("ui.panel.energy.cards.energy_distribution_title"),
|
title: hass.localize("ui.panel.energy.cards.energy_distribution_title"),
|
||||||
type: "energy-distribution",
|
type: "energy-distribution",
|
||||||
|
@ -120,7 +122,7 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasGrid || hasSolar || hasGas || hasWater) {
|
if (hasGrid || hasSolar || hasGas || hasWater || hasBattery) {
|
||||||
view.cards!.push({
|
view.cards!.push({
|
||||||
title: hass.localize(
|
title: hass.localize(
|
||||||
"ui.panel.energy.cards.energy_sources_table_title"
|
"ui.panel.energy.cards.energy_sources_table_title"
|
||||||
|
|
|
@ -10,6 +10,8 @@ import {
|
||||||
addYears,
|
addYears,
|
||||||
addMonths,
|
addMonths,
|
||||||
addHours,
|
addHours,
|
||||||
|
startOfDay,
|
||||||
|
addDays,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import type {
|
import type {
|
||||||
BarSeriesOption,
|
BarSeriesOption,
|
||||||
|
@ -282,6 +284,10 @@ export function getCompareTransform(start: Date, compareStart?: Date) {
|
||||||
) {
|
) {
|
||||||
return (ts: Date) => addMonths(ts, compareMonthDiff);
|
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();
|
const compareOffset = start.getTime() - compareStart.getTime();
|
||||||
return (ts: Date) => addMilliseconds(ts, compareOffset);
|
return (ts: Date) => addMilliseconds(ts, compareOffset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,14 +102,13 @@ class HuiEnergyDistrubutionCard
|
||||||
const prefs = this._data.prefs;
|
const prefs = this._data.prefs;
|
||||||
const types = energySourcesByType(prefs);
|
const types = energySourcesByType(prefs);
|
||||||
|
|
||||||
// The strategy only includes this card if we have a grid.
|
const hasGrid =
|
||||||
const hasConsumption = true;
|
!!types.grid?.[0].flow_from.length || !!types.grid?.[0].flow_to.length;
|
||||||
|
|
||||||
const hasSolarProduction = types.solar !== undefined;
|
const hasSolarProduction = types.solar !== undefined;
|
||||||
const hasBattery = types.battery !== undefined;
|
const hasBattery = types.battery !== undefined;
|
||||||
const hasGas = types.gas !== undefined;
|
const hasGas = types.gas !== undefined;
|
||||||
const hasWater = types.water !== undefined;
|
const hasWater = types.water !== undefined;
|
||||||
const hasReturnToGrid = hasConsumption && types.grid![0].flow_to.length > 0;
|
const hasReturnToGrid = !!types.grid?.[0].flow_to.length;
|
||||||
|
|
||||||
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
const { summedData, compareSummedData: _ } = getSummedData(this._data);
|
||||||
const { consumption, compareConsumption: __ } = computeConsumptionData(
|
const { consumption, compareConsumption: __ } = computeConsumptionData(
|
||||||
|
@ -163,14 +162,14 @@ class HuiEnergyDistrubutionCard
|
||||||
}
|
}
|
||||||
let batteryFromGrid: null | number = null;
|
let batteryFromGrid: null | number = null;
|
||||||
let batteryToGrid: null | number = null;
|
let batteryToGrid: null | number = null;
|
||||||
if (hasBattery) {
|
if (hasBattery && hasGrid) {
|
||||||
batteryToGrid = consumption.total.battery_to_grid;
|
batteryToGrid = consumption.total.battery_to_grid;
|
||||||
batteryFromGrid = consumption.total.grid_to_battery;
|
batteryFromGrid = consumption.total.grid_to_battery;
|
||||||
}
|
}
|
||||||
|
|
||||||
let solarToBattery: null | number = null;
|
let solarToBattery: null | number = null;
|
||||||
let solarToGrid: null | number = null;
|
let solarToGrid: null | number = null;
|
||||||
if (hasSolarProduction) {
|
if (hasSolarProduction && hasGrid) {
|
||||||
solarToGrid = consumption.total.solar_to_grid;
|
solarToGrid = consumption.total.solar_to_grid;
|
||||||
}
|
}
|
||||||
if (hasSolarProduction && hasBattery) {
|
if (hasSolarProduction && hasBattery) {
|
||||||
|
@ -182,7 +181,9 @@ class HuiEnergyDistrubutionCard
|
||||||
batteryConsumption = Math.max(consumption.total.used_battery, 0);
|
batteryConsumption = Math.max(consumption.total.used_battery, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const gridConsumption = Math.max(consumption.total.used_grid, 0);
|
const gridConsumption = hasGrid
|
||||||
|
? Math.max(consumption.total.used_grid, 0)
|
||||||
|
: 0;
|
||||||
|
|
||||||
const totalHomeConsumption = Math.max(0, consumption.total.used_total);
|
const totalHomeConsumption = Math.max(0, consumption.total.used_total);
|
||||||
|
|
||||||
|
@ -206,7 +207,11 @@ class HuiEnergyDistrubutionCard
|
||||||
// This fallback is used in the demo
|
// This fallback is used in the demo
|
||||||
let electricityMapUrl = "https://app.electricitymap.org";
|
let electricityMapUrl = "https://app.electricitymap.org";
|
||||||
|
|
||||||
if (this._data.co2SignalEntity && this._data.fossilEnergyConsumption) {
|
if (
|
||||||
|
hasGrid &&
|
||||||
|
this._data.co2SignalEntity &&
|
||||||
|
this._data.fossilEnergyConsumption
|
||||||
|
) {
|
||||||
// Calculate high carbon consumption
|
// Calculate high carbon consumption
|
||||||
const highCarbonEnergy = Object.values(
|
const highCarbonEnergy = Object.values(
|
||||||
this._data.fossilEnergyConsumption
|
this._data.fossilEnergyConsumption
|
||||||
|
@ -225,7 +230,7 @@ class HuiEnergyDistrubutionCard
|
||||||
if (gridConsumption !== totalFromGrid) {
|
if (gridConsumption !== totalFromGrid) {
|
||||||
// Only get the part that was used for consumption and not the battery
|
// Only get the part that was used for consumption and not the battery
|
||||||
highCarbonConsumption =
|
highCarbonConsumption =
|
||||||
highCarbonEnergy * (gridConsumption / totalFromGrid);
|
highCarbonEnergy * (gridConsumption! / totalFromGrid);
|
||||||
} else {
|
} else {
|
||||||
highCarbonConsumption = highCarbonEnergy;
|
highCarbonConsumption = highCarbonEnergy;
|
||||||
}
|
}
|
||||||
|
@ -378,41 +383,43 @@ class HuiEnergyDistrubutionCard
|
||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="circle-container grid">
|
${hasGrid
|
||||||
<div class="circle">
|
? html`<div class="circle-container grid">
|
||||||
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
|
<div class="circle">
|
||||||
${returnedToGrid !== null
|
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
|
||||||
? html`<span class="return">
|
${returnedToGrid !== null
|
||||||
<ha-svg-icon
|
? html`<span class="return">
|
||||||
class="small"
|
<ha-svg-icon
|
||||||
.path=${mdiArrowLeft}
|
class="small"
|
||||||
></ha-svg-icon
|
.path=${mdiArrowLeft}
|
||||||
>${formatConsumptionShort(
|
></ha-svg-icon
|
||||||
|
>${formatConsumptionShort(
|
||||||
|
this.hass,
|
||||||
|
returnedToGrid,
|
||||||
|
"kWh"
|
||||||
|
)}
|
||||||
|
</span>`
|
||||||
|
: ""}
|
||||||
|
<span class="consumption">
|
||||||
|
${hasReturnToGrid
|
||||||
|
? html`<ha-svg-icon
|
||||||
|
class="small"
|
||||||
|
.path=${mdiArrowRight}
|
||||||
|
></ha-svg-icon>`
|
||||||
|
: ""}${formatConsumptionShort(
|
||||||
this.hass,
|
this.hass,
|
||||||
returnedToGrid,
|
totalFromGrid,
|
||||||
"kWh"
|
"kWh"
|
||||||
)}
|
)}
|
||||||
</span>`
|
</span>
|
||||||
: ""}
|
</div>
|
||||||
<span class="consumption">
|
<span class="label"
|
||||||
${hasReturnToGrid
|
>${this.hass.localize(
|
||||||
? html`<ha-svg-icon
|
"ui.panel.lovelace.cards.energy.energy_distribution.grid"
|
||||||
class="small"
|
)}</span
|
||||||
.path=${mdiArrowRight}
|
>
|
||||||
></ha-svg-icon>`
|
</div> `
|
||||||
: ""}${formatConsumptionShort(
|
: html`<div class="grid-spacer"></div>`}
|
||||||
this.hass,
|
|
||||||
totalFromGrid,
|
|
||||||
"kWh"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span class="label"
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.lovelace.cards.energy.energy_distribution.grid"
|
|
||||||
)}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="circle-container home">
|
<div class="circle-container home">
|
||||||
<div
|
<div
|
||||||
class="circle ${classMap({
|
class="circle ${classMap({
|
||||||
|
@ -480,22 +487,27 @@ class HuiEnergyDistrubutionCard
|
||||||
shape-rendering="geometricPrecision"
|
shape-rendering="geometricPrecision"
|
||||||
/>`
|
/>`
|
||||||
: ""}
|
: ""}
|
||||||
<circle
|
${hasGrid
|
||||||
|
? svg`<circle
|
||||||
class="grid"
|
class="grid"
|
||||||
cx="40"
|
cx="40"
|
||||||
cy="40"
|
cy="40"
|
||||||
r="38"
|
r="38"
|
||||||
stroke-dasharray="${homeHighCarbonCircumference ??
|
stroke-dasharray="${
|
||||||
CIRCLE_CIRCUMFERENCE -
|
homeHighCarbonCircumference ??
|
||||||
homeSolarCircumference! -
|
CIRCLE_CIRCUMFERENCE -
|
||||||
(homeBatteryCircumference ||
|
homeSolarCircumference! -
|
||||||
0)} ${homeHighCarbonCircumference !== undefined
|
(homeBatteryCircumference || 0)
|
||||||
? CIRCLE_CIRCUMFERENCE - homeHighCarbonCircumference
|
} ${
|
||||||
: homeSolarCircumference! +
|
homeHighCarbonCircumference !== undefined
|
||||||
(homeBatteryCircumference || 0)}"
|
? CIRCLE_CIRCUMFERENCE - homeHighCarbonCircumference
|
||||||
|
: homeSolarCircumference! +
|
||||||
|
(homeBatteryCircumference || 0)
|
||||||
|
}"
|
||||||
stroke-dashoffset="0"
|
stroke-dashoffset="0"
|
||||||
shape-rendering="geometricPrecision"
|
shape-rendering="geometricPrecision"
|
||||||
/>
|
/>`
|
||||||
|
: nothing}
|
||||||
</svg>`
|
</svg>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
|
@ -619,15 +631,19 @@ class HuiEnergyDistrubutionCard
|
||||||
d="M55,100 v-15 c0,-35 10,-30 30,-30 h20"
|
d="M55,100 v-15 c0,-35 10,-30 30,-30 h20"
|
||||||
vector-effect="non-scaling-stroke"
|
vector-effect="non-scaling-stroke"
|
||||||
></path>
|
></path>
|
||||||
<path
|
${
|
||||||
id="battery-grid"
|
hasGrid
|
||||||
class=${classMap({
|
? svg`<path
|
||||||
"battery-from-grid": Boolean(batteryFromGrid),
|
id="battery-grid"
|
||||||
"battery-to-grid": Boolean(batteryToGrid),
|
class=${classMap({
|
||||||
})}
|
"battery-from-grid": Boolean(batteryFromGrid),
|
||||||
d="M45,100 v-15 c0,-35 -10,-30 -30,-30 h-20"
|
"battery-to-grid": Boolean(batteryToGrid),
|
||||||
vector-effect="non-scaling-stroke"
|
})}
|
||||||
></path>
|
d="M45,100 v-15 c0,-35 -10,-30 -30,-30 h-20"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
></path>`
|
||||||
|
: nothing
|
||||||
|
}
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${hasBattery && hasSolarProduction
|
${hasBattery && hasSolarProduction
|
||||||
|
@ -638,12 +654,14 @@ class HuiEnergyDistrubutionCard
|
||||||
vector-effect="non-scaling-stroke"
|
vector-effect="non-scaling-stroke"
|
||||||
></path>`
|
></path>`
|
||||||
: ""}
|
: ""}
|
||||||
<path
|
${hasGrid
|
||||||
class="grid"
|
? svg`<path
|
||||||
id="grid"
|
class="grid"
|
||||||
d="M0,${hasBattery ? 50 : hasSolarProduction ? 56 : 53} H100"
|
id="grid"
|
||||||
vector-effect="non-scaling-stroke"
|
d="M0,${hasBattery ? 50 : hasSolarProduction ? 56 : 53} H100"
|
||||||
></path>
|
vector-effect="non-scaling-stroke"
|
||||||
|
></path>`
|
||||||
|
: nothing}
|
||||||
${solarToGrid && this._animate
|
${solarToGrid && this._animate
|
||||||
? svg`<circle
|
? svg`<circle
|
||||||
r="1"
|
r="1"
|
||||||
|
@ -839,6 +857,10 @@ class HuiEnergyDistrubutionCard
|
||||||
.spacer {
|
.spacer {
|
||||||
width: 84px;
|
width: 84px;
|
||||||
}
|
}
|
||||||
|
.grid-spacer {
|
||||||
|
width: 84px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
.circle {
|
.circle {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
|
|
|
@ -418,12 +418,11 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||||
|
|
||||||
.keypad {
|
.keypad {
|
||||||
--keypad-columns: 3;
|
--keypad-columns: 3;
|
||||||
margin-top: 12px;
|
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(var(--keypad-columns), auto);
|
grid-template-columns: repeat(var(--keypad-columns), auto);
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
grid-gap: 24px;
|
grid-gap: 16px;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ import {
|
||||||
attachConditionMediaQueriesListeners,
|
attachConditionMediaQueriesListeners,
|
||||||
checkConditionsMet,
|
checkConditionsMet,
|
||||||
} from "../common/validate-condition";
|
} from "../common/validate-condition";
|
||||||
import { createCardElement } from "../create-element/create-card-element";
|
import { tryCreateCardElement } from "../create-element/create-card-element";
|
||||||
|
import { createErrorCardElement } from "../create-element/create-element-base";
|
||||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -71,10 +72,23 @@ export class HuiCard extends ReactiveElement {
|
||||||
public getGridOptions(): LovelaceGridOptions {
|
public getGridOptions(): LovelaceGridOptions {
|
||||||
const elementOptions = this.getElementGridOptions();
|
const elementOptions = this.getElementGridOptions();
|
||||||
const configOptions = this.getConfigGridOptions();
|
const configOptions = this.getConfigGridOptions();
|
||||||
return {
|
const mergedConfig = {
|
||||||
...elementOptions,
|
...elementOptions,
|
||||||
...configOptions,
|
...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
|
// options provided by the element
|
||||||
|
@ -82,7 +96,9 @@ export class HuiCard extends ReactiveElement {
|
||||||
if (!this._element) return {};
|
if (!this._element) return {};
|
||||||
|
|
||||||
if (this._element.getGridOptions) {
|
if (this._element.getGridOptions) {
|
||||||
return this._element.getGridOptions();
|
const options = this._element.getGridOptions();
|
||||||
|
// Some custom cards might return undefined, so we ensure we return an object
|
||||||
|
return options || {};
|
||||||
}
|
}
|
||||||
if (this._element.getLayoutOptions) {
|
if (this._element.getLayoutOptions) {
|
||||||
// Disabled for now to avoid spamming the console, need to be re-enabled when hui-card performance are fixed
|
// Disabled for now to avoid spamming the console, need to be re-enabled when hui-card performance are fixed
|
||||||
|
@ -119,7 +135,15 @@ export class HuiCard extends ReactiveElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadElement(config: LovelaceCardConfig) {
|
private _loadElement(config: LovelaceCardConfig) {
|
||||||
this._element = createCardElement(config);
|
try {
|
||||||
|
this._element = tryCreateCardElement(config);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const errorMessage = err instanceof Error ? err.message : undefined;
|
||||||
|
this._element = createErrorCardElement({
|
||||||
|
type: "error",
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
this._elementConfig = config;
|
this._elementConfig = config;
|
||||||
if (this.hass) {
|
if (this.hass) {
|
||||||
this._element.hass = this.hass;
|
this._element.hass = this.hass;
|
||||||
|
@ -200,6 +224,7 @@ export class HuiCard extends ReactiveElement {
|
||||||
this._element.preview = this.preview;
|
this._element.preview = this.preview;
|
||||||
// For backwards compatibility
|
// For backwards compatibility
|
||||||
(this._element as any).editMode = this.preview;
|
(this._element as any).editMode = this.preview;
|
||||||
|
fireEvent(this, "card-updated");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(this.config?.type, e);
|
console.error(this.config?.type, e);
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js";
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||||
import type { ErrorCardConfig } from "./types";
|
import type { ErrorCardConfig } from "./types";
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-svg-icon";
|
|
||||||
|
|
||||||
const ERROR_ICONS = {
|
const ERROR_ICONS = {
|
||||||
warning: mdiAlertOutline,
|
warning: mdiAlertOutline,
|
||||||
|
@ -30,9 +30,10 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||||
public getGridOptions(): LovelaceGridOptions {
|
public getGridOptions(): LovelaceGridOptions {
|
||||||
return {
|
return {
|
||||||
columns: 6,
|
columns: 6,
|
||||||
rows: 1,
|
rows: this.preview ? "auto" : 1,
|
||||||
min_rows: 1,
|
min_rows: 1,
|
||||||
min_columns: 6,
|
min_columns: 6,
|
||||||
|
fixed_rows: this.preview,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,17 +46,24 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||||
const error =
|
const error =
|
||||||
this._config?.error ||
|
this._config?.error ||
|
||||||
this.hass?.localize("ui.errors.config.configuration_error");
|
this.hass?.localize("ui.errors.config.configuration_error");
|
||||||
const showTitle = this.hass === undefined || this.hass?.user?.is_admin;
|
const showTitle =
|
||||||
|
this.hass === undefined || this.hass?.user?.is_admin || this.preview;
|
||||||
|
const showMessage = this.preview;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card class="${this.severity} ${showTitle ? "" : "no-title"}">
|
<ha-card class="${this.severity} ${showTitle ? "" : "no-title"}">
|
||||||
<div class="icon">
|
<div class="header">
|
||||||
<slot name="icon">
|
<div class="icon">
|
||||||
<ha-svg-icon .path=${ERROR_ICONS[this.severity]}></ha-svg-icon>
|
<slot name="icon">
|
||||||
</slot>
|
<ha-svg-icon .path=${ERROR_ICONS[this.severity]}></ha-svg-icon>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
${showTitle
|
||||||
|
? html`<div class="title"><slot>${error}</slot></div>`
|
||||||
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
${showTitle
|
${showMessage && this._config?.message
|
||||||
? html`<div class="title"><slot>${error}</slot></div>`
|
? html`<div class="message">${this._config.message}</div>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
|
@ -65,10 +73,6 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||||
ha-card {
|
ha-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
column-gap: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
}
|
||||||
ha-card::after {
|
ha-card::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -81,6 +85,15 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||||
content: "";
|
content: "";
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
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 {
|
.no-title {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
@ -90,13 +103,13 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-weight: var(--ha-font-weight-bold);
|
font-weight: var(--ha-font-weight-bold);
|
||||||
}
|
}
|
||||||
ha-card.warning > .icon {
|
ha-card.warning .icon {
|
||||||
color: var(--warning-color);
|
color: var(--warning-color);
|
||||||
}
|
}
|
||||||
ha-card.warning::after {
|
ha-card.warning::after {
|
||||||
background-color: var(--warning-color);
|
background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
ha-card.error > .icon {
|
ha-card.error .icon {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
ha-card.error::after {
|
ha-card.error::after {
|
||||||
|
|
|
@ -85,7 +85,7 @@ export class HuiBadgeEditMode extends LitElement {
|
||||||
if (this._touchStarted) return;
|
if (this._touchStarted) return;
|
||||||
this._hover = true;
|
this._hover = true;
|
||||||
});
|
});
|
||||||
this.addEventListener("mouseout", () => {
|
this.addEventListener("mouseleave", () => {
|
||||||
this._hover = false;
|
this._hover = false;
|
||||||
});
|
});
|
||||||
this.addEventListener("click", () => {
|
this.addEventListener("click", () => {
|
||||||
|
|
|
@ -71,7 +71,7 @@ export class HuiCardEditMode extends LitElement {
|
||||||
if (this._touchStarted) return;
|
if (this._touchStarted) return;
|
||||||
this._hover = true;
|
this._hover = true;
|
||||||
});
|
});
|
||||||
this.addEventListener("mouseout", () => {
|
this.addEventListener("mouseleave", () => {
|
||||||
this._hover = false;
|
this._hover = false;
|
||||||
});
|
});
|
||||||
this.addEventListener("click", () => {
|
this.addEventListener("click", () => {
|
||||||
|
|
|
@ -297,24 +297,17 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dateRangeChanged(ev) {
|
private _dateRangeChanged(ev) {
|
||||||
const weekStartsOn = firstWeekdayIndex(this.hass.locale);
|
|
||||||
this._startDate = calcDate(
|
this._startDate = calcDate(
|
||||||
ev.detail.value.startDate,
|
ev.detail.value.startDate,
|
||||||
startOfDay,
|
startOfDay,
|
||||||
this.hass.locale,
|
this.hass.locale,
|
||||||
this.hass.config,
|
this.hass.config
|
||||||
{
|
|
||||||
weekStartsOn,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
this._endDate = calcDate(
|
this._endDate = calcDate(
|
||||||
ev.detail.value.endDate,
|
ev.detail.value.endDate,
|
||||||
endOfDay,
|
endOfDay,
|
||||||
this.hass.locale,
|
this.hass.locale,
|
||||||
this.hass.config,
|
this.hass.config
|
||||||
{
|
|
||||||
weekStartsOn,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this._updateCollectionPeriod();
|
this._updateCollectionPeriod();
|
||||||
|
|
|
@ -57,6 +57,7 @@ import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../dialogs/generic/show-dialog-box";
|
} from "../../dialogs/generic/show-dialog-box";
|
||||||
|
import { showMoreInfoDialog } from "../../dialogs/more-info/show-ha-more-info-dialog";
|
||||||
import {
|
import {
|
||||||
QuickBarMode,
|
QuickBarMode,
|
||||||
showQuickBar,
|
showQuickBar,
|
||||||
|
@ -75,9 +76,9 @@ import { getLovelaceStrategy } from "./strategies/get-strategy";
|
||||||
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
|
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
|
||||||
import type { Lovelace } from "./types";
|
import type { Lovelace } from "./types";
|
||||||
import "./views/hui-view";
|
import "./views/hui-view";
|
||||||
import "./views/hui-view-container";
|
|
||||||
import type { HUIView } from "./views/hui-view";
|
import type { HUIView } from "./views/hui-view";
|
||||||
import "./views/hui-view-background";
|
import "./views/hui-view-background";
|
||||||
|
import "./views/hui-view-container";
|
||||||
|
|
||||||
@customElement("hui-root")
|
@customElement("hui-root")
|
||||||
class HUIRoot extends LitElement {
|
class HUIRoot extends LitElement {
|
||||||
|
@ -490,7 +491,16 @@ class HUIRoot extends LitElement {
|
||||||
} else if (searchParams.conversation === "1") {
|
} else if (searchParams.conversation === "1") {
|
||||||
this._clearParam("conversation");
|
this._clearParam("conversation");
|
||||||
this._showVoiceCommandDialog();
|
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, {
|
window.addEventListener("scroll", this._handleWindowScroll, {
|
||||||
passive: true,
|
passive: true,
|
||||||
});
|
});
|
||||||
|
@ -730,6 +740,10 @@ class HUIRoot extends LitElement {
|
||||||
showVoiceCommandDialog(this, this.hass, { pipeline_id: "last_used" });
|
showVoiceCommandDialog(this, this.hass, { pipeline_id: "last_used" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _showMoreInfoDialog(entityId: string): void {
|
||||||
|
showMoreInfoDialog(this, { entityId });
|
||||||
|
}
|
||||||
|
|
||||||
private _handleEnableEditMode(ev: CustomEvent<RequestSelectedDetail>): void {
|
private _handleEnableEditMode(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -62,6 +62,8 @@ export interface LovelaceGridOptions {
|
||||||
min_columns?: number;
|
min_columns?: number;
|
||||||
min_rows?: number;
|
min_rows?: number;
|
||||||
max_rows?: number;
|
max_rows?: number;
|
||||||
|
fixed_rows?: boolean;
|
||||||
|
fixed_columns?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceCard extends HTMLElement {
|
export interface LovelaceCard extends HTMLElement {
|
||||||
|
|
|
@ -5807,8 +5807,8 @@
|
||||||
"provisioned_devices": "Provisioned devices",
|
"provisioned_devices": "Provisioned devices",
|
||||||
"not_ready": "{count} not ready",
|
"not_ready": "{count} not ready",
|
||||||
"nvm_backup": {
|
"nvm_backup": {
|
||||||
"title": "Backup and Restore",
|
"title": "Backup and restore",
|
||||||
"description": "Back up or restore your Z-Wave controller's Non-Volatile Memory (NVM). The NVM contains your network information including paired devices. It's recommended to create a backup before making any major changes to your Z-Wave network.",
|
"description": "Back up or restore your Z-Wave controller's non-volatile memory (NVM). The NVM contains your network information including paired devices. It's recommended to create a backup before making any major changes to your Z-Wave network.",
|
||||||
"download_backup": "Download backup",
|
"download_backup": "Download backup",
|
||||||
"restore_backup": "Restore from backup",
|
"restore_backup": "Restore from backup",
|
||||||
"backup_failed": "Failed to download backup",
|
"backup_failed": "Failed to download backup",
|
||||||
|
@ -6013,9 +6013,9 @@
|
||||||
"default": "Default"
|
"default": "Default"
|
||||||
},
|
},
|
||||||
"network_status": {
|
"network_status": {
|
||||||
"connected": "Connected",
|
"connected": "status: connected",
|
||||||
"connecting": "Connecting",
|
"connecting": "status: connecting",
|
||||||
"unknown": "Unknown"
|
"unknown": "status: unknown"
|
||||||
},
|
},
|
||||||
"add_node": {
|
"add_node": {
|
||||||
"title": "Add a Z-Wave device",
|
"title": "Add a Z-Wave device",
|
||||||
|
|
Loading…
Reference in New Issue