mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-02 14:17:21 +00:00
Compare commits
23 Commits
dev
...
20251127.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38b7bd18bb | ||
|
|
a00e944a35 | ||
|
|
481569804e | ||
|
|
a1d7e270ff | ||
|
|
225ccf1d2f | ||
|
|
4a5e1f9f3f | ||
|
|
b27b7210fd | ||
|
|
acd5181449 | ||
|
|
b6b2d03a80 | ||
|
|
7aee2b7cb7 | ||
|
|
df1914cb7a | ||
|
|
6706d5904d | ||
|
|
221aefd764 | ||
|
|
670057e8e6 | ||
|
|
427e46201c | ||
|
|
fd1240f335 | ||
|
|
aa7670cb59 | ||
|
|
468139229c | ||
|
|
39752f0e3f | ||
|
|
4d850d067f | ||
|
|
bcae64df88 | ||
|
|
690fd5a061 | ||
|
|
ac56c6df9a |
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -36,14 +36,14 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||
uses: github/codeql-action/init@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||
uses: github/codeql-action/autobuild@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -57,4 +57,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
||||
uses: github/codeql-action/analyze@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||
|
||||
2
.github/workflows/nightly.yaml
vendored
2
.github/workflows/nightly.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
|
||||
2
.github/workflows/relative-ci.yaml
vendored
2
.github/workflows/relative-ci.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send bundle stats and build information to RelativeCI
|
||||
uses: relative-ci/agent-action@c45aaa919ef85620af54242a241ac17a8fa35983 # v3.2.1
|
||||
uses: relative-ci/agent-action@feb19ddc698445db27401f1490f6ac182da0816f # v3.2.0
|
||||
with:
|
||||
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
||||
token: ${{ github.token }}
|
||||
|
||||
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
script/release
|
||||
|
||||
- name: Upload release assets
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
with:
|
||||
files: |
|
||||
dist/*.whl
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
# home-assistant/wheels doesn't support SHA pinning
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2025.11.0
|
||||
uses: home-assistant/wheels@2025.10.0
|
||||
with:
|
||||
abi: cp313
|
||||
tag: musllinux_1_2
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
- name: Tar folder
|
||||
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
||||
- name: Upload release asset
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
with:
|
||||
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
||||
|
||||
@@ -137,6 +137,6 @@ jobs:
|
||||
- name: Tar folder
|
||||
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
|
||||
- name: Upload release asset
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
with:
|
||||
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz
|
||||
|
||||
@@ -305,8 +305,9 @@ export class HcMain extends HassElement {
|
||||
await llColl.refresh();
|
||||
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
||||
if (isStrategyDashboard(rawConfig)) {
|
||||
const { generateLovelaceDashboardStrategy } =
|
||||
await import("../../../../src/panels/lovelace/strategies/get-strategy");
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
const config = await generateLovelaceDashboardStrategy(
|
||||
rawConfig,
|
||||
this.hass!
|
||||
@@ -346,8 +347,9 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private async _generateDefaultLovelaceConfig() {
|
||||
const { generateLovelaceDashboardStrategy } =
|
||||
await import("../../../../src/panels/lovelace/strategies/get-strategy");
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceDashboardStrategy(DEFAULT_CONFIG, this.hass!)
|
||||
);
|
||||
|
||||
@@ -88,8 +88,8 @@ class HassioRegistriesDialog extends LitElement {
|
||||
<ha-button
|
||||
?disabled=${Boolean(
|
||||
!this._input.registry ||
|
||||
!this._input.username ||
|
||||
!this._input.password
|
||||
!this._input.username ||
|
||||
!this._input.password
|
||||
)}
|
||||
@click=${this._addNewRegistry}
|
||||
appearance="filled"
|
||||
|
||||
18
package.json
18
package.json
@@ -89,8 +89,8 @@
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@tsparticles/engine": "3.9.1",
|
||||
"@tsparticles/preset-links": "3.2.0",
|
||||
"@vaadin/combo-box": "24.9.6",
|
||||
"@vaadin/vaadin-themable-mixin": "24.9.6",
|
||||
"@vaadin/combo-box": "24.9.5",
|
||||
"@vaadin/vaadin-themable-mixin": "24.9.5",
|
||||
"@vibrant/color": "4.0.0",
|
||||
"@vue/web-component-wrapper": "1.3.0",
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||
@@ -152,13 +152,13 @@
|
||||
"@babel/helper-define-polyfill-provider": "0.6.5",
|
||||
"@babel/plugin-transform-runtime": "7.28.5",
|
||||
"@babel/preset-env": "7.28.5",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.21.7",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.21.6",
|
||||
"@lokalise/node-api": "15.4.0",
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.0.3",
|
||||
"@octokit/rest": "22.0.1",
|
||||
"@rsdoctor/rspack-plugin": "1.3.11",
|
||||
"@rspack/core": "1.6.5",
|
||||
"@rspack/core": "1.6.4",
|
||||
"@rspack/dev-server": "1.1.4",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.22",
|
||||
@@ -178,7 +178,7 @@
|
||||
"@types/tar": "6.1.13",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@vitest/coverage-v8": "4.0.14",
|
||||
"@vitest/coverage-v8": "4.0.13",
|
||||
"babel-loader": "10.0.0",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.3",
|
||||
@@ -194,7 +194,7 @@
|
||||
"eslint-plugin-wc": "3.0.2",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.3.2",
|
||||
"glob": "13.0.0",
|
||||
"glob": "12.0.0",
|
||||
"gulp": "5.0.1",
|
||||
"gulp-brotli": "3.0.0",
|
||||
"gulp-json-transform": "0.5.0",
|
||||
@@ -209,7 +209,7 @@
|
||||
"lodash.template": "4.5.0",
|
||||
"map-stream": "0.0.7",
|
||||
"pinst": "3.0.0",
|
||||
"prettier": "3.7.2",
|
||||
"prettier": "3.6.2",
|
||||
"rspack-manifest-plugin": "5.2.0",
|
||||
"serve": "14.2.5",
|
||||
"sinon": "21.0.0",
|
||||
@@ -217,9 +217,9 @@
|
||||
"terser-webpack-plugin": "5.3.14",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.48.0",
|
||||
"typescript-eslint": "8.47.0",
|
||||
"vite-tsconfig-paths": "5.1.4",
|
||||
"vitest": "4.0.14",
|
||||
"vitest": "4.0.13",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20251029.0"
|
||||
version = "20251127.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
||||
@@ -45,8 +45,9 @@ export const computeFormatFunctions = async (
|
||||
formatEntityAttributeName: FormatEntityAttributeNameFunc;
|
||||
formatEntityName: FormatEntityNameFunc;
|
||||
}> => {
|
||||
const { computeStateDisplay } =
|
||||
await import("../entity/compute_state_display");
|
||||
const { computeStateDisplay } = await import(
|
||||
"../entity/compute_state_display"
|
||||
);
|
||||
const { computeAttributeValueDisplay, computeAttributeNameDisplay } =
|
||||
await import("../entity/compute_attribute_display");
|
||||
|
||||
|
||||
@@ -593,7 +593,6 @@ export class HaChartBase extends LitElement {
|
||||
}
|
||||
const options = {
|
||||
animation: !this._reducedMotion,
|
||||
animationDuration: 500,
|
||||
darkMode: this._themes.darkMode ?? false,
|
||||
aria: { show: true },
|
||||
dataZoom: this._getDataZoomConfig(),
|
||||
|
||||
@@ -167,7 +167,6 @@ export class HaSankeyChart extends LitElement {
|
||||
curveness: 0.5,
|
||||
},
|
||||
layoutIterations: 0,
|
||||
animationDuration: 500,
|
||||
label: {
|
||||
formatter: (params) =>
|
||||
data.nodes.find((node) => node.id === (params.data as Node).id)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import type { VisualMapComponentOption } from "echarts/components";
|
||||
import type { LineSeriesOption } from "echarts/charts";
|
||||
import type { YAXisOption } from "echarts/types/dist/shared";
|
||||
@@ -27,7 +27,6 @@ const safeParseFloat = (value) => {
|
||||
return isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
@customElement("state-history-chart-line")
|
||||
export class StateHistoryChartLine extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -796,6 +795,7 @@ export class StateHistoryChartLine extends LitElement {
|
||||
return Math.abs(value) < 1 ? value : roundingFn(value);
|
||||
}
|
||||
}
|
||||
customElements.define("state-history-chart-line", StateHistoryChartLine);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
|
||||
@@ -838,10 +838,10 @@ export class HaDataTable extends LitElement {
|
||||
} else if (this.sortDirection === "asc") {
|
||||
this.sortDirection = "desc";
|
||||
} else {
|
||||
this.sortDirection = "asc";
|
||||
this.sortDirection = null;
|
||||
}
|
||||
|
||||
this.sortColumn = columnId;
|
||||
this.sortColumn = this.sortDirection === null ? undefined : columnId;
|
||||
|
||||
fireEvent(this, "sorting-changed", {
|
||||
column: columnId,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mdiAlert } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
@@ -17,7 +17,6 @@ import { CLIMATE_HVAC_ACTION_TO_MODE } from "../../data/climate";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-state-icon";
|
||||
|
||||
@customElement("state-badge")
|
||||
export class StateBadge extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
@@ -266,3 +265,5 @@ declare global {
|
||||
"state-badge": StateBadge;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("state-badge", StateBadge);
|
||||
|
||||
@@ -659,7 +659,6 @@ export class HaAssistChat extends LitElement {
|
||||
--markdown-table-border-color: var(--divider-color);
|
||||
--markdown-code-background-color: var(--primary-background-color);
|
||||
--markdown-code-text-color: var(--primary-text-color);
|
||||
--markdown-list-indent: 1rem;
|
||||
&:not(:has(ha-markdown-element)) {
|
||||
min-height: 1lh;
|
||||
min-width: 1lh;
|
||||
|
||||
@@ -21,8 +21,7 @@ export class HaBottomSheet extends LitElement {
|
||||
|
||||
private _isDragging = false;
|
||||
|
||||
private _handleAfterHide(afterHideEvent: Event) {
|
||||
afterHideEvent.stopPropagation();
|
||||
private _handleAfterHide() {
|
||||
this.open = false;
|
||||
const ev = new Event("closed", {
|
||||
bubbles: true,
|
||||
|
||||
@@ -248,7 +248,7 @@ export class HaGenericPicker extends LitElement {
|
||||
});
|
||||
};
|
||||
|
||||
private _hidePicker(ev: Event) {
|
||||
private _hidePicker(ev) {
|
||||
ev.stopPropagation();
|
||||
if (this._newValue) {
|
||||
fireEvent(this, "value-changed", { value: this._newValue });
|
||||
|
||||
@@ -186,6 +186,7 @@ export class HaIcon extends LitElement {
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
fill: currentcolor;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -73,8 +73,6 @@ export class HaLanguagePicker extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ attribute: "native-name", type: Boolean })
|
||||
public nativeName = false;
|
||||
|
||||
@@ -137,7 +135,6 @@ export class HaLanguagePicker extends LitElement {
|
||||
.value=${value}
|
||||
.valueRenderer=${this._valueRenderer}
|
||||
.disabled=${this.disabled}
|
||||
.helper=${this.helper}
|
||||
.getItems=${this._getItems}
|
||||
@value-changed=${this._changed}
|
||||
hide-clear-icon
|
||||
|
||||
@@ -71,7 +71,7 @@ class HaMarkdownElement extends ReactiveElement {
|
||||
if (!this.innerHTML && this.cache) {
|
||||
const key = this._computeCacheKey();
|
||||
if (markdownCache.has(key)) {
|
||||
render(h(unsafeHTML(markdownCache.get(key))), this.renderRoot);
|
||||
render(markdownCache.get(key)!, this.renderRoot);
|
||||
this._resize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +71,13 @@ export class HaMarkdown extends LitElement {
|
||||
color: var(--markdown-link-color, var(--primary-color));
|
||||
}
|
||||
img {
|
||||
background-color: var(--markdown-image-background-color);
|
||||
background-color: rgba(10, 10, 10, 0.15);
|
||||
border-radius: var(--markdown-image-border-radius);
|
||||
max-width: 100%;
|
||||
min-height: 2lh;
|
||||
height: auto;
|
||||
width: auto;
|
||||
text-indent: 4px;
|
||||
transition: height 0.2s ease-in-out;
|
||||
}
|
||||
p:first-child > img:first-child {
|
||||
@@ -84,9 +86,10 @@ export class HaMarkdown extends LitElement {
|
||||
p:first-child > img:last-child {
|
||||
vertical-align: top;
|
||||
}
|
||||
:host > ul,
|
||||
:host > ol {
|
||||
padding-inline-start: var(--markdown-list-indent, revert);
|
||||
ol,
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
li {
|
||||
&:has(input[type="checkbox"]) {
|
||||
@@ -137,19 +140,16 @@ export class HaMarkdown extends LitElement {
|
||||
margin: var(--ha-space-4) 0;
|
||||
}
|
||||
table {
|
||||
border-collapse: var(--markdown-table-border-collapse, collapse);
|
||||
}
|
||||
div:has(> table) {
|
||||
overflow: auto;
|
||||
border-collapse: collapse;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
}
|
||||
th {
|
||||
text-align: start;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
border-width: var(--markdown-table-border-width, 1px);
|
||||
border-style: var(--markdown-table-border-style, solid);
|
||||
border-color: var(--markdown-table-border-color, var(--divider-color));
|
||||
border: 1px solid var(--markdown-table-border-color, transparent);
|
||||
padding: 0.25em 0.5em;
|
||||
}
|
||||
blockquote {
|
||||
|
||||
@@ -103,8 +103,8 @@ export class HaPickerField extends LitElement {
|
||||
--md-list-item-two-line-container-height: 56px;
|
||||
--md-list-item-top-space: 0px;
|
||||
--md-list-item-bottom-space: 0px;
|
||||
--md-list-item-leading-space: var(--ha-space-4);
|
||||
--md-list-item-trailing-space: var(--ha-space-2);
|
||||
--md-list-item-leading-space: 8px;
|
||||
--md-list-item-trailing-space: 8px;
|
||||
--ha-md-list-item-gap: var(--ha-space-2);
|
||||
/* Remove the default focus ring */
|
||||
--md-focus-ring-width: 0px;
|
||||
|
||||
@@ -450,7 +450,7 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
const hasOptional = Boolean(
|
||||
!shouldRenderServiceDataYaml &&
|
||||
serviceData?.flatFields.some((field) => showOptionalToggle(field))
|
||||
serviceData?.flatFields.some((field) => showOptionalToggle(field))
|
||||
);
|
||||
|
||||
const targetEntities = this._getTargetedEntities(
|
||||
@@ -467,7 +467,7 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
const descriptionPlaceholders =
|
||||
domain && serviceName
|
||||
? this.hass.services[domain]?.[serviceName]?.description_placeholders
|
||||
? this.hass.services[domain][serviceName].description_placeholders
|
||||
: undefined;
|
||||
|
||||
const description =
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
mdiDevices,
|
||||
mdiFormatListBulleted,
|
||||
mdiGestureDoubleTap,
|
||||
mdiHomeAssistant,
|
||||
mdiMapMarker,
|
||||
mdiMapMarkerRadius,
|
||||
mdiMessageAlert,
|
||||
@@ -22,7 +23,6 @@ import { customElement, property } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { FALLBACK_DOMAIN_ICONS, triggerIcon } from "../data/icons";
|
||||
import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-icon";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@@ -53,7 +53,6 @@ export type DialogWidth = "small" | "medium" | "large" | "full";
|
||||
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
|
||||
*
|
||||
* @attr {boolean} open - Controls the dialog open state.
|
||||
* @attr {("alert"|"standard")} type - Dialog type. Defaults to "standard".
|
||||
* @attr {("small"|"medium"|"large"|"full")} width - Preferred dialog width preset. Defaults to "medium".
|
||||
* @attr {boolean} prevent-scrim-close - Prevents closing the dialog by clicking the scrim/overlay. Defaults to false.
|
||||
* @attr {string} header-title - Header title text. If not set, the headerTitle slot is used.
|
||||
@@ -85,9 +84,6 @@ export class HaWaDialog extends LitElement {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public open = false;
|
||||
|
||||
@property({ reflect: true })
|
||||
public type: "alert" | "standard" = "standard";
|
||||
|
||||
@property({ type: String, reflect: true, attribute: "width" })
|
||||
public width: DialogWidth = "medium";
|
||||
|
||||
@@ -204,7 +200,18 @@ export class HaWaDialog extends LitElement {
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
wa-dialog {
|
||||
--full-width: var(--ha-dialog-width-full, min(95vw, var(--safe-width)));
|
||||
--full-width: var(
|
||||
--ha-dialog-width-full,
|
||||
min(
|
||||
95vw,
|
||||
calc(
|
||||
100vw - var(--safe-area-inset-left, var(--ha-space-0)) - var(
|
||||
--safe-area-inset-right,
|
||||
var(--ha-space-0)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
|
||||
--spacing: var(--dialog-content-padding, var(--ha-space-6));
|
||||
--show-duration: var(--ha-dialog-show-duration, 200ms);
|
||||
@@ -221,7 +228,8 @@ export class HaWaDialog extends LitElement {
|
||||
--ha-dialog-border-radius,
|
||||
var(--ha-border-radius-3xl)
|
||||
);
|
||||
max-width: var(--ha-dialog-max-width, var(--safe-width));
|
||||
max-width: var(--ha-dialog-max-width, 100vw);
|
||||
max-width: var(--ha-dialog-max-width, 100svw);
|
||||
}
|
||||
|
||||
:host([width="small"]) wa-dialog {
|
||||
@@ -241,57 +249,34 @@ export class HaWaDialog extends LitElement {
|
||||
max-width: var(--width, var(--full-width));
|
||||
max-height: var(
|
||||
--ha-dialog-max-height,
|
||||
calc(var(--safe-height) - var(--ha-space-20))
|
||||
calc(100% - var(--ha-space-20))
|
||||
);
|
||||
min-height: var(--ha-dialog-min-height);
|
||||
position: var(--dialog-surface-position, relative);
|
||||
margin-top: var(--dialog-surface-margin-top, auto);
|
||||
/* Used to offset the dialog from the safe areas when space is limited */
|
||||
transform: translate(
|
||||
calc(
|
||||
var(--safe-area-offset-left, var(--ha-space-0)) - var(
|
||||
--safe-area-offset-right,
|
||||
var(--ha-space-0)
|
||||
)
|
||||
),
|
||||
calc(
|
||||
var(--safe-area-offset-top, var(--ha-space-0)) - var(
|
||||
--safe-area-offset-bottom,
|
||||
var(--ha-space-0)
|
||||
)
|
||||
)
|
||||
);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
:host([type="standard"]) {
|
||||
:host {
|
||||
--ha-dialog-border-radius: var(--ha-space-0);
|
||||
}
|
||||
|
||||
wa-dialog {
|
||||
/* Make the container fill the whole screen width and not the safe width */
|
||||
--full-width: var(--ha-dialog-width-full, 100vw);
|
||||
--width: var(--full-width);
|
||||
}
|
||||
wa-dialog {
|
||||
--full-width: var(--ha-dialog-width-full, 100vw);
|
||||
}
|
||||
|
||||
wa-dialog::part(dialog) {
|
||||
/* Make the dialog fill the whole screen height and not the safe height */
|
||||
min-height: var(--ha-dialog-min-height, 100vh);
|
||||
min-height: var(--ha-dialog-min-height, 100dvh);
|
||||
max-height: var(--ha-dialog-max-height, 100vh);
|
||||
max-height: var(--ha-dialog-max-height, 100dvh);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
/* Use safe area as padding instead of the container size */
|
||||
padding-top: var(--safe-area-inset-top);
|
||||
padding-bottom: var(--safe-area-inset-bottom);
|
||||
padding-left: var(--safe-area-inset-left);
|
||||
padding-right: var(--safe-area-inset-right);
|
||||
/* Reset the transform to center the dialog */
|
||||
transform: none;
|
||||
}
|
||||
wa-dialog::part(dialog) {
|
||||
min-height: var(--ha-dialog-min-height, 100vh);
|
||||
min-height: var(--ha-dialog-min-height, 100svh);
|
||||
max-height: var(--ha-dialog-max-height, 100vh);
|
||||
max-height: var(--ha-dialog-max-height, 100svh);
|
||||
padding-top: var(--safe-area-inset-top, var(--ha-space-0));
|
||||
padding-bottom: var(--safe-area-inset-bottom, var(--ha-space-0));
|
||||
padding-left: var(--safe-area-inset-left, var(--ha-space-0));
|
||||
padding-right: var(--safe-area-inset-right, var(--ha-space-0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { property } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../ha-state-icon";
|
||||
|
||||
@customElement("ha-entity-marker")
|
||||
class HaEntityMarker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -90,6 +89,8 @@ class HaEntityMarker extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
customElements.define("ha-entity-marker", HaEntityMarker);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-entity-marker": HaEntityMarker;
|
||||
|
||||
@@ -144,7 +144,9 @@ const tryDescribeTrigger = (
|
||||
const type = getTriggerObjectId(trigger.trigger);
|
||||
|
||||
return (
|
||||
hass.localize(`component.${domain}.triggers.${type}.name`) ||
|
||||
hass.localize(
|
||||
`component.${domain}.triggers.${type}.description_configured`
|
||||
) ||
|
||||
hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.${triggerType as LegacyTrigger["trigger"]}.label`
|
||||
) ||
|
||||
@@ -917,7 +919,9 @@ const tryDescribeCondition = (
|
||||
const type = getConditionObjectId(condition.condition);
|
||||
|
||||
return (
|
||||
hass.localize(`component.${domain}.conditions.${type}.name`) ||
|
||||
hass.localize(
|
||||
`component.${domain}.conditions.${type}.description_configured`
|
||||
) ||
|
||||
hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.type.${conditionType as LegacyCondition["condition"]}.label`
|
||||
) ||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { AreaRegistryEntry } from "./area_registry";
|
||||
import type { RegistryEntry } from "./registry";
|
||||
@@ -74,3 +75,27 @@ export const getFloorAreaLookup = (
|
||||
}
|
||||
return floorAreaLookup;
|
||||
};
|
||||
|
||||
export const floorCompare =
|
||||
(entries?: HomeAssistant["floors"], order?: string[]) =>
|
||||
(a: string, b: string) => {
|
||||
const indexA = order ? order.indexOf(a) : -1;
|
||||
const indexB = order ? order.indexOf(b) : -1;
|
||||
if (indexA === -1 && indexB === -1) {
|
||||
const floorA = entries?.[a];
|
||||
const floorB = entries?.[b];
|
||||
if (floorA && floorB && floorA.level !== floorB.level) {
|
||||
return (floorB.level ?? -9999) - (floorA.level ?? -9999);
|
||||
}
|
||||
const nameA = floorA?.name ?? a;
|
||||
const nameB = floorB?.name ?? b;
|
||||
return stringCompare(nameA, nameB);
|
||||
}
|
||||
if (indexA === -1) {
|
||||
return 1;
|
||||
}
|
||||
if (indexB === -1) {
|
||||
return -1;
|
||||
}
|
||||
return indexA - indexB;
|
||||
};
|
||||
|
||||
@@ -47,7 +47,8 @@ export interface HassioFullBackupCreateParams {
|
||||
confirm_password?: string;
|
||||
background?: boolean;
|
||||
}
|
||||
export interface HassioPartialBackupCreateParams extends HassioFullBackupCreateParams {
|
||||
export interface HassioPartialBackupCreateParams
|
||||
extends HassioFullBackupCreateParams {
|
||||
folders?: string[];
|
||||
addons?: string[];
|
||||
homeassistant?: boolean;
|
||||
|
||||
@@ -18,7 +18,8 @@ export const enum LawnMowerEntityFeature {
|
||||
}
|
||||
|
||||
interface LawnMowerEntityAttributes
|
||||
extends HassEntityAttributeBase, Record<string, any> {}
|
||||
extends HassEntityAttributeBase,
|
||||
Record<string, any> {}
|
||||
|
||||
export interface LawnMowerEntity extends HassEntityBase {
|
||||
attributes: LawnMowerEntityAttributes;
|
||||
|
||||
@@ -18,7 +18,8 @@ export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {
|
||||
cards?: LovelaceCardConfig[];
|
||||
}
|
||||
|
||||
export interface LovelaceStrategySectionConfig extends LovelaceBaseSectionConfig {
|
||||
export interface LovelaceStrategySectionConfig
|
||||
extends LovelaceBaseSectionConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
|
||||
views: LovelaceViewRawConfig[];
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardStrategyConfig extends LovelaceDashboardBaseConfig {
|
||||
export interface LovelaceDashboardStrategyConfig
|
||||
extends LovelaceDashboardBaseConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ export interface LovelaceDashboardMutableParams {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardCreateParams extends LovelaceDashboardMutableParams {
|
||||
export interface LovelaceDashboardCreateParams
|
||||
extends LovelaceDashboardMutableParams {
|
||||
url_path: string;
|
||||
mode: "storage";
|
||||
}
|
||||
|
||||
@@ -106,7 +106,8 @@ export interface AutomationTrace extends BaseTrace {
|
||||
}
|
||||
|
||||
export interface AutomationTraceExtended
|
||||
extends AutomationTrace, BaseTraceExtended {
|
||||
extends AutomationTrace,
|
||||
BaseTraceExtended {
|
||||
config: ManualAutomationConfig;
|
||||
blueprint_inputs?: BlueprintAutomationConfig;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { mdiAlertOutline, mdiClose } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-button";
|
||||
@@ -65,7 +64,6 @@ class DialogBox extends LitElement {
|
||||
<ha-wa-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
type=${confirmPrompt ? "alert" : "standard"}
|
||||
?prevent-scrim-close=${confirmPrompt}
|
||||
@closed=${this._dialogClosed}
|
||||
aria-labelledby="dialog-box-title"
|
||||
@@ -81,11 +79,7 @@ class DialogBox extends LitElement {
|
||||
></ha-icon-button
|
||||
></slot>`
|
||||
: nothing}
|
||||
<span
|
||||
class=${classMap({ title: true, alert: confirmPrompt })}
|
||||
slot="title"
|
||||
id="dialog-box-title"
|
||||
>
|
||||
<span slot="title" id="dialog-box-title">
|
||||
${this._params.warning
|
||||
? html`<ha-svg-icon
|
||||
.path=${mdiAlertOutline}
|
||||
@@ -205,14 +199,6 @@ class DialogBox extends LitElement {
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
.title.alert {
|
||||
padding: 0 var(--ha-space-2);
|
||||
}
|
||||
@media all and (min-width: 450px) and (min-height: 500px) {
|
||||
.title.alert {
|
||||
padding: 0 var(--ha-space-1);
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ export interface PromptDialogParams extends BaseDialogBoxParams {
|
||||
}
|
||||
|
||||
export interface DialogBoxParams
|
||||
extends ConfirmationDialogParams, PromptDialogParams {
|
||||
extends ConfirmationDialogParams,
|
||||
PromptDialogParams {
|
||||
confirm?: (out?: string) => void;
|
||||
confirmation?: boolean;
|
||||
prompt?: boolean;
|
||||
|
||||
@@ -19,9 +19,8 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export interface HassDialog<
|
||||
T = HASSDomEvents[ValidHassDomEvent],
|
||||
> extends HTMLElement {
|
||||
export interface HassDialog<T = HASSDomEvents[ValidHassDomEvent]>
|
||||
extends HTMLElement {
|
||||
showDialog(params: T);
|
||||
closeDialog?: () => boolean;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import "../../../components/buttons/ha-progress-button";
|
||||
import "../../../components/ha-camera-stream";
|
||||
@@ -9,7 +9,6 @@ import type { HomeAssistant } from "../../../types";
|
||||
import { fileDownload } from "../../../util/file_download";
|
||||
import { showToast } from "../../../util/toast";
|
||||
|
||||
@customElement("more-info-camera")
|
||||
class MoreInfoCamera extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -113,6 +112,8 @@ class MoreInfoCamera extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
customElements.define("more-info-camera", MoreInfoCamera);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"more-info-camera": MoreInfoCamera;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-attribute-icon";
|
||||
@@ -32,7 +32,6 @@ import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||
|
||||
type MainControl = "temperature" | "humidity";
|
||||
|
||||
@customElement("more-info-climate")
|
||||
class MoreInfoClimate extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -568,6 +567,8 @@ class MoreInfoClimate extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("more-info-climate", MoreInfoClimate);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"more-info-climate": MoreInfoClimate;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
||||
import type { GroupEntity } from "../../../data/group";
|
||||
import { computeGroupDomain } from "../../../data/group";
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
importMoreInfoControl,
|
||||
} from "../state_more_info_control";
|
||||
|
||||
@customElement("more-info-group")
|
||||
class MoreInfoGroup extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -107,6 +106,8 @@ class MoreInfoGroup extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("more-info-group", MoreInfoGroup);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"more-info-group": MoreInfoGroup;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiPower, mdiTuneVariant } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-control-select-menu";
|
||||
@@ -15,7 +15,6 @@ import type { HomeAssistant } from "../../../types";
|
||||
import "../components/ha-more-info-control-select-container";
|
||||
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||
|
||||
@customElement("more-info-humidifier")
|
||||
class MoreInfoHumidifier extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -250,6 +249,8 @@ class MoreInfoHumidifier extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("more-info-humidifier", MoreInfoHumidifier);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"more-info-humidifier": MoreInfoHumidifier;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import "../components/ha-spinner";
|
||||
import "../components/ha-button";
|
||||
|
||||
@customElement("ha-init-page")
|
||||
class HaInitPage extends LitElement {
|
||||
@property({ type: Boolean }) public error = false;
|
||||
|
||||
@@ -121,6 +120,8 @@ class HaInitPage extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
customElements.define("ha-init-page", HaInitPage);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-init-page": HaInitPage;
|
||||
|
||||
@@ -128,8 +128,6 @@ class HassSubpage extends LitElement {
|
||||
ha-menu-button,
|
||||
ha-icon-button-arrow-prev,
|
||||
::slotted([slot="toolbar-icon"]) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
pointer-events: auto;
|
||||
color: var(--sidebar-icon-color);
|
||||
}
|
||||
@@ -145,6 +143,7 @@ class HassSubpage extends LitElement {
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@@ -621,9 +621,9 @@ export class HaTabsSubpageDataTable extends KeyboardShortcutMixin(LitElement) {
|
||||
} else if (this._sortDirection === "asc") {
|
||||
this._sortDirection = "desc";
|
||||
} else {
|
||||
this._sortDirection = "asc";
|
||||
this._sortDirection = null;
|
||||
}
|
||||
this._sortColumn = columnId;
|
||||
this._sortColumn = this._sortDirection === null ? undefined : columnId;
|
||||
|
||||
fireEvent(this, "sorting-changed", {
|
||||
column: columnId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import type { LocalizeKeys } from "../common/translations/localize";
|
||||
import "../components/ha-button";
|
||||
import "../components/ha-icon-button";
|
||||
@@ -26,7 +26,6 @@ export interface ToastActionParams {
|
||||
| { translationKey: LocalizeKeys; args?: Record<string, string> };
|
||||
}
|
||||
|
||||
@customElement("notification-manager")
|
||||
class NotificationManager extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -116,6 +115,8 @@ class NotificationManager extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("notification-manager", NotificationManager);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"notification-manager": NotificationManager;
|
||||
|
||||
@@ -90,7 +90,9 @@ class OnboardingRestoreBackupCloudLogin extends LitElement {
|
||||
this._email = this._cloudLoginElement.emailField.value;
|
||||
}
|
||||
|
||||
await import("../../panels/config/cloud/forgot-password/cloud-forgot-password-card");
|
||||
await import(
|
||||
"../../panels/config/cloud/forgot-password/cloud-forgot-password-card"
|
||||
);
|
||||
this._view = "forgot-password";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { TZDate } from "@date-fns/tz";
|
||||
import { addDays, isSameDay } from "date-fns";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { formatDate } from "../../common/datetime/format_date";
|
||||
import { formatDateTime } from "../../common/datetime/format_date_time";
|
||||
import { formatTime } from "../../common/datetime/format_time";
|
||||
@@ -26,7 +26,6 @@ import type { CalendarEventDetailDialogParams } from "./show-dialog-calendar-eve
|
||||
import { showCalendarEventEditDialog } from "./show-dialog-calendar-event-editor";
|
||||
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
|
||||
|
||||
@customElement("dialog-calendar-event-detail")
|
||||
class DialogCalendarEventDetail extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -272,3 +271,8 @@ declare global {
|
||||
"dialog-calendar-event-detail": DialogCalendarEventDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(
|
||||
"dialog-calendar-event-detail",
|
||||
DialogCalendarEventDetail
|
||||
);
|
||||
|
||||
@@ -13,7 +13,6 @@ import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strateg
|
||||
import type { Lovelace } from "../lovelace/types";
|
||||
import "../lovelace/views/hui-view";
|
||||
import "../lovelace/views/hui-view-container";
|
||||
import "../lovelace/views/hui-view-background";
|
||||
|
||||
const CLIMATE_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = {
|
||||
strategy: {
|
||||
@@ -116,7 +115,6 @@ class PanelClimate extends LitElement {
|
||||
this._lovelace
|
||||
? html`
|
||||
<hui-view-container .hass=${this.hass}>
|
||||
<hui-view-background .hass=${this.hass}> </hui-view-background>
|
||||
<hui-view
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
|
||||
@@ -40,7 +40,6 @@ const SENSOR_DOMAINS = ["sensor"];
|
||||
const TEMPERATURE_DEVICE_CLASSES = [SENSOR_DEVICE_CLASS_TEMPERATURE];
|
||||
const HUMIDITY_DEVICE_CLASSES = [SENSOR_DEVICE_CLASS_HUMIDITY];
|
||||
|
||||
@customElement("dialog-area-registry-detail")
|
||||
class DialogAreaDetail extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -427,3 +426,5 @@ declare global {
|
||||
"dialog-area-registry-detail": DialogAreaDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-area-registry-detail", DialogAreaDetail);
|
||||
|
||||
@@ -1,494 +0,0 @@
|
||||
import { mdiClose, mdiDragHorizontalVariant, mdiTextureBox } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import {
|
||||
type AreasFloorHierarchy,
|
||||
getAreasFloorHierarchy,
|
||||
getAreasOrder,
|
||||
getFloorOrder,
|
||||
} from "../../../common/areas/areas-floor-hierarchy";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dialog-header";
|
||||
import "../../../components/ha-floor-icon";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-md-dialog";
|
||||
import type { HaMdDialog } from "../../../components/ha-md-dialog";
|
||||
import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-sortable";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import {
|
||||
reorderAreaRegistryEntries,
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
import { reorderFloorRegistryEntries } from "../../../data/floor_registry";
|
||||
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import type { AreasFloorsOrderDialogParams } from "./show-dialog-areas-floors-order";
|
||||
|
||||
const UNASSIGNED_FLOOR = "__unassigned__";
|
||||
|
||||
interface FloorChange {
|
||||
areaId: string;
|
||||
floorId: string | null;
|
||||
}
|
||||
|
||||
@customElement("dialog-areas-floors-order")
|
||||
class DialogAreasFloorsOrder extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
@state() private _hierarchy?: AreasFloorHierarchy;
|
||||
|
||||
@state() private _saving = false;
|
||||
|
||||
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||
|
||||
public async showDialog(
|
||||
_params: AreasFloorsOrderDialogParams
|
||||
): Promise<void> {
|
||||
this._open = true;
|
||||
this._computeHierarchy();
|
||||
}
|
||||
|
||||
private _computeHierarchy(): void {
|
||||
this._hierarchy = getAreasFloorHierarchy(
|
||||
Object.values(this.hass.floors),
|
||||
Object.values(this.hass.areas)
|
||||
);
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._dialog?.close();
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._open = false;
|
||||
this._hierarchy = undefined;
|
||||
this._saving = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._open || !this._hierarchy) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const dialogTitle = this.hass.localize(
|
||||
"ui.panel.config.areas.dialog.reorder_title"
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-md-dialog open @closed=${this._dialogClosed}>
|
||||
<ha-dialog-header slot="headline">
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
@click=${this.closeDialog}
|
||||
></ha-icon-button>
|
||||
<span slot="title" .title=${dialogTitle}>${dialogTitle}</span>
|
||||
</ha-dialog-header>
|
||||
<div slot="content" class="content">
|
||||
<ha-sortable
|
||||
handle-selector=".floor-handle"
|
||||
draggable-selector=".floor"
|
||||
@item-moved=${this._floorMoved}
|
||||
invert-swap
|
||||
>
|
||||
<div class="floors">
|
||||
${repeat(
|
||||
this._hierarchy.floors,
|
||||
(floor) => floor.id,
|
||||
(floor) => this._renderFloor(floor)
|
||||
)}
|
||||
</div>
|
||||
</ha-sortable>
|
||||
${this._renderUnassignedAreas()}
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this.closeDialog} appearance="plain">
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._save} .disabled=${this._saving}>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-md-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderFloor(floor: { id: string; areas: string[] }) {
|
||||
const floorEntry = this.hass.floors[floor.id];
|
||||
if (!floorEntry) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="floor">
|
||||
<div class="floor-header">
|
||||
<ha-floor-icon .floor=${floorEntry}></ha-floor-icon>
|
||||
<span class="floor-name">${floorEntry.name}</span>
|
||||
<ha-svg-icon
|
||||
class="floor-handle"
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
<ha-sortable
|
||||
handle-selector=".area-handle"
|
||||
draggable-selector="ha-md-list-item"
|
||||
@item-moved=${this._areaMoved}
|
||||
@item-added=${this._areaAdded}
|
||||
group="areas"
|
||||
.floor=${floor.id}
|
||||
>
|
||||
<ha-md-list>
|
||||
${floor.areas.length > 0
|
||||
? floor.areas.map((areaId) => this._renderArea(areaId))
|
||||
: html`<p class="empty">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.dialog.empty_floor"
|
||||
)}
|
||||
</p>`}
|
||||
</ha-md-list>
|
||||
</ha-sortable>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderUnassignedAreas() {
|
||||
const hasFloors = this._hierarchy!.floors.length > 0;
|
||||
|
||||
return html`
|
||||
<div class="floor unassigned">
|
||||
${hasFloors
|
||||
? html`<div class="floor-header">
|
||||
<span class="floor-name">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.dialog.unassigned_areas"
|
||||
)}
|
||||
</span>
|
||||
</div>`
|
||||
: nothing}
|
||||
<ha-sortable
|
||||
handle-selector=".area-handle"
|
||||
draggable-selector="ha-md-list-item"
|
||||
@item-moved=${this._areaMoved}
|
||||
@item-added=${this._areaAdded}
|
||||
group="areas"
|
||||
.floor=${UNASSIGNED_FLOOR}
|
||||
>
|
||||
<ha-md-list>
|
||||
${this._hierarchy!.areas.length > 0
|
||||
? this._hierarchy!.areas.map((areaId) => this._renderArea(areaId))
|
||||
: html`<p class="empty">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.areas.dialog.empty_unassigned"
|
||||
)}
|
||||
</p>`}
|
||||
</ha-md-list>
|
||||
</ha-sortable>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderArea(areaId: string) {
|
||||
const area = this.hass.areas[areaId];
|
||||
if (!area) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-md-list-item .sortableData=${area}>
|
||||
${area.icon
|
||||
? html`<ha-icon slot="start" .icon=${area.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiTextureBox}
|
||||
></ha-svg-icon>`}
|
||||
<span slot="headline">${area.name}</span>
|
||||
<ha-svg-icon
|
||||
class="area-handle"
|
||||
slot="end"
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</ha-md-list-item>
|
||||
`;
|
||||
}
|
||||
|
||||
private _floorMoved(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._hierarchy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { oldIndex, newIndex } = ev.detail;
|
||||
const newFloors = [...this._hierarchy.floors];
|
||||
const [movedFloor] = newFloors.splice(oldIndex, 1);
|
||||
newFloors.splice(newIndex, 0, movedFloor);
|
||||
|
||||
this._hierarchy = {
|
||||
...this._hierarchy,
|
||||
floors: newFloors,
|
||||
};
|
||||
}
|
||||
|
||||
private _areaMoved(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._hierarchy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { floor } = ev.currentTarget as HTMLElement & { floor: string };
|
||||
const { oldIndex, newIndex } = ev.detail;
|
||||
const floorId = floor === UNASSIGNED_FLOOR ? null : floor;
|
||||
|
||||
if (floorId === null) {
|
||||
// Reorder unassigned areas
|
||||
const newAreas = [...this._hierarchy.areas];
|
||||
const [movedArea] = newAreas.splice(oldIndex, 1);
|
||||
newAreas.splice(newIndex, 0, movedArea);
|
||||
|
||||
this._hierarchy = {
|
||||
...this._hierarchy,
|
||||
areas: newAreas,
|
||||
};
|
||||
} else {
|
||||
// Reorder areas within a floor
|
||||
this._hierarchy = {
|
||||
...this._hierarchy,
|
||||
floors: this._hierarchy.floors.map((f) => {
|
||||
if (f.id === floorId) {
|
||||
const newAreas = [...f.areas];
|
||||
const [movedArea] = newAreas.splice(oldIndex, 1);
|
||||
newAreas.splice(newIndex, 0, movedArea);
|
||||
return { ...f, areas: newAreas };
|
||||
}
|
||||
return f;
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private _areaAdded(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._hierarchy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { floor } = ev.currentTarget as HTMLElement & { floor: string };
|
||||
const { data: area, index } = ev.detail as {
|
||||
data: AreaRegistryEntry;
|
||||
index: number;
|
||||
};
|
||||
|
||||
const newFloorId = floor === UNASSIGNED_FLOOR ? null : floor;
|
||||
|
||||
// Update hierarchy
|
||||
const newUnassignedAreas = this._hierarchy.areas.filter(
|
||||
(id) => id !== area.area_id
|
||||
);
|
||||
if (newFloorId === null) {
|
||||
// Add to unassigned at the specified index
|
||||
newUnassignedAreas.splice(index, 0, area.area_id);
|
||||
}
|
||||
|
||||
this._hierarchy = {
|
||||
...this._hierarchy,
|
||||
floors: this._hierarchy.floors.map((f) => {
|
||||
if (f.id === newFloorId) {
|
||||
// Add to new floor at the specified index
|
||||
const newAreas = [...f.areas];
|
||||
newAreas.splice(index, 0, area.area_id);
|
||||
return { ...f, areas: newAreas };
|
||||
}
|
||||
// Remove from old floor
|
||||
return {
|
||||
...f,
|
||||
areas: f.areas.filter((id) => id !== area.area_id),
|
||||
};
|
||||
}),
|
||||
areas: newUnassignedAreas,
|
||||
};
|
||||
}
|
||||
|
||||
private _computeFloorChanges(): FloorChange[] {
|
||||
if (!this._hierarchy) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const changes: FloorChange[] = [];
|
||||
|
||||
// Check areas assigned to floors
|
||||
for (const floor of this._hierarchy.floors) {
|
||||
for (const areaId of floor.areas) {
|
||||
const originalFloorId = this.hass.areas[areaId]?.floor_id ?? null;
|
||||
if (floor.id !== originalFloorId) {
|
||||
changes.push({ areaId, floorId: floor.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check unassigned areas
|
||||
for (const areaId of this._hierarchy.areas) {
|
||||
const originalFloorId = this.hass.areas[areaId]?.floor_id ?? null;
|
||||
if (originalFloorId !== null) {
|
||||
changes.push({ areaId, floorId: null });
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private async _save(): Promise<void> {
|
||||
if (!this._hierarchy || this._saving) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._saving = true;
|
||||
|
||||
try {
|
||||
const areaOrder = getAreasOrder(this._hierarchy);
|
||||
const floorOrder = getFloorOrder(this._hierarchy);
|
||||
|
||||
// Update floor assignments for areas that changed floors
|
||||
const floorChanges = this._computeFloorChanges();
|
||||
const floorChangePromises = floorChanges.map(({ areaId, floorId }) =>
|
||||
updateAreaRegistryEntry(this.hass, areaId, {
|
||||
floor_id: floorId,
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(floorChangePromises);
|
||||
|
||||
// Reorder areas and floors
|
||||
await reorderAreaRegistryEntries(this.hass, areaOrder);
|
||||
await reorderFloorRegistryEntries(this.hass, floorOrder);
|
||||
|
||||
this.closeDialog();
|
||||
} catch (err: any) {
|
||||
showToast(this, {
|
||||
message:
|
||||
err.message ||
|
||||
this.hass.localize("ui.panel.config.areas.dialog.reorder_failed"),
|
||||
});
|
||||
this._saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-md-dialog {
|
||||
min-width: 600px;
|
||||
max-height: 90%;
|
||||
--dialog-content-padding: 8px 24px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 600px), all and (max-height: 500px) {
|
||||
ha-md-dialog {
|
||||
--md-dialog-container-shape: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.floors {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.floor {
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: var(
|
||||
--ha-card-border-radius,
|
||||
var(--ha-border-radius-lg)
|
||||
);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.floor.unassigned {
|
||||
border-style: dashed;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.floor-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background-color: var(--secondary-background-color);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.floor-name {
|
||||
flex: 1;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
|
||||
.floor-handle {
|
||||
cursor: grab;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
ha-md-list {
|
||||
padding: 0;
|
||||
--md-list-item-leading-space: 16px;
|
||||
--md-list-item-trailing-space: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-list-item-one-line-container-height: 48px;
|
||||
--md-list-item-container-shape: 0;
|
||||
}
|
||||
|
||||
ha-md-list-item.sortable-ghost {
|
||||
border-radius: calc(
|
||||
var(--ha-card-border-radius, var(--ha-border-radius-lg)) - 1px
|
||||
);
|
||||
box-shadow: inset 0 0 0 2px var(--primary-color);
|
||||
}
|
||||
|
||||
.area-handle {
|
||||
cursor: grab;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: var(--secondary-text-color);
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
ha-md-list:has(ha-md-list-item) .empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-areas-floors-order": DialogAreasFloorsOrder;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiTextureBox } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
@@ -27,7 +27,6 @@ import type { HomeAssistant } from "../../../types";
|
||||
import { showAreaRegistryDetailDialog } from "./show-dialog-area-registry-detail";
|
||||
import type { FloorRegistryDetailDialogParams } from "./show-dialog-floor-registry-detail";
|
||||
|
||||
@customElement("dialog-floor-registry-detail")
|
||||
class DialogFloorDetail extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -145,10 +144,6 @@ class DialogFloorDetail extends LitElement {
|
||||
"ui.panel.config.floors.editor.level"
|
||||
)}
|
||||
type="number"
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.floors.editor.level_helper"
|
||||
)}
|
||||
helperPersistent
|
||||
></ha-textfield>
|
||||
|
||||
<ha-icon-picker
|
||||
@@ -362,3 +357,5 @@ declare global {
|
||||
"dialog-floor-registry-detail": DialogFloorDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-floor-registry-detail", DialogFloorDetail);
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { ActionDetail } from "@material/mwc-list";
|
||||
import {
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiDragHorizontalVariant,
|
||||
mdiHelpCircle,
|
||||
mdiPencil,
|
||||
mdiPlus,
|
||||
mdiSort,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
@@ -21,6 +21,7 @@ import memoizeOne from "memoize-one";
|
||||
import {
|
||||
getAreasFloorHierarchy,
|
||||
getAreasOrder,
|
||||
getFloorOrder,
|
||||
type AreasFloorHierarchy,
|
||||
} from "../../../common/areas/areas-floor-hierarchy";
|
||||
import { formatListWithAnds } from "../../../common/string/format-list";
|
||||
@@ -41,6 +42,7 @@ import type { FloorRegistryEntry } from "../../../data/floor_registry";
|
||||
import {
|
||||
createFloorRegistryEntry,
|
||||
deleteFloorRegistryEntry,
|
||||
reorderFloorRegistryEntries,
|
||||
updateFloorRegistryEntry,
|
||||
} from "../../../data/floor_registry";
|
||||
import {
|
||||
@@ -56,7 +58,6 @@ import {
|
||||
loadAreaRegistryDetailDialog,
|
||||
showAreaRegistryDetailDialog,
|
||||
} from "./show-dialog-area-registry-detail";
|
||||
import { showAreasFloorsOrderDialog } from "./show-dialog-areas-floors-order";
|
||||
import { showFloorRegistryDetailDialog } from "./show-dialog-floor-registry-detail";
|
||||
|
||||
const UNASSIGNED_FLOOR = "__unassigned__";
|
||||
@@ -175,90 +176,94 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
.route=${this.route}
|
||||
has-fab
|
||||
>
|
||||
<ha-button-menu slot="toolbar-icon">
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-list-item graphic="icon" @click=${this._showReorderDialog}>
|
||||
<ha-svg-icon .path=${mdiSort} slot="graphic"></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.areas.picker.reorder")}
|
||||
</ha-list-item>
|
||||
<ha-list-item graphic="icon" @click=${this._showHelp}>
|
||||
<ha-svg-icon .path=${mdiHelpCircle} slot="graphic"></ha-svg-icon>
|
||||
${this.hass.localize("ui.common.help")}
|
||||
</ha-list-item>
|
||||
</ha-button-menu>
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize("ui.common.help")}
|
||||
.path=${mdiHelpCircle}
|
||||
@click=${this._showHelp}
|
||||
></ha-icon-button>
|
||||
<div class="container">
|
||||
<div class="floors">
|
||||
${this._hierarchy.floors.map(({ areas, id }) => {
|
||||
const floor = this.hass.floors[id];
|
||||
if (!floor) {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<div class="floor">
|
||||
<div class="header">
|
||||
<h2>
|
||||
<ha-floor-icon .floor=${floor}></ha-floor-icon>
|
||||
${floor.name}
|
||||
</h2>
|
||||
<div class="actions">
|
||||
<ha-button-menu
|
||||
.floor=${floor}
|
||||
@action=${this._handleFloorAction}
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-list-item graphic="icon"
|
||||
><ha-svg-icon
|
||||
.path=${mdiPencil}
|
||||
slot="graphic"
|
||||
></ha-svg-icon
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.areas.picker.floor.edit_floor"
|
||||
)}</ha-list-item
|
||||
<ha-sortable
|
||||
handle-selector=".handle"
|
||||
draggable-selector=".floor"
|
||||
@item-moved=${this._floorMoved}
|
||||
.options=${SORT_OPTIONS}
|
||||
group="floors"
|
||||
invert-swap
|
||||
>
|
||||
<div class="floors">
|
||||
${this._hierarchy.floors.map(({ areas, id }) => {
|
||||
const floor = this.hass.floors[id];
|
||||
if (!floor) {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<div class="floor">
|
||||
<div class="header">
|
||||
<h2>
|
||||
<ha-floor-icon .floor=${floor}></ha-floor-icon>
|
||||
${floor.name}
|
||||
</h2>
|
||||
<div class="actions">
|
||||
<ha-svg-icon
|
||||
class="handle"
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
<ha-button-menu
|
||||
.floor=${floor}
|
||||
@action=${this._handleFloorAction}
|
||||
>
|
||||
<ha-list-item class="warning" graphic="icon"
|
||||
><ha-svg-icon
|
||||
class="warning"
|
||||
.path=${mdiDelete}
|
||||
slot="graphic"
|
||||
></ha-svg-icon
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.areas.picker.floor.delete_floor"
|
||||
)}</ha-list-item
|
||||
>
|
||||
</ha-button-menu>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-list-item graphic="icon"
|
||||
><ha-svg-icon
|
||||
.path=${mdiPencil}
|
||||
slot="graphic"
|
||||
></ha-svg-icon
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.areas.picker.floor.edit_floor"
|
||||
)}</ha-list-item
|
||||
>
|
||||
<ha-list-item class="warning" graphic="icon"
|
||||
><ha-svg-icon
|
||||
class="warning"
|
||||
.path=${mdiDelete}
|
||||
slot="graphic"
|
||||
></ha-svg-icon
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.areas.picker.floor.delete_floor"
|
||||
)}</ha-list-item
|
||||
>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
</div>
|
||||
<ha-sortable
|
||||
handle-selector="a"
|
||||
draggable-selector="a"
|
||||
@item-added=${this._areaAdded}
|
||||
@item-moved=${this._areaMoved}
|
||||
group="areas"
|
||||
.options=${SORT_OPTIONS}
|
||||
.floor=${floor.floor_id}
|
||||
>
|
||||
<div class="areas">
|
||||
${areas.map((areaId) => {
|
||||
const area = this.hass.areas[areaId];
|
||||
if (!area) {
|
||||
return nothing;
|
||||
}
|
||||
const stats = areasStats.get(area.area_id);
|
||||
return this._renderArea(area, stats);
|
||||
})}
|
||||
</div>
|
||||
</ha-sortable>
|
||||
</div>
|
||||
<ha-sortable
|
||||
handle-selector="a"
|
||||
draggable-selector="a"
|
||||
@item-added=${this._areaAdded}
|
||||
@item-moved=${this._areaMoved}
|
||||
group="areas"
|
||||
.options=${SORT_OPTIONS}
|
||||
.floor=${floor.floor_id}
|
||||
>
|
||||
<div class="areas">
|
||||
${areas.map((areaId) => {
|
||||
const area = this.hass.areas[areaId];
|
||||
if (!area) {
|
||||
return nothing;
|
||||
}
|
||||
const stats = areasStats.get(area.area_id);
|
||||
return this._renderArea(area, stats);
|
||||
})}
|
||||
</div>
|
||||
</ha-sortable>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</ha-sortable>
|
||||
|
||||
${this._hierarchy.areas.length
|
||||
? html`
|
||||
@@ -390,6 +395,51 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private async _floorMoved(ev) {
|
||||
ev.stopPropagation();
|
||||
if (!this.hass || !this._hierarchy) {
|
||||
return;
|
||||
}
|
||||
const { oldIndex, newIndex } = ev.detail;
|
||||
|
||||
const reorderFloors = (
|
||||
floors: AreasFloorHierarchy["floors"],
|
||||
oldIdx: number,
|
||||
newIdx: number
|
||||
) => {
|
||||
const newFloors = [...floors];
|
||||
const [movedFloor] = newFloors.splice(oldIdx, 1);
|
||||
newFloors.splice(newIdx, 0, movedFloor);
|
||||
return newFloors;
|
||||
};
|
||||
|
||||
// Optimistically update UI
|
||||
this._hierarchy = {
|
||||
...this._hierarchy,
|
||||
floors: reorderFloors(this._hierarchy.floors, oldIndex, newIndex),
|
||||
};
|
||||
|
||||
const areaOrder = getAreasOrder(this._hierarchy);
|
||||
const floorOrder = getFloorOrder(this._hierarchy);
|
||||
|
||||
// Block hierarchy updates for 500ms to avoid flickering
|
||||
// because of multiple async updates
|
||||
this._blockHierarchyUpdateFor(500);
|
||||
|
||||
try {
|
||||
await reorderAreaRegistryEntries(this.hass, areaOrder);
|
||||
await reorderFloorRegistryEntries(this.hass, floorOrder);
|
||||
} catch {
|
||||
showToast(this, {
|
||||
message: this.hass.localize(
|
||||
"ui.panel.config.areas.picker.floor_reorder_failed"
|
||||
),
|
||||
});
|
||||
// Revert on error
|
||||
this._computeHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
private async _areaMoved(ev) {
|
||||
ev.stopPropagation();
|
||||
if (!this.hass || !this._hierarchy) {
|
||||
@@ -552,10 +602,6 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
this._openAreaDialog();
|
||||
}
|
||||
|
||||
private _showReorderDialog() {
|
||||
showAreasFloorsOrderDialog(this, {});
|
||||
}
|
||||
|
||||
private _showHelp() {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.areas.caption"),
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
export interface AreasFloorsOrderDialogParams {}
|
||||
|
||||
export const loadAreasFloorsOrderDialog = () =>
|
||||
import("./dialog-areas-floors-order");
|
||||
|
||||
export const showAreasFloorsOrderDialog = (
|
||||
element: HTMLElement,
|
||||
params: AreasFloorsOrderDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-areas-floors-order",
|
||||
dialogImport: loadAreasFloorsOrderDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
||||
@@ -555,7 +555,8 @@ class DialogAddAutomationElement
|
||||
interactive
|
||||
type="button"
|
||||
class="paste"
|
||||
@click=${this._paste}
|
||||
.value=${PASTE_VALUE}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div class="shortcut-label">
|
||||
<div class="label">
|
||||
@@ -1669,11 +1670,6 @@ class DialogAddAutomationElement
|
||||
});
|
||||
}
|
||||
|
||||
private _paste() {
|
||||
this._params!.add(PASTE_VALUE);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private _selected(ev: CustomEvent<{ value: string }>) {
|
||||
let target: HassServiceTarget | undefined;
|
||||
if (
|
||||
|
||||
@@ -553,6 +553,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
area.icon,
|
||||
] as [string, string, string | undefined, string | undefined];
|
||||
})
|
||||
.sort(([, nameA], [, nameB]) =>
|
||||
stringCompare(nameA, nameB, this.hass.locale.language)
|
||||
)
|
||||
.map(([areaTargetId, areaName, floorId, areaIcon]) => {
|
||||
const { open, devices, entities } =
|
||||
this._entries[`floor${TARGET_SEPARATOR}${floorId || ""}`].areas![
|
||||
|
||||
@@ -124,9 +124,9 @@ export class HaPlatformCondition extends LitElement {
|
||||
|
||||
const hasOptional = Boolean(
|
||||
conditionDesc?.fields &&
|
||||
Object.values(conditionDesc.fields).some((field) =>
|
||||
showOptionalToggle(field)
|
||||
)
|
||||
Object.values(conditionDesc.fields).some((field) =>
|
||||
showOptionalToggle(field)
|
||||
)
|
||||
);
|
||||
|
||||
return html`
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { transform } from "../../../common/decorators/transform";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
@@ -112,7 +112,6 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-automation-editor")
|
||||
export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
KeyboardShortcutMixin(LitElement)
|
||||
) {
|
||||
@@ -1340,3 +1339,5 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-automation-editor", HaAutomationEditor);
|
||||
|
||||
@@ -35,8 +35,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import "../../../components/ha-tooltip";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
hasRejectedItems,
|
||||
@@ -329,19 +327,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
const date = new Date(automation.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const elementId = "last-triggered-" + slugify(automation.entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}>${relativeTime(date, locale)}</span>
|
||||
`}
|
||||
? formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, locale)}
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -160,9 +160,9 @@ export class HaPlatformTrigger extends LitElement {
|
||||
|
||||
const hasOptional = Boolean(
|
||||
triggerDesc?.fields &&
|
||||
Object.values(triggerDesc.fields).some((field) =>
|
||||
showOptionalToggle(field)
|
||||
)
|
||||
Object.values(triggerDesc.fields).some((field) =>
|
||||
showOptionalToggle(field)
|
||||
)
|
||||
);
|
||||
|
||||
return html`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
@@ -13,7 +13,6 @@ import type { WebhookDialogParams } from "./show-dialog-manage-cloudhook";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-copy-textfield";
|
||||
|
||||
@customElement("dialog-manage-cloudhook")
|
||||
export class DialogManageCloudhook extends LitElement {
|
||||
protected hass?: HomeAssistant;
|
||||
|
||||
@@ -156,3 +155,5 @@ declare global {
|
||||
"dialog-manage-cloudhook": DialogManageCloudhook;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-manage-cloudhook", DialogManageCloudhook);
|
||||
|
||||
@@ -1138,20 +1138,23 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
|
||||
if (domains.includes("mqtt")) {
|
||||
const mqtt =
|
||||
await import("./device-detail/integration-elements/mqtt/device-actions");
|
||||
const mqtt = await import(
|
||||
"./device-detail/integration-elements/mqtt/device-actions"
|
||||
);
|
||||
const actions = mqtt.getMQTTDeviceActions(this, device);
|
||||
deviceActions.push(...actions);
|
||||
}
|
||||
if (domains.includes("zha")) {
|
||||
const zha =
|
||||
await import("./device-detail/integration-elements/zha/device-actions");
|
||||
const zha = await import(
|
||||
"./device-detail/integration-elements/zha/device-actions"
|
||||
);
|
||||
const actions = await zha.getZHADeviceActions(this, this.hass, device);
|
||||
deviceActions.push(...actions);
|
||||
}
|
||||
if (domains.includes("zwave_js")) {
|
||||
const zwave =
|
||||
await import("./device-detail/integration-elements/zwave_js/device-actions");
|
||||
const zwave = await import(
|
||||
"./device-detail/integration-elements/zwave_js/device-actions"
|
||||
);
|
||||
const actions = await zwave.getZwaveDeviceActions(
|
||||
this,
|
||||
this.hass,
|
||||
@@ -1160,8 +1163,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
deviceActions.push(...actions);
|
||||
}
|
||||
if (domains.includes("esphome")) {
|
||||
const esphome =
|
||||
await import("./device-detail/integration-elements/esphome/device-actions");
|
||||
const esphome = await import(
|
||||
"./device-detail/integration-elements/esphome/device-actions"
|
||||
);
|
||||
const actions = await esphome.getESPHomeDeviceActions(
|
||||
this,
|
||||
this.hass,
|
||||
@@ -1170,8 +1174,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
deviceActions.push(...actions);
|
||||
}
|
||||
if (domains.includes("matter")) {
|
||||
const matter =
|
||||
await import("./device-detail/integration-elements/matter/device-actions");
|
||||
const matter = await import(
|
||||
"./device-detail/integration-elements/matter/device-actions"
|
||||
);
|
||||
const defaultActions = matter.getMatterDeviceDefaultActions(
|
||||
this,
|
||||
this.hass,
|
||||
@@ -1215,8 +1220,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
).map((int) => int.domain);
|
||||
|
||||
if (domains.includes("zwave_js")) {
|
||||
const zwave =
|
||||
await import("./device-detail/integration-elements/zwave_js/device-alerts");
|
||||
const zwave = await import(
|
||||
"./device-detail/integration-elements/zwave_js/device-alerts"
|
||||
);
|
||||
|
||||
const alerts = await zwave.getZwaveDeviceAlerts(this.hass, device);
|
||||
deviceAlerts.push(...alerts);
|
||||
@@ -1298,7 +1304,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
`);
|
||||
}
|
||||
if (domains.includes("zwave_js")) {
|
||||
import("./device-detail/integration-elements/zwave_js/ha-device-info-zwave_js");
|
||||
import(
|
||||
"./device-detail/integration-elements/zwave_js/ha-device-info-zwave_js"
|
||||
);
|
||||
deviceInfo.push(html`
|
||||
<ha-device-info-zwave_js
|
||||
.hass=${this.hass}
|
||||
@@ -1307,7 +1315,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
`);
|
||||
}
|
||||
if (domains.includes("matter")) {
|
||||
import("./device-detail/integration-elements/matter/ha-device-info-matter");
|
||||
import(
|
||||
"./device-detail/integration-elements/matter/ha-device-info-matter"
|
||||
);
|
||||
deviceInfo.push(html`
|
||||
<ha-device-info-matter
|
||||
.hass=${this.hass}
|
||||
|
||||
@@ -1012,6 +1012,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
? html`<ha-area-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._areaId}
|
||||
.placeholder=${this._device?.area_id}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._areaPicked}
|
||||
></ha-area-picker>`
|
||||
|
||||
@@ -116,10 +116,8 @@ import { showAddIntegrationDialog } from "../integrations/show-add-integration-d
|
||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
|
||||
export interface StateEntity extends Omit<
|
||||
EntityRegistryEntry,
|
||||
"id" | "unique_id"
|
||||
> {
|
||||
export interface StateEntity
|
||||
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
||||
readonly?: boolean;
|
||||
selectable?: boolean;
|
||||
id?: string;
|
||||
|
||||
@@ -530,7 +530,9 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
||||
zha: {
|
||||
tag: "zha-config-dashboard-router",
|
||||
load: () =>
|
||||
import("./integrations/integration-panels/zha/zha-config-dashboard-router"),
|
||||
import(
|
||||
"./integrations/integration-panels/zha/zha-config-dashboard-router"
|
||||
),
|
||||
},
|
||||
mqtt: {
|
||||
tag: "mqtt-config-panel",
|
||||
@@ -540,22 +542,30 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
||||
zwave_js: {
|
||||
tag: "zwave_js-config-router",
|
||||
load: () =>
|
||||
import("./integrations/integration-panels/zwave_js/zwave_js-config-router"),
|
||||
import(
|
||||
"./integrations/integration-panels/zwave_js/zwave_js-config-router"
|
||||
),
|
||||
},
|
||||
matter: {
|
||||
tag: "matter-config-panel",
|
||||
load: () =>
|
||||
import("./integrations/integration-panels/matter/matter-config-panel"),
|
||||
import(
|
||||
"./integrations/integration-panels/matter/matter-config-panel"
|
||||
),
|
||||
},
|
||||
thread: {
|
||||
tag: "thread-config-panel",
|
||||
load: () =>
|
||||
import("./integrations/integration-panels/thread/thread-config-panel"),
|
||||
import(
|
||||
"./integrations/integration-panels/thread/thread-config-panel"
|
||||
),
|
||||
},
|
||||
bluetooth: {
|
||||
tag: "bluetooth-config-dashboard-router",
|
||||
load: () =>
|
||||
import("./integrations/integration-panels/bluetooth/bluetooth-config-dashboard-router"),
|
||||
import(
|
||||
"./integrations/integration-panels/bluetooth/bluetooth-config-dashboard-router"
|
||||
),
|
||||
},
|
||||
dhcp: {
|
||||
tag: "dhcp-config-panel",
|
||||
@@ -570,7 +580,9 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
||||
zeroconf: {
|
||||
tag: "zeroconf-config-panel",
|
||||
load: () =>
|
||||
import("./integrations/integration-panels/zeroconf/zeroconf-config-panel"),
|
||||
import(
|
||||
"./integrations/integration-panels/zeroconf/zeroconf-config-panel"
|
||||
),
|
||||
},
|
||||
application_credentials: {
|
||||
tag: "ha-config-application-credentials",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
@@ -14,7 +14,6 @@ import type {
|
||||
} from "./show-dialog-schedule-block-info";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
|
||||
@customElement("dialog-schedule-block-info")
|
||||
class DialogScheduleBlockInfo extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -157,3 +156,5 @@ declare global {
|
||||
"dialog-schedule-block-info": DialogScheduleBlockInfo;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-schedule-block-info", DialogScheduleBlockInfo);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
@@ -15,7 +15,6 @@ import type { HomeAssistant } from "../../../../../types";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import type { IssueCommandServiceData } from "./types";
|
||||
|
||||
@customElement("zha-cluster-commands")
|
||||
export class ZHAClusterCommands extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@@ -260,3 +259,5 @@ declare global {
|
||||
"zha-cluster-commands": ZHAClusterCommands;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zha-cluster-commands", ZHAClusterCommands);
|
||||
|
||||
@@ -192,9 +192,9 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
private get _canBind(): boolean {
|
||||
return Boolean(
|
||||
this._groupToBind &&
|
||||
this._clustersToBind &&
|
||||
this._clustersToBind?.length > 0 &&
|
||||
this.device
|
||||
this._clustersToBind &&
|
||||
this._clustersToBind?.length > 0 &&
|
||||
this.device
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-button";
|
||||
@@ -12,7 +11,6 @@ import type { HaSwitch } from "../../../components/ha-switch";
|
||||
import "../../../components/ha-switch";
|
||||
import type { BackupConfig } from "../../../data/backup";
|
||||
import { fetchBackupConfig } from "../../../data/backup";
|
||||
import { getSupervisorUpdateConfig } from "../../../data/supervisor/update";
|
||||
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LabsPreviewFeatureEnableDialogParams } from "./show-dialog-labs-preview-feature-enable";
|
||||
@@ -37,10 +35,7 @@ export class DialogLabsPreviewFeatureEnable
|
||||
): Promise<void> {
|
||||
this._params = params;
|
||||
this._createBackup = false;
|
||||
this._fetchBackupConfig();
|
||||
if (isComponentLoaded(this.hass, "hassio")) {
|
||||
this._fetchUpdateBackupConfig();
|
||||
}
|
||||
await this._fetchBackupConfig();
|
||||
}
|
||||
|
||||
public closeDialog(): boolean {
|
||||
@@ -59,21 +54,15 @@ export class DialogLabsPreviewFeatureEnable
|
||||
try {
|
||||
const { config } = await fetchBackupConfig(this.hass);
|
||||
this._backupConfig = config;
|
||||
} catch (err) {
|
||||
// Ignore error, user will get manual backup option
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private async _fetchUpdateBackupConfig() {
|
||||
try {
|
||||
const config = await getSupervisorUpdateConfig(this.hass);
|
||||
this._createBackup = config.core_backup_before_update;
|
||||
} catch (err) {
|
||||
// Ignore error, user can still toggle the switch manually
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
// Default to enabled if automatic backups are configured, disabled otherwise
|
||||
this._createBackup =
|
||||
config.automatic_backups_configured &&
|
||||
!!config.create_backup.password &&
|
||||
config.create_backup.agent_ids.length > 0;
|
||||
} catch {
|
||||
// User will get manual backup option if fetch fails
|
||||
this._createBackup = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -385,10 +385,6 @@ class HaConfigLabs extends SubscribeMixin(LitElement) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a[slot="toolbar-icon"] {
|
||||
color: var(--sidebar-icon-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiClose, mdiContentCopy } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import "../../../components/ha-alert";
|
||||
@@ -26,7 +26,6 @@ import { showToast } from "../../../util/toast";
|
||||
import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
|
||||
import { formatSystemLogTime } from "./util";
|
||||
|
||||
@customElement("dialog-system-log-detail")
|
||||
class DialogSystemLogDetail extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -273,3 +272,5 @@ declare global {
|
||||
"dialog-system-log-detail": DialogSystemLogDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-system-log-detail", DialogSystemLogDetail);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiPencil } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-entities-picker";
|
||||
@@ -43,7 +43,6 @@ const cropOptions: CropOptions = {
|
||||
aspectRatio: 1,
|
||||
};
|
||||
|
||||
@customElement("dialog-person-detail")
|
||||
class DialogPersonDetail extends LitElement implements HassDialog {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -568,3 +567,5 @@ declare global {
|
||||
"dialog-person-detail": DialogPersonDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-person-detail", DialogPersonDetail);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
||||
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
@@ -301,21 +301,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
const date = new Date(scene.state);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const elementId = "last-activated-" + slugify(scene.entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}
|
||||
>${relativeTime(date, this.hass.locale)}</span
|
||||
>
|
||||
`}
|
||||
? formatShortDateTime(date, this.hass.locale, this.hass.config)
|
||||
: relativeTime(date, this.hass.locale)}
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { goBack, navigate } from "../../../common/navigate";
|
||||
@@ -79,7 +79,6 @@ import "./manual-script-editor";
|
||||
import type { HaManualScriptEditor } from "./manual-script-editor";
|
||||
import { showAutomationSaveTimeoutDialog } from "../automation/automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
||||
|
||||
@customElement("ha-script-editor")
|
||||
export class HaScriptEditor extends SubscribeMixin(
|
||||
PreventUnsavedMixin(KeyboardShortcutMixin(LitElement))
|
||||
) {
|
||||
@@ -1279,6 +1278,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-script-editor", HaScriptEditor);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-script-editor": HaScriptEditor;
|
||||
|
||||
@@ -33,8 +33,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import "../../../components/ha-tooltip";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
hasRejectedItems,
|
||||
@@ -304,27 +302,19 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
sortable: true,
|
||||
title: localize("ui.card.automation.last_triggered"),
|
||||
template: (script) => {
|
||||
if (!script.last_triggered) {
|
||||
return this.hass.localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(script.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const elementId = "last-triggered-" + slugify(script.entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}
|
||||
>${relativeTime(date, this.hass.locale)}</span
|
||||
>
|
||||
`}
|
||||
${script.last_triggered
|
||||
? dayDifference > 3
|
||||
? formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: relativeTime(date, this.hass.locale)
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
`;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
|
||||
@customElement("cloud-alexa-pref")
|
||||
export class CloudAlexaPref extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -298,3 +297,5 @@ declare global {
|
||||
"cloud-alexa-pref": CloudAlexaPref;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("cloud-alexa-pref", CloudAlexaPref);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
|
||||
@@ -23,7 +23,6 @@ import type { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import { showSaveSuccessToast } from "../../../util/toast-saved-success";
|
||||
|
||||
@customElement("cloud-google-pref")
|
||||
export class CloudGooglePref extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -368,3 +367,5 @@ declare global {
|
||||
"cloud-google-pref": CloudGooglePref;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("cloud-google-pref", CloudGooglePref);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||
@@ -19,7 +19,6 @@ const SCHEMA = [
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("dialog-home-zone-detail")
|
||||
class DialogHomeZoneDetail extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -152,3 +151,5 @@ declare global {
|
||||
"dialog-home-zone-detail": DialogHomeZoneDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-home-zone-detail", DialogHomeZoneDetail);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord";
|
||||
@@ -14,7 +14,6 @@ import { haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { ZoneDetailDialogParams } from "./show-dialog-zone-detail";
|
||||
|
||||
@customElement("dialog-zone-detail")
|
||||
class DialogZoneDetail extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -242,3 +241,5 @@ declare global {
|
||||
"dialog-zone-detail": DialogZoneDetail;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("dialog-zone-detail", DialogZoneDetail);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { property } from "lit/decorators";
|
||||
import type { NavigateOptions } from "../../common/navigate";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import { deepEqual } from "../../common/util/deep-equal";
|
||||
@@ -22,7 +22,6 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-panel-custom")
|
||||
export class HaPanelCustom extends ReactiveElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@@ -172,3 +171,5 @@ export class HaPanelCustom extends ReactiveElement {
|
||||
iframeDoc.close();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-custom", HaPanelCustom);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import type { HassService } from "home-assistant-js-websocket";
|
||||
import { ERR_CONNECTION_LOST } from "home-assistant-js-websocket";
|
||||
import { dump, load } from "js-yaml";
|
||||
import { load } from "js-yaml";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@@ -137,7 +137,7 @@ class HaPanelDevAction extends LitElement {
|
||||
|
||||
const descriptionPlaceholders =
|
||||
domain && serviceName
|
||||
? this.hass.services[domain]?.[serviceName]?.description_placeholders
|
||||
? this.hass.services[domain][serviceName].description_placeholders
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
@@ -320,10 +320,7 @@ class HaPanelDevAction extends LitElement {
|
||||
${this.hass.localize(
|
||||
`component.${domain}.services.${serviceName}.fields.${field.key}.example`,
|
||||
descriptionPlaceholders
|
||||
) ||
|
||||
(typeof field.example === "object"
|
||||
? html`<pre>${dump(field.example)}</pre>`
|
||||
: field.example)}
|
||||
) || field.example}
|
||||
</td>
|
||||
</tr>`
|
||||
)}
|
||||
|
||||
@@ -547,9 +547,9 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
|
||||
} else if (this._sortDirection === "asc") {
|
||||
this._sortDirection = "desc";
|
||||
} else {
|
||||
this._sortDirection = "asc";
|
||||
this._sortDirection = null;
|
||||
}
|
||||
this._sortColumn = columnId;
|
||||
this._sortColumn = this._sortDirection === null ? undefined : columnId;
|
||||
}
|
||||
|
||||
private _handleGroupBy(ev) {
|
||||
|
||||
@@ -23,10 +23,7 @@ import {
|
||||
getSummedData,
|
||||
} from "../../data/energy";
|
||||
import type { LovelaceConfig } from "../../data/lovelace/config/types";
|
||||
import {
|
||||
isStrategyView,
|
||||
type LovelaceViewConfig,
|
||||
} from "../../data/lovelace/config/view";
|
||||
import type { LovelaceViewConfig } from "../../data/lovelace/config/view";
|
||||
import type { StatisticValue } from "../../data/recorder";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, PanelInfo } from "../../types";
|
||||
@@ -36,7 +33,6 @@ import "../lovelace/hui-root";
|
||||
import type { Lovelace } from "../lovelace/types";
|
||||
import "../lovelace/views/hui-view";
|
||||
import "../lovelace/views/hui-view-container";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
|
||||
export const DEFAULT_ENERGY_COLLECTION_KEY = "energy_dashboard";
|
||||
|
||||
@@ -51,17 +47,15 @@ const OVERVIEW_VIEW = {
|
||||
strategy: {
|
||||
type: "energy-overview",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
allow_compare: false,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const ENERGY_VIEW = {
|
||||
const ELECTRICITY_VIEW = {
|
||||
path: "electricity",
|
||||
back_path: "/energy",
|
||||
strategy: {
|
||||
type: "energy",
|
||||
type: "energy-electricity",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
allow_compare: true,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
@@ -69,19 +63,8 @@ const WATER_VIEW = {
|
||||
back_path: "/energy",
|
||||
path: "water",
|
||||
strategy: {
|
||||
type: "water",
|
||||
type: "energy-water",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
allow_compare: true,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
const POWER_VIEW = {
|
||||
back_path: "/energy",
|
||||
path: "power",
|
||||
strategy: {
|
||||
type: "power",
|
||||
collection_key: DEFAULT_ENERGY_COLLECTION_KEY,
|
||||
allow_compare: false,
|
||||
},
|
||||
} as LovelaceViewConfig;
|
||||
|
||||
@@ -163,15 +146,11 @@ class PanelEnergy extends LitElement {
|
||||
}
|
||||
await this._setLovelace();
|
||||
|
||||
// Check if current path is valid, navigate to first view if not
|
||||
const views = this._lovelace!.config?.views || [];
|
||||
const validPaths = views.map((view) => view.path);
|
||||
// Navigate to first view if not there yet
|
||||
const firstPath = this._lovelace!.config?.views?.[0]?.path;
|
||||
const viewPath: string | undefined = this.route!.path.split("/")[1];
|
||||
if (!viewPath || !validPaths.includes(viewPath)) {
|
||||
navigate(`${this.route!.prefix}/${validPaths[0]}`);
|
||||
} else {
|
||||
// Force hui-root to re-process the route by creating a new route object
|
||||
this.route = { ...this.route! };
|
||||
if (viewPath !== firstPath) {
|
||||
navigate(`${this.route!.prefix}/${firstPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +208,6 @@ class PanelEnergy extends LitElement {
|
||||
views.findIndex((view) => view.path === viewPath),
|
||||
0
|
||||
);
|
||||
const view = views[viewIndex];
|
||||
|
||||
const showBack = this._searchParms.has("historyBack") || viewIndex > 0;
|
||||
|
||||
@@ -259,16 +237,13 @@ class PanelEnergy extends LitElement {
|
||||
`}
|
||||
${!this.narrow
|
||||
? html`<div class="main-title">
|
||||
${this.hass.localize(
|
||||
`ui.panel.energy.title.${viewPath}` as LocalizeKeys
|
||||
) || this.hass.localize("panel.energy")}
|
||||
${this.hass.localize("panel.energy")}
|
||||
</div>`
|
||||
: nothing}
|
||||
|
||||
<hui-energy-period-selector
|
||||
.hass=${this.hass}
|
||||
.collectionKey=${DEFAULT_ENERGY_COLLECTION_KEY}
|
||||
.allowCompare=${isStrategyView(view) && view.strategy.allow_compare}
|
||||
>
|
||||
${this.hass.user?.is_admin
|
||||
? html`
|
||||
@@ -309,35 +284,23 @@ class PanelEnergy extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
const hasEnergy = this._prefs.energy_sources.some((source) =>
|
||||
const isElectricityOnly = this._prefs.energy_sources.every((source) =>
|
||||
["grid", "solar", "battery"].includes(source.type)
|
||||
);
|
||||
|
||||
const hasPower =
|
||||
this._prefs.energy_sources.some(
|
||||
(source) =>
|
||||
(source.type === "solar" && source.stat_rate) ||
|
||||
(source.type === "battery" && source.stat_rate) ||
|
||||
(source.type === "grid" && source.power?.length)
|
||||
) || this._prefs.device_consumption.some((device) => device.stat_rate);
|
||||
if (isElectricityOnly) {
|
||||
return {
|
||||
views: [ELECTRICITY_VIEW],
|
||||
};
|
||||
}
|
||||
|
||||
const hasWater =
|
||||
this._prefs.energy_sources.some((source) => source.type === "water") ||
|
||||
this._prefs.device_consumption_water?.length > 0;
|
||||
|
||||
const views: LovelaceViewConfig[] = [];
|
||||
if (hasEnergy) {
|
||||
views.push(ENERGY_VIEW);
|
||||
}
|
||||
if (hasPower) {
|
||||
views.push(POWER_VIEW);
|
||||
}
|
||||
const views: LovelaceViewConfig[] = [OVERVIEW_VIEW, ELECTRICITY_VIEW];
|
||||
if (hasWater) {
|
||||
views.push(WATER_VIEW);
|
||||
}
|
||||
if (views.length > 1) {
|
||||
views.unshift(OVERVIEW_VIEW);
|
||||
}
|
||||
return { views };
|
||||
}
|
||||
|
||||
@@ -361,7 +324,6 @@ class PanelEnergy extends LitElement {
|
||||
|
||||
const energy_sources = energyData.prefs.energy_sources;
|
||||
const device_consumption = energyData.prefs.device_consumption;
|
||||
const device_consumption_water = energyData.prefs.device_consumption_water;
|
||||
const stats = energyData.state.stats;
|
||||
|
||||
const timeSet = new Set<number>();
|
||||
@@ -547,20 +509,6 @@ class PanelEnergy extends LitElement {
|
||||
|
||||
printCategory("device_consumption", devices, electricUnit);
|
||||
|
||||
if (device_consumption_water) {
|
||||
const waterDevices: string[] = [];
|
||||
device_consumption_water.forEach((source) => {
|
||||
source = source as DeviceConsumptionEnergyPreference;
|
||||
waterDevices.push(source.stat_consumption);
|
||||
});
|
||||
|
||||
printCategory(
|
||||
"device_consumption_water",
|
||||
waterDevices,
|
||||
energyData.state.waterUnit
|
||||
);
|
||||
}
|
||||
|
||||
const { summedData, compareSummedData: _ } = getSummedData(
|
||||
energyData.state
|
||||
);
|
||||
|
||||
@@ -7,8 +7,8 @@ import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
|
||||
@customElement("energy-view-strategy")
|
||||
export class EnergyViewStrategy extends ReactiveElement {
|
||||
@customElement("energy-electricity-view-strategy")
|
||||
export class EnergyElectricityViewStrategy extends ReactiveElement {
|
||||
static async generate(
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
@@ -46,6 +46,15 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
const hasBattery = prefs.energy_sources.some(
|
||||
(source) => source.type === "battery"
|
||||
);
|
||||
const hasPowerSources = prefs.energy_sources.find(
|
||||
(source) =>
|
||||
(source.type === "solar" && source.stat_rate) ||
|
||||
(source.type === "battery" && source.stat_rate) ||
|
||||
(source.type === "grid" && source.power?.length)
|
||||
);
|
||||
const hasPowerDevices = prefs.device_consumption.find(
|
||||
(device) => device.stat_rate
|
||||
);
|
||||
const showFloorsNAreas = !prefs.device_consumption.some(
|
||||
(d) => d.included_in_stat
|
||||
);
|
||||
@@ -55,6 +64,26 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
collection_key: "energy_dashboard",
|
||||
});
|
||||
|
||||
if (hasPowerSources) {
|
||||
if (hasPowerDevices) {
|
||||
view.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sankey_title"),
|
||||
type: "power-sankey",
|
||||
collection_key: collectionKey,
|
||||
group_by_floor: showFloorsNAreas,
|
||||
group_by_area: showFloorsNAreas,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
}
|
||||
view.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sources_graph_title"),
|
||||
type: "power-sources-graph",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
}
|
||||
|
||||
// Only include if we have a grid or battery.
|
||||
if (hasGrid || hasBattery) {
|
||||
view.cards!.push({
|
||||
@@ -161,6 +190,6 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"energy-view-strategy": EnergyViewStrategy;
|
||||
"energy-electricity-view-strategy": EnergyElectricityViewStrategy;
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
const sourceHasCost = (source: Record<string, any>): boolean =>
|
||||
Boolean(
|
||||
source.stat_cost ||
|
||||
source.stat_compensation ||
|
||||
source.entity_energy_price ||
|
||||
source.number_energy_price
|
||||
source.stat_compensation ||
|
||||
source.entity_energy_price ||
|
||||
source.number_energy_price
|
||||
);
|
||||
|
||||
@customElement("energy-overview-view-strategy")
|
||||
@@ -55,10 +55,9 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
const hasBattery = prefs.energy_sources.some(
|
||||
(source) => source.type === "battery"
|
||||
);
|
||||
const hasWaterSources = prefs.energy_sources.some(
|
||||
const hasWater = prefs.energy_sources.some(
|
||||
(source) => source.type === "water"
|
||||
);
|
||||
const hasWaterDevices = prefs.device_consumption_water?.length;
|
||||
const hasPowerSources = prefs.energy_sources.find(
|
||||
(source) =>
|
||||
(source.type === "solar" && source.stat_rate) ||
|
||||
@@ -81,6 +80,21 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
column_span: 24,
|
||||
cards: [],
|
||||
};
|
||||
if (hasPowerSources && hasPowerDevices) {
|
||||
const showFloorsNAreas = !prefs.device_consumption.some(
|
||||
(d) => d.included_in_stat
|
||||
);
|
||||
overviewSection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sankey_title"),
|
||||
type: "power-sankey",
|
||||
collection_key: collectionKey,
|
||||
group_by_floor: showFloorsNAreas,
|
||||
group_by_area: showFloorsNAreas,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Only include if we have a grid or battery.
|
||||
if (hasGrid || hasBattery) {
|
||||
overviewSection.cards!.push({
|
||||
@@ -98,50 +112,12 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
}
|
||||
view.sections!.push(overviewSection);
|
||||
|
||||
const powerSection: LovelaceSectionConfig = {
|
||||
const electricitySection: LovelaceSectionConfig = {
|
||||
type: "grid",
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: hass.localize("ui.panel.energy.title.power"),
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "/energy/power",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
if (hasPowerDevices) {
|
||||
const showFloorsNAreas = !prefs.device_consumption.some(
|
||||
(d) => d.included_in_stat
|
||||
);
|
||||
powerSection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sankey_title"),
|
||||
type: "power-sankey",
|
||||
collection_key: collectionKey,
|
||||
group_by_floor: showFloorsNAreas,
|
||||
group_by_area: showFloorsNAreas,
|
||||
grid_options: {
|
||||
columns: 24,
|
||||
},
|
||||
});
|
||||
powerSection.column_span = 24;
|
||||
view.sections!.push(powerSection);
|
||||
} else if (hasPowerSources) {
|
||||
powerSection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sources_graph_title"),
|
||||
type: "power-sources-graph",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
view.sections!.push(powerSection);
|
||||
}
|
||||
|
||||
const energySection: LovelaceSectionConfig = {
|
||||
type: "grid",
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: hass.localize("ui.panel.energy.title.energy"),
|
||||
heading: hass.localize("ui.panel.energy.overview.electricity"),
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "/energy/electricity",
|
||||
@@ -149,16 +125,15 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
},
|
||||
],
|
||||
};
|
||||
view.sections!.push(energySection);
|
||||
if (hasGrid || hasBattery) {
|
||||
energySection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_usage_graph_title"),
|
||||
type: "energy-usage-graph",
|
||||
collection_key: "energy_dashboard",
|
||||
|
||||
if (hasPowerSources) {
|
||||
electricitySection.cards!.push({
|
||||
type: "power-sources-graph",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
}
|
||||
if (prefs!.device_consumption.length > 3) {
|
||||
energySection.cards!.push({
|
||||
electricitySection.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_top_consumers_title"
|
||||
),
|
||||
@@ -167,15 +142,23 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
max_devices: 3,
|
||||
modes: ["bar"],
|
||||
});
|
||||
} else if (hasGrid) {
|
||||
electricitySection.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_usage_graph_title"),
|
||||
type: "energy-usage-graph",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
}
|
||||
|
||||
view.sections!.push(electricitySection);
|
||||
|
||||
if (hasGas) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: hass.localize("ui.panel.energy.title.gas"),
|
||||
heading: hass.localize("ui.panel.energy.overview.gas"),
|
||||
},
|
||||
{
|
||||
title: hass.localize(
|
||||
@@ -188,33 +171,25 @@ export class EnergyViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasWaterSources || hasWaterDevices) {
|
||||
if (hasWater) {
|
||||
view.sections!.push({
|
||||
type: "grid",
|
||||
cards: [
|
||||
{
|
||||
type: "heading",
|
||||
heading: hass.localize("ui.panel.energy.title.water"),
|
||||
heading: hass.localize("ui.panel.energy.overview.water"),
|
||||
tap_action: {
|
||||
action: "navigate",
|
||||
navigation_path: "/energy/water",
|
||||
},
|
||||
},
|
||||
hasWaterSources
|
||||
? {
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_water_graph_title"
|
||||
),
|
||||
type: "energy-water-graph",
|
||||
collection_key: collectionKey,
|
||||
}
|
||||
: {
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.water_sankey_title"
|
||||
),
|
||||
type: "water-sankey",
|
||||
collection_key: collectionKey,
|
||||
},
|
||||
{
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_water_graph_title"
|
||||
),
|
||||
type: "energy-water-graph",
|
||||
collection_key: collectionKey,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
|
||||
@customElement("water-view-strategy")
|
||||
export class WaterViewStrategy extends ReactiveElement {
|
||||
@customElement("energy-water-view-strategy")
|
||||
export class EnergyWaterViewStrategy extends ReactiveElement {
|
||||
static async generate(
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
@@ -22,24 +22,27 @@ export class WaterViewStrategy extends ReactiveElement {
|
||||
});
|
||||
const prefs = energyCollection.prefs;
|
||||
|
||||
const hasWaterSources = prefs?.energy_sources.some(
|
||||
(source) => source.type === "water"
|
||||
);
|
||||
const hasWaterDevices = prefs?.device_consumption_water?.length;
|
||||
|
||||
// No water sources available
|
||||
if (!prefs || (!hasWaterDevices && !hasWaterSources)) {
|
||||
if (
|
||||
!prefs ||
|
||||
(!prefs.device_consumption_water?.length &&
|
||||
!prefs.energy_sources.some((source) => source.type === "water"))
|
||||
) {
|
||||
return view;
|
||||
}
|
||||
|
||||
view.type = "sidebar";
|
||||
|
||||
const hasWater = prefs.energy_sources.some(
|
||||
(source) => source.type === "water"
|
||||
);
|
||||
|
||||
view.cards!.push({
|
||||
type: "energy-compare",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
|
||||
if (hasWaterSources) {
|
||||
if (hasWater) {
|
||||
view.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.energy_water_graph_title"),
|
||||
type: "energy-water-graph",
|
||||
@@ -47,7 +50,7 @@ export class WaterViewStrategy extends ReactiveElement {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasWaterSources) {
|
||||
if (hasWater) {
|
||||
view.cards!.push({
|
||||
title: hass.localize(
|
||||
"ui.panel.energy.cards.energy_sources_table_title"
|
||||
@@ -59,7 +62,7 @@ export class WaterViewStrategy extends ReactiveElement {
|
||||
}
|
||||
|
||||
// Only include if we have at least 1 water device in the config.
|
||||
if (hasWaterDevices) {
|
||||
if (prefs.device_consumption_water?.length) {
|
||||
const showFloorsNAreas = !prefs.device_consumption_water.some(
|
||||
(d) => d.included_in_stat
|
||||
);
|
||||
@@ -78,6 +81,6 @@ export class WaterViewStrategy extends ReactiveElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"water-view-strategy": WaterViewStrategy;
|
||||
"energy-water-view-strategy": EnergyWaterViewStrategy;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { getEnergyDataCollection } from "../../../data/energy";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||
|
||||
@customElement("power-view-strategy")
|
||||
export class PowerViewStrategy extends ReactiveElement {
|
||||
static async generate(
|
||||
_config: LovelaceStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const view: LovelaceViewConfig = { cards: [] };
|
||||
|
||||
const collectionKey =
|
||||
_config.collection_key || DEFAULT_ENERGY_COLLECTION_KEY;
|
||||
|
||||
const energyCollection = getEnergyDataCollection(hass, {
|
||||
key: collectionKey,
|
||||
});
|
||||
const prefs = energyCollection.prefs;
|
||||
|
||||
const hasPowerSources = prefs?.energy_sources.some(
|
||||
(source) =>
|
||||
(source.type === "solar" && source.stat_rate) ||
|
||||
(source.type === "battery" && source.stat_rate) ||
|
||||
(source.type === "grid" && source.power?.length)
|
||||
);
|
||||
const hasPowerDevices = prefs?.device_consumption.some(
|
||||
(device) => device.stat_rate
|
||||
);
|
||||
|
||||
// No power sources configured
|
||||
if (!prefs || (!hasPowerSources && !hasPowerDevices)) {
|
||||
return view;
|
||||
}
|
||||
|
||||
view.type = "sidebar";
|
||||
|
||||
view.cards!.push({
|
||||
type: "energy-compare",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
|
||||
if (hasPowerDevices) {
|
||||
const showFloorsNAreas = !prefs.device_consumption.some(
|
||||
(d) => d.included_in_stat
|
||||
);
|
||||
view.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sankey_title"),
|
||||
type: "power-sankey",
|
||||
collection_key: collectionKey,
|
||||
group_by_floor: showFloorsNAreas,
|
||||
group_by_area: showFloorsNAreas,
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPowerSources) {
|
||||
view.cards!.push({
|
||||
title: hass.localize("ui.panel.energy.cards.power_sources_graph_title"),
|
||||
type: "power-sources-graph",
|
||||
collection_key: collectionKey,
|
||||
});
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"power-view-strategy": PowerViewStrategy;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
} from "home-assistant-js-websocket/dist/types";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { storage } from "../../common/decorators/storage";
|
||||
@@ -52,7 +52,6 @@ import type { HomeAssistant } from "../../types";
|
||||
import { fileDownload } from "../../util/file_download";
|
||||
import { addEntitiesToLovelaceView } from "../lovelace/editor/add-entities-to-view";
|
||||
|
||||
@customElement("ha-panel-history")
|
||||
class HaPanelHistory extends LitElement {
|
||||
@property({ attribute: false }) hass!: HomeAssistant;
|
||||
|
||||
@@ -680,6 +679,8 @@ class HaPanelHistory extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-history", HaPanelHistory);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-panel-history": HaPanelHistory;
|
||||
|
||||
@@ -13,7 +13,6 @@ import { generateLovelaceViewStrategy } from "../lovelace/strategies/get-strateg
|
||||
import type { Lovelace } from "../lovelace/types";
|
||||
import "../lovelace/views/hui-view";
|
||||
import "../lovelace/views/hui-view-container";
|
||||
import "../lovelace/views/hui-view-background";
|
||||
|
||||
const LIGHT_LOVELACE_VIEW_CONFIG: LovelaceStrategyViewConfig = {
|
||||
strategy: {
|
||||
@@ -116,7 +115,6 @@ class PanelLight extends LitElement {
|
||||
this._lovelace
|
||||
? html`
|
||||
<hui-view-container .hass=${this.hass}>
|
||||
<hui-view-background .hass=${this.hass}> </hui-view-background>
|
||||
<hui-view
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
|
||||
@@ -62,7 +62,9 @@ class HuiAlarmModeCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-alarm-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-alarm-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-alarm-modes-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,9 @@ class HuiAreaControlsCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-area-controls-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-area-controls-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-area-controls-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,9 @@ class HuiClimateFanModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-climate-fan-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-climate-fan-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-climate-fan-modes-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,9 @@ class HuiClimateHvacModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-climate-hvac-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-climate-hvac-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-climate-hvac-modes-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,9 @@ class HuiClimatePresetModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-climate-preset-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-climate-preset-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement(
|
||||
"hui-climate-preset-modes-card-feature-editor"
|
||||
);
|
||||
|
||||
@@ -71,7 +71,9 @@ class HuiClimateSwingHorizontalModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-climate-swing-horizontal-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-climate-swing-horizontal-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement(
|
||||
"hui-climate-swing-horizontal-modes-card-feature-editor"
|
||||
);
|
||||
|
||||
@@ -71,7 +71,9 @@ class HuiClimateSwingModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-climate-swing-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-climate-swing-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement(
|
||||
"hui-climate-swing-modes-card-feature-editor"
|
||||
);
|
||||
|
||||
@@ -79,7 +79,9 @@ class HuiCounterActionsCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-counter-actions-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-counter-actions-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-counter-actions-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,9 @@ class HuiFanPresetModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-fan-preset-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-fan-preset-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-fan-preset-modes-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,9 @@ class HuiHumidifierModesCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-humidifier-modes-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-humidifier-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-humidifier-modes-card-feature-editor");
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,9 @@ class HuiLawnMowerCommandCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-lawn-mower-commands-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-lawn-mower-commands-card-feature-editor"
|
||||
);
|
||||
return document.createElement(
|
||||
"hui-lawn-mower-commands-card-feature-editor"
|
||||
);
|
||||
|
||||
@@ -60,7 +60,9 @@ class HuiMediaPlayerVolumeButtonsCardFeature
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import("../editor/config-elements/hui-media-player-volume-buttons-card-feature-editor");
|
||||
await import(
|
||||
"../editor/config-elements/hui-media-player-volume-buttons-card-feature-editor"
|
||||
);
|
||||
return document.createElement(
|
||||
"hui-media-player-volume-buttons-card-feature-editor"
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user