mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-22 07:39:29 +00:00
Compare commits
57 Commits
20231204.0
...
initial-fo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e98eb8de7f | ||
![]() |
12e6701ffa | ||
![]() |
7167b66719 | ||
![]() |
a52ba5fbd1 | ||
![]() |
22cf903656 | ||
![]() |
d77b657036 | ||
![]() |
4a3038c12c | ||
![]() |
3ac7cd5d4a | ||
![]() |
58eddd2b42 | ||
![]() |
d808da68bd | ||
![]() |
2ed4d1efa0 | ||
![]() |
fcb9e13a84 | ||
![]() |
3ada2f3279 | ||
![]() |
8d2d45ae4e | ||
![]() |
c9e6963387 | ||
![]() |
6d36b0e28c | ||
![]() |
61117bb34f | ||
![]() |
86a3c32844 | ||
![]() |
b855b3e103 | ||
![]() |
80edeebab9 | ||
![]() |
f366e287b1 | ||
![]() |
9ce8684aba | ||
![]() |
caa6ea531c | ||
![]() |
cca1183ee3 | ||
![]() |
0f9c97aea0 | ||
![]() |
8d08aa8c79 | ||
![]() |
eebcab435d | ||
![]() |
76d3c6e237 | ||
![]() |
a820ca1e90 | ||
![]() |
39260d172f | ||
![]() |
e646528b86 | ||
![]() |
86726102fb | ||
![]() |
1330558819 | ||
![]() |
b4ab0fc10b | ||
![]() |
15becf9ef6 | ||
![]() |
ef3785ce9f | ||
![]() |
134e13005d | ||
![]() |
dccd9b2541 | ||
![]() |
de83ad7a7a | ||
![]() |
7e55b9e6b8 | ||
![]() |
1b74ca47bf | ||
![]() |
c3d4be9ceb | ||
![]() |
b8d0c7f7c7 | ||
![]() |
92bce4078f | ||
![]() |
ff8f0697c2 | ||
![]() |
dddba7af00 | ||
![]() |
14a49d6664 | ||
![]() |
22da402d56 | ||
![]() |
aa4acc6572 | ||
![]() |
4d432fba57 | ||
![]() |
97b71c785b | ||
![]() |
8a93284bb3 | ||
![]() |
bb2abe4efc | ||
![]() |
ccada33caf | ||
![]() |
c5f15ee6ba | ||
![]() |
6f240ec681 | ||
![]() |
fc6aef138d |
2
.github/workflows/labeler.yaml
vendored
2
.github/workflows/labeler.yaml
vendored
@@ -10,6 +10,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Apply labels
|
||||
uses: actions/labeler@v4.3.0
|
||||
uses: actions/labeler@v5.0.0
|
||||
with:
|
||||
sync-labels: true
|
||||
|
2
.github/workflows/nightly.yaml
vendored
2
.github/workflows/nightly.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
uses: home-assistant/actions/helpers/verify-version@master
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 90 days stale policy
|
||||
uses: actions/stale@v8.0.0
|
||||
uses: actions/stale@v9.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 90
|
||||
|
@@ -39,7 +39,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
<div class="picker">
|
||||
<div class="label">
|
||||
${this._switching
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
? html`<ha-circular-progress
|
||||
indeterminate
|
||||
></ha-circular-progress>`
|
||||
: until(
|
||||
selectedDemoConfig.then(
|
||||
(conf) => html`
|
||||
|
@@ -0,0 +1,4 @@
|
||||
---
|
||||
title: Circular Progress
|
||||
subtitle: Can be used to indicate an ongoing task.
|
||||
---
|
64
gallery/src/pages/components/ha-circular-progress.ts
Normal file
64
gallery/src/pages/components/ha-circular-progress.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-bar";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "@material/web/progress/circular-progress";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
@customElement("demo-components-ha-circular-progress")
|
||||
export class DemoHaCircularProgress extends LitElement {
|
||||
@property({ attribute: false }) hass!: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<ha-card header="Basic circular progress">
|
||||
<div class="card-content">
|
||||
<ha-circular-progress indeterminate></ha-circular-progress></div
|
||||
></ha-card>
|
||||
<ha-card header="Different circular progress sizes">
|
||||
<div class="card-content">
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
size="tiny"
|
||||
></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
size="small"
|
||||
></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
size="medium"
|
||||
></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
size="large"
|
||||
></ha-circular-progress></div
|
||||
></ha-card>
|
||||
<ha-card header="Circular progress with an aria-label">
|
||||
<div class="card-content">
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
aria-label="Doing something..."
|
||||
></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
.ariaLabel=${"Doing something..."}
|
||||
></ha-circular-progress></div
|
||||
></ha-card>`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-components-ha-circular-progress": DemoHaCircularProgress;
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ class HassioAddonConfigDashboard extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
|
||||
}
|
||||
const hasConfiguration =
|
||||
(this.addon.options && Object.keys(this.addon.options).length) ||
|
||||
|
@@ -34,7 +34,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
|
||||
}
|
||||
return html`
|
||||
<div class="content">
|
||||
|
@@ -22,7 +22,7 @@ class HassioAddonInfoDashboard extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
|
@@ -18,7 +18,9 @@ class HassioAddonLogDashboard extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.addon) {
|
||||
return html` <ha-circular-progress active></ha-circular-progress> `;
|
||||
return html`
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
`;
|
||||
}
|
||||
return html`
|
||||
<div class="content">
|
||||
|
@@ -95,7 +95,7 @@ class HassioBackupDialog
|
||||
</ha-header-bar>
|
||||
</div>
|
||||
${this._restoringBackup
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
|
||||
: html`
|
||||
<supervisor-backup-content
|
||||
.hass=${this.hass}
|
||||
|
@@ -57,7 +57,7 @@ class HassioCreateBackupDialog extends LitElement {
|
||||
)}
|
||||
>
|
||||
${this._creatingBackup
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
|
||||
: html`<supervisor-backup-content
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this._dialogParams.supervisor}
|
||||
|
@@ -71,7 +71,11 @@ class HassioDatadiskDialog extends LitElement {
|
||||
?hideActions=${this.moving}
|
||||
>
|
||||
${this.moving
|
||||
? html` <ha-circular-progress alt="Moving" size="large" active>
|
||||
? html` <ha-circular-progress
|
||||
aria-label="Moving"
|
||||
size="large"
|
||||
indeterminate
|
||||
>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this.dialogParams.supervisor.localize(
|
||||
|
@@ -155,7 +155,11 @@ export class DialogHassioNetwork
|
||||
.disabled=${this._scanning}
|
||||
>
|
||||
${this._scanning
|
||||
? html`<ha-circular-progress active size="small">
|
||||
? html`<ha-circular-progress
|
||||
aria-label="Scanning"
|
||||
indeterminate
|
||||
size="small"
|
||||
>
|
||||
</ha-circular-progress>`
|
||||
: this.supervisor.localize("dialog.network.scan_ap")}
|
||||
</mwc-button>
|
||||
@@ -274,7 +278,7 @@ export class DialogHassioNetwork
|
||||
</mwc-button>
|
||||
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
|
||||
${this._processing
|
||||
? html`<ha-circular-progress active size="small">
|
||||
? html`<ha-circular-progress indeterminate size="small">
|
||||
</ha-circular-progress>`
|
||||
: this.supervisor.localize("common.save")}
|
||||
</mwc-button>
|
||||
|
@@ -158,7 +158,7 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
<mwc-button @click=${this._addRepository}>
|
||||
${this._processing
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
></ha-circular-progress>`
|
||||
: this._dialogParams!.supervisor.localize(
|
||||
|
@@ -174,7 +174,11 @@ class UpdateAvailableCard extends LitElement {
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: html`<ha-circular-progress alt="Updating" size="large" active>
|
||||
: html`<ha-circular-progress
|
||||
aria-label="Updating"
|
||||
size="large"
|
||||
indeterminate
|
||||
>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this.supervisor.localize("update_available.updating", {
|
||||
|
36
package.json
36
package.json
@@ -26,13 +26,13 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.23.5",
|
||||
"@braintree/sanitize-url": "6.0.4",
|
||||
"@braintree/sanitize-url": "7.0.0",
|
||||
"@codemirror/autocomplete": "6.11.1",
|
||||
"@codemirror/commands": "6.3.2",
|
||||
"@codemirror/language": "6.9.3",
|
||||
"@codemirror/legacy-modes": "6.3.3",
|
||||
"@codemirror/search": "6.5.5",
|
||||
"@codemirror/state": "6.3.2",
|
||||
"@codemirror/state": "6.3.3",
|
||||
"@codemirror/view": "6.22.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "6.12.0",
|
||||
@@ -60,7 +60,6 @@
|
||||
"@material/mwc-base": "0.27.0",
|
||||
"@material/mwc-button": "0.27.0",
|
||||
"@material/mwc-checkbox": "0.27.0",
|
||||
"@material/mwc-circular-progress": "0.27.0",
|
||||
"@material/mwc-dialog": "0.27.0",
|
||||
"@material/mwc-drawer": "0.27.0",
|
||||
"@material/mwc-fab": "0.27.0",
|
||||
@@ -91,8 +90,8 @@
|
||||
"@polymer/paper-toast": "3.0.1",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@vaadin/combo-box": "24.2.4",
|
||||
"@vaadin/vaadin-themable-mixin": "24.2.4",
|
||||
"@vaadin/combo-box": "24.2.5",
|
||||
"@vaadin/vaadin-themable-mixin": "24.2.5",
|
||||
"@vibrant/color": "3.2.1-alpha.1",
|
||||
"@vibrant/core": "3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||
@@ -100,7 +99,7 @@
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"app-datepicker": "5.1.1",
|
||||
"chart.js": "4.4.0",
|
||||
"chart.js": "4.4.1",
|
||||
"comlink": "4.4.1",
|
||||
"core-js": "3.33.3",
|
||||
"cropperjs": "1.6.1",
|
||||
@@ -111,7 +110,7 @@
|
||||
"element-internals-polyfill": "1.3.9",
|
||||
"fuse.js": "7.0.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"hls.js": "1.4.12",
|
||||
"hls.js": "1.4.13",
|
||||
"home-assistant-js-websocket": "9.1.0",
|
||||
"idb-keyval": "6.2.1",
|
||||
"intl-messageformat": "10.5.8",
|
||||
@@ -120,14 +119,13 @@
|
||||
"leaflet-draw": "1.0.4",
|
||||
"lit": "2.8.0",
|
||||
"luxon": "3.4.4",
|
||||
"marked": "10.0.0",
|
||||
"marked": "11.0.1",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "0.3.2",
|
||||
"punycode": "2.3.1",
|
||||
"qr-scanner": "1.4.2",
|
||||
"qrcode": "1.5.3",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
"roboto-fontface": "0.10.0",
|
||||
"rrule": "2.8.1",
|
||||
"sortablejs": "1.15.1",
|
||||
@@ -160,7 +158,7 @@
|
||||
"@babel/preset-typescript": "7.23.3",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.8.3",
|
||||
"@koa/cors": "4.0.0",
|
||||
"@lokalise/node-api": "12.0.0",
|
||||
"@lokalise/node-api": "12.1.0",
|
||||
"@octokit/auth-oauth-device": "6.0.1",
|
||||
"@octokit/plugin-retry": "6.0.1",
|
||||
"@octokit/rest": "20.0.2",
|
||||
@@ -178,7 +176,7 @@
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/leaflet": "1.9.8",
|
||||
"@types/leaflet-draw": "1.0.11",
|
||||
"@types/luxon": "3.3.6",
|
||||
"@types/luxon": "3.3.7",
|
||||
"@types/mocha": "10.0.6",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/serve-handler": "6.1.4",
|
||||
@@ -186,18 +184,18 @@
|
||||
"@types/tar": "6.1.10",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.1",
|
||||
"@typescript-eslint/parser": "6.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.2",
|
||||
"@typescript-eslint/parser": "6.13.2",
|
||||
"@web/dev-server": "0.1.38",
|
||||
"@web/dev-server-rollup": "0.4.1",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"chai": "4.3.10",
|
||||
"del": "7.1.0",
|
||||
"eslint": "8.54.0",
|
||||
"eslint": "8.55.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "17.1.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-import-resolver-webpack": "0.13.8",
|
||||
"eslint-plugin-disable": "2.0.3",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
@@ -206,7 +204,7 @@
|
||||
"eslint-plugin-unused-imports": "3.0.0",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"fs-extra": "11.2.0",
|
||||
"glob": "10.3.10",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-flatmap": "1.0.2",
|
||||
@@ -218,7 +216,7 @@
|
||||
"husky": "8.0.3",
|
||||
"instant-mocha": "1.5.2",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "15.1.0",
|
||||
"lint-staged": "15.2.0",
|
||||
"lit-analyzer": "2.0.1",
|
||||
"lodash.template": "4.5.0",
|
||||
"magic-string": "0.30.5",
|
||||
@@ -231,7 +229,7 @@
|
||||
"rollup": "2.79.1",
|
||||
"rollup-plugin-string": "3.0.0",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rollup-plugin-visualizer": "5.9.3",
|
||||
"rollup-plugin-visualizer": "5.10.0",
|
||||
"serve-handler": "6.1.5",
|
||||
"sinon": "17.0.1",
|
||||
"source-map-url": "0.4.1",
|
||||
@@ -239,7 +237,7 @@
|
||||
"tar": "6.2.0",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-lit-plugin": "2.0.1",
|
||||
"typescript": "5.3.2",
|
||||
"typescript": "5.3.3",
|
||||
"vinyl-buffer": "1.0.1",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
"webpack": "5.89.0",
|
||||
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20231204.0"
|
||||
version = "20231208.2"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
@@ -63,6 +63,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
ha-authorize ha-alert {
|
||||
display: block;
|
||||
margin: 16px 0;
|
||||
background-color: var(--primary-background-color, #fafafa);
|
||||
}
|
||||
</style>
|
||||
<ha-alert alert-type="error"
|
||||
@@ -93,6 +94,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin: 16px 0;
|
||||
background-color: var(--primary-background-color, #fafafa);
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { HassConfig, HassEntity } from "home-assistant-js-websocket";
|
||||
import {
|
||||
DOMAIN_ATTRIBUTES_FORMATERS,
|
||||
DOMAIN_ATTRIBUTES_UNITS,
|
||||
TEMPERATURE_ATTRIBUTES,
|
||||
} from "../../data/entity_attributes";
|
||||
@@ -14,11 +15,10 @@ import { formatNumber } from "../number/format_number";
|
||||
import { capitalizeFirstLetter } from "../string/capitalize-first-letter";
|
||||
import { isDate } from "../string/is_date";
|
||||
import { isTimestamp } from "../string/is_timestamp";
|
||||
import { blankBeforePercent } from "../translations/blank_before_percent";
|
||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { computeStateDomain } from "./compute_state_domain";
|
||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||
|
||||
export const computeAttributeValueDisplay = (
|
||||
localize: LocalizeFunc,
|
||||
@@ -39,19 +39,18 @@ export const computeAttributeValueDisplay = (
|
||||
|
||||
// Number value, return formatted number
|
||||
if (typeof attributeValue === "number") {
|
||||
const formattedValue = formatNumber(attributeValue, locale);
|
||||
|
||||
const domain = computeStateDomain(stateObj);
|
||||
|
||||
const formatter = DOMAIN_ATTRIBUTES_FORMATERS[domain]?.[attribute];
|
||||
|
||||
const formattedValue = formatter
|
||||
? formatter(attributeValue, locale)
|
||||
: formatNumber(attributeValue, locale);
|
||||
|
||||
let unit = DOMAIN_ATTRIBUTES_UNITS[domain]?.[attribute] as
|
||||
| string
|
||||
| undefined;
|
||||
|
||||
if (domain === "light" && attribute === "brightness") {
|
||||
const percentage = Math.round((attributeValue / 255) * 100);
|
||||
return `${percentage}${blankBeforePercent(locale)}%`;
|
||||
}
|
||||
|
||||
if (domain === "weather") {
|
||||
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ export class HaProgressButton extends LitElement {
|
||||
? html`
|
||||
<ha-circular-progress
|
||||
size="small"
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
`
|
||||
: ""}
|
||||
|
@@ -469,6 +469,7 @@ export class HaChartBase extends LitElement {
|
||||
.chartTooltip li {
|
||||
display: flex;
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
align-items: center;
|
||||
line-height: 16px;
|
||||
padding: 4px 0;
|
||||
@@ -476,6 +477,7 @@ export class HaChartBase extends LitElement {
|
||||
.chartTooltip .title {
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
word-break: break-word;
|
||||
direction: ltr;
|
||||
}
|
||||
.chartTooltip .footer {
|
||||
|
@@ -10,7 +10,6 @@ import {
|
||||
ScorableTextItem,
|
||||
fuzzyFilterSort,
|
||||
} from "../../common/string/filter/sequence-matching";
|
||||
import { AreaRegistryEntry } from "../../data/area_registry";
|
||||
import {
|
||||
DeviceEntityDisplayLookup,
|
||||
DeviceRegistryEntry,
|
||||
@@ -102,7 +101,7 @@ export class HaDevicePicker extends LitElement {
|
||||
private _getDevices = memoizeOne(
|
||||
(
|
||||
devices: DeviceRegistryEntry[],
|
||||
areas: AreaRegistryEntry[],
|
||||
areas: HomeAssistant["areas"],
|
||||
entities: EntityRegistryDisplayEntry[],
|
||||
includeDomains: this["includeDomains"],
|
||||
excludeDomains: this["excludeDomains"],
|
||||
@@ -133,8 +132,6 @@ export class HaDevicePicker extends LitElement {
|
||||
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
|
||||
}
|
||||
|
||||
const areaLookup = areas;
|
||||
|
||||
let inputDevices = devices.filter(
|
||||
(device) => device.id === this.value || !device.disabled_by
|
||||
);
|
||||
@@ -224,8 +221,8 @@ export class HaDevicePicker extends LitElement {
|
||||
id: device.id,
|
||||
name: name,
|
||||
area:
|
||||
device.area_id && areaLookup[device.area_id]
|
||||
? areaLookup[device.area_id].name
|
||||
device.area_id && areas[device.area_id]
|
||||
? areas[device.area_id].name
|
||||
: this.hass.localize("ui.components.device-picker.no_area"),
|
||||
strings: [name || ""],
|
||||
};
|
||||
@@ -267,7 +264,7 @@ export class HaDevicePicker extends LitElement {
|
||||
this._init = true;
|
||||
const devices = this._getDevices(
|
||||
Object.values(this.hass.devices),
|
||||
Object.values(this.hass.areas),
|
||||
this.hass.areas,
|
||||
Object.values(this.hass.entities),
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
|
@@ -1,54 +1,44 @@
|
||||
import { CircularProgress } from "@material/mwc-circular-progress";
|
||||
import { CSSResultGroup, css } from "lit";
|
||||
import "element-internals-polyfill";
|
||||
import { MdCircularProgress } from "@material/web/progress/circular-progress";
|
||||
import { CSSResult, PropertyValues, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-circular-progress")
|
||||
// @ts-ignore
|
||||
export class HaCircularProgress extends CircularProgress {
|
||||
@property({ type: Boolean })
|
||||
public active = false;
|
||||
export class HaCircularProgress extends MdCircularProgress {
|
||||
@property({ attribute: "aria-label", type: String }) public ariaLabel =
|
||||
"Loading";
|
||||
|
||||
@property()
|
||||
public alt = "Loading";
|
||||
@property() public size: "tiny" | "small" | "medium" | "large" = "medium";
|
||||
|
||||
@property()
|
||||
public size: "tiny" | "small" | "medium" | "large" = "medium";
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
// @ts-ignore
|
||||
public set density(_) {
|
||||
// just a dummy
|
||||
}
|
||||
|
||||
public get density() {
|
||||
switch (this.size) {
|
||||
case "tiny":
|
||||
return -8;
|
||||
case "small":
|
||||
return -5;
|
||||
case "medium":
|
||||
return 0;
|
||||
case "large":
|
||||
return 5;
|
||||
default:
|
||||
return 0;
|
||||
if (changedProps.has("size")) {
|
||||
switch (this.size) {
|
||||
case "tiny":
|
||||
this.style.setProperty("--md-circular-progress-size", "16px");
|
||||
break;
|
||||
case "small":
|
||||
this.style.setProperty("--md-circular-progress-size", "28px");
|
||||
break;
|
||||
// medium is default size
|
||||
case "medium":
|
||||
this.style.setProperty("--md-circular-progress-size", "48px");
|
||||
break;
|
||||
case "large":
|
||||
this.style.setProperty("--md-circular-progress-size", "68px");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
public set indeterminate(_) {
|
||||
// just a dummy
|
||||
}
|
||||
|
||||
public get indeterminate() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
super.styles,
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
overflow: hidden;
|
||||
--md-sys-color-primary: var(--primary-color);
|
||||
--md-circular-progress-size: 48px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -2,6 +2,7 @@ import {
|
||||
DIRECTION_ALL,
|
||||
Manager,
|
||||
Pan,
|
||||
Press,
|
||||
Tap,
|
||||
TouchMouseInput,
|
||||
} from "@egjs/hammerjs";
|
||||
@@ -108,6 +109,9 @@ export class HaControlCircularSlider extends LitElement {
|
||||
@property({ type: Number })
|
||||
public max = 100;
|
||||
|
||||
@property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
|
||||
public preventInteractionOnScroll?: boolean;
|
||||
|
||||
@state()
|
||||
public _localValue?: number = this.value;
|
||||
|
||||
@@ -246,16 +250,62 @@ export class HaControlCircularSlider extends LitElement {
|
||||
this._mc = new Manager(this._interaction, {
|
||||
inputClass: TouchMouseInput,
|
||||
});
|
||||
|
||||
const pressToActivate =
|
||||
this.preventInteractionOnScroll && "ontouchstart" in window;
|
||||
|
||||
// If press to activate is true, a 60ms press is required to activate the slider
|
||||
this._mc.add(
|
||||
new Pan({
|
||||
direction: DIRECTION_ALL,
|
||||
enable: true,
|
||||
threshold: 0,
|
||||
new Press({
|
||||
enable: pressToActivate,
|
||||
pointers: 1,
|
||||
time: 60,
|
||||
})
|
||||
);
|
||||
|
||||
const panRecognizer = new Pan({
|
||||
direction: DIRECTION_ALL,
|
||||
enable: !pressToActivate,
|
||||
threshold: 0,
|
||||
});
|
||||
|
||||
this._mc.add(panRecognizer);
|
||||
|
||||
this._mc.add(new Tap({ event: "singletap" }));
|
||||
|
||||
this._mc.on("press", (e) => {
|
||||
e.srcEvent.stopPropagation();
|
||||
e.srcEvent.preventDefault();
|
||||
if (this.disabled || this.readonly) return;
|
||||
const percentage = this._getPercentageFromEvent(e);
|
||||
const raw = this._percentageToValue(percentage);
|
||||
this._activeSlider = this._findActiveSlider(raw);
|
||||
const bounded = this._boundedValue(raw);
|
||||
this._setActiveValue(bounded);
|
||||
const stepped = this._steppedValue(bounded);
|
||||
if (this._activeSlider) {
|
||||
fireEvent(this, `${this._activeSlider}-changing`, { value: stepped });
|
||||
}
|
||||
panRecognizer.set({ enable: true });
|
||||
});
|
||||
|
||||
this._mc.on("pressup", (e) => {
|
||||
e.srcEvent.stopPropagation();
|
||||
e.srcEvent.preventDefault();
|
||||
const percentage = this._getPercentageFromEvent(e);
|
||||
const raw = this._percentageToValue(percentage);
|
||||
const bounded = this._boundedValue(raw);
|
||||
const stepped = this._steppedValue(bounded);
|
||||
this._setActiveValue(stepped);
|
||||
if (this._activeSlider) {
|
||||
fireEvent(this, `${this._activeSlider}-changing`, {
|
||||
value: undefined,
|
||||
});
|
||||
fireEvent(this, `${this._activeSlider}-changed`, { value: stepped });
|
||||
}
|
||||
this._activeSlider = undefined;
|
||||
});
|
||||
|
||||
this._mc.on("pan", (e) => {
|
||||
e.srcEvent.stopPropagation();
|
||||
e.srcEvent.preventDefault();
|
||||
@@ -271,6 +321,9 @@ export class HaControlCircularSlider extends LitElement {
|
||||
this._mc.on("pancancel", () => {
|
||||
if (this.disabled || this.readonly) return;
|
||||
this._activeSlider = undefined;
|
||||
if (pressToActivate) {
|
||||
panRecognizer.set({ enable: false });
|
||||
}
|
||||
});
|
||||
this._mc.on("panmove", (e) => {
|
||||
if (this.disabled || this.readonly) return;
|
||||
@@ -297,6 +350,9 @@ export class HaControlCircularSlider extends LitElement {
|
||||
fireEvent(this, `${this._activeSlider}-changed`, { value: stepped });
|
||||
}
|
||||
this._activeSlider = undefined;
|
||||
if (pressToActivate) {
|
||||
panRecognizer.set({ enable: false });
|
||||
}
|
||||
});
|
||||
this._mc.on("singletap", (e) => {
|
||||
if (this.disabled || this.readonly) return;
|
||||
@@ -315,6 +371,9 @@ export class HaControlCircularSlider extends LitElement {
|
||||
this._lastSlider = this._activeSlider;
|
||||
this.shadowRoot?.getElementById("#slider")?.focus();
|
||||
this._activeSlider = undefined;
|
||||
if (pressToActivate) {
|
||||
panRecognizer.set({ enable: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -618,6 +677,7 @@ export class HaControlCircularSlider extends LitElement {
|
||||
--control-circular-slider-high-color: var(
|
||||
--control-circular-slider-color
|
||||
);
|
||||
--control-circular-slider-interaction-margin: 12px;
|
||||
width: 320px;
|
||||
display: block;
|
||||
}
|
||||
@@ -633,7 +693,9 @@ export class HaControlCircularSlider extends LitElement {
|
||||
fill: none;
|
||||
stroke: transparent;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 48px;
|
||||
stroke-width: calc(
|
||||
24px + 2 * var(--control-circular-slider-interaction-margin)
|
||||
);
|
||||
cursor: pointer;
|
||||
}
|
||||
#display {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||
import { mdiMinus, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
CSSResultGroup,
|
||||
@@ -49,6 +50,13 @@ export class HaControlNumberButton extends LitElement {
|
||||
|
||||
@query("#input") _input!: HTMLDivElement;
|
||||
|
||||
private _hideUnit = new ResizeController(this, {
|
||||
callback: (entries) => {
|
||||
const width = entries[0]?.contentRect.width;
|
||||
return width < 100;
|
||||
},
|
||||
});
|
||||
|
||||
private boundedValue(value: number) {
|
||||
const clamped = conditionalClamp(value, this.min, this.max);
|
||||
return Math.round(clamped / this._step) * this._step;
|
||||
@@ -145,7 +153,10 @@ export class HaControlNumberButton extends LitElement {
|
||||
?disabled=${this.disabled}
|
||||
@keydown=${this._handleKeyDown}
|
||||
>
|
||||
${value} ${unit ? html`<span class="unit">${unit}</span>` : nothing}
|
||||
${value}
|
||||
${unit && !this._hideUnit.value
|
||||
? html`<span class="unit">${unit}</span>`
|
||||
: nothing}
|
||||
</div>
|
||||
<button
|
||||
class="button minus"
|
||||
|
@@ -6,6 +6,11 @@ import { MdOutlinedIconButton } from "@material/web/iconbutton/outlined-icon-but
|
||||
@customElement("ha-outlined-icon-button")
|
||||
export class HaOutlinedIconButton extends MdOutlinedIconButton {
|
||||
static override styles = [
|
||||
css`
|
||||
.icon-button {
|
||||
border-radius: var(--_container-shape);
|
||||
}
|
||||
`,
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
|
@@ -30,9 +30,9 @@ export class HaNumberSelector extends LitElement {
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
if (changedProps.has("value")) {
|
||||
if (this.value !== Number(this._valueStr)) {
|
||||
if (this._valueStr === "" || this.value !== Number(this._valueStr)) {
|
||||
this._valueStr =
|
||||
!this.value || isNaN(this.value) ? "" : this.value.toString();
|
||||
this.value == null || isNaN(this.value) ? "" : this.value.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -597,9 +597,9 @@ export class HaServiceControl extends LitElement {
|
||||
);
|
||||
}
|
||||
target = {
|
||||
entity_id: targetEntities,
|
||||
device_id: targetDevices,
|
||||
area_id: targetAreas,
|
||||
...(targetEntities.length ? { entity_id: targetEntities } : {}),
|
||||
...(targetDevices.length ? { device_id: targetDevices } : {}),
|
||||
...(targetAreas.length ? { area_id: targetAreas } : {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,19 @@
|
||||
import { DEFAULT_SCHEMA, dump, load, Schema } from "js-yaml";
|
||||
import { html, LitElement, nothing, PropertyValues } from "lit";
|
||||
import {
|
||||
CSSResultGroup,
|
||||
css,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import "./ha-code-editor";
|
||||
import { showToast } from "../util/toast";
|
||||
import { copyToClipboard } from "../common/util/copy-clipboard";
|
||||
|
||||
const isEmpty = (obj: Record<string, unknown>): boolean => {
|
||||
if (typeof obj !== "object") {
|
||||
@@ -37,6 +47,8 @@ export class HaYamlEditor extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
@property({ type: Boolean }) public copyClipboard = false;
|
||||
|
||||
@state() private _yaml = "";
|
||||
|
||||
public setValue(value): void {
|
||||
@@ -88,6 +100,15 @@ export class HaYamlEditor extends LitElement {
|
||||
@value-changed=${this._onChange}
|
||||
dir="ltr"
|
||||
></ha-code-editor>
|
||||
${this.copyClipboard
|
||||
? html`<div class="card-actions">
|
||||
<mwc-button @click=${this._copyYaml}>
|
||||
${this.hass.localize(
|
||||
"ui.components.yaml-editor.copy_to_clipboard"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -117,6 +138,35 @@ export class HaYamlEditor extends LitElement {
|
||||
get yaml() {
|
||||
return this._yaml;
|
||||
}
|
||||
|
||||
private async _copyYaml(): Promise<void> {
|
||||
if (this.yaml) {
|
||||
await copyToClipboard(this.yaml);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.card-actions {
|
||||
border-radius: var(
|
||||
--actions-border-radius,
|
||||
0px 0px var(--ha-card-border-radius, 12px)
|
||||
var(--ha-card-border-radius, 12px)
|
||||
);
|
||||
border: 1px solid var(--divider-color);
|
||||
padding: 5px 16px;
|
||||
}
|
||||
ha-code-editor {
|
||||
flex-grow: 1;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -147,7 +147,7 @@ class DialogMediaManage extends LitElement {
|
||||
${!this._currentItem
|
||||
? html`
|
||||
<div class="refresh">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
: !children.length
|
||||
|
@@ -58,6 +58,7 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
this._navigateIds = undefined;
|
||||
this._currentItem = undefined;
|
||||
this._preferredLayout = "auto";
|
||||
this.classList.remove("opened");
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
@@ -79,6 +80,7 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
)
|
||||
: this._currentItem.title}
|
||||
@closed=${this.closeDialog}
|
||||
@opened=${this._dialogOpened}
|
||||
>
|
||||
<ha-dialog-header show-border slot="heading">
|
||||
${this._navigateIds.length > 1
|
||||
@@ -167,6 +169,10 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _dialogOpened() {
|
||||
this.classList.add("opened");
|
||||
}
|
||||
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
@@ -217,10 +223,13 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
|
||||
ha-media-player-browse {
|
||||
--media-browser-max-height: calc(100vh - 65px);
|
||||
height: calc(100vh - 65px);
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
:host(.opened) ha-media-player-browse {
|
||||
height: calc(100vh - 65px);
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-max-width: 800px;
|
||||
@@ -231,7 +240,6 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
ha-media-player-browse {
|
||||
position: initial;
|
||||
--media-browser-max-height: 100vh - 137px;
|
||||
height: 100vh - 137px;
|
||||
width: 700px;
|
||||
}
|
||||
}
|
||||
|
@@ -332,7 +332,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
}
|
||||
|
||||
if (!this._currentItem) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
|
||||
}
|
||||
|
||||
const currentItem = this._currentItem;
|
||||
|
@@ -54,8 +54,8 @@ class MediaUploadButton extends LitElement {
|
||||
? html`
|
||||
<ha-circular-progress
|
||||
size="tiny"
|
||||
active
|
||||
alt=""
|
||||
indeterminate
|
||||
area-label="Uploading"
|
||||
slot="icon"
|
||||
></ha-circular-progress>
|
||||
`
|
||||
|
@@ -1,3 +1,6 @@
|
||||
import { formatDuration } from "../common/datetime/duration";
|
||||
import { FrontendLocaleData } from "./translation";
|
||||
|
||||
export const STATE_ATTRIBUTES = [
|
||||
"entity_id",
|
||||
"assumed_state",
|
||||
@@ -64,6 +67,7 @@ export const DOMAIN_ATTRIBUTES_UNITS = {
|
||||
color_temp_kelvin: "K",
|
||||
min_color_temp_kelvin: "K",
|
||||
max_color_temp_kelvin: "K",
|
||||
brightness: "%",
|
||||
},
|
||||
sun: {
|
||||
elevation: "°",
|
||||
@@ -74,4 +78,22 @@ export const DOMAIN_ATTRIBUTES_UNITS = {
|
||||
sensor: {
|
||||
battery_level: "%",
|
||||
},
|
||||
media_player: {
|
||||
volume_level: "%",
|
||||
},
|
||||
} as const satisfies Record<string, Record<string, string>>;
|
||||
|
||||
type Formatter = (value: number, locale: FrontendLocaleData) => string;
|
||||
|
||||
export const DOMAIN_ATTRIBUTES_FORMATERS: Record<
|
||||
string,
|
||||
Record<string, Formatter>
|
||||
> = {
|
||||
light: {
|
||||
brightness: (value) => Math.round((value / 255) * 100).toString(),
|
||||
},
|
||||
media_player: {
|
||||
volume_level: (value) => Math.round(value * 100).toString(),
|
||||
media_duration: (value) => formatDuration(value.toString(), "s"),
|
||||
},
|
||||
};
|
||||
|
@@ -2,6 +2,7 @@ import {
|
||||
HassEntityAttributeBase,
|
||||
HassEntityBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { getExtendedEntityRegistryEntry } from "./entity_registry";
|
||||
import { showEnterCodeDialogDialog } from "../dialogs/enter-code/show-enter-code-dialog";
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
@@ -30,15 +31,20 @@ export const callProtectedLockService = async (
|
||||
service: ProtectedLockService
|
||||
) => {
|
||||
let code: string | undefined;
|
||||
const lockRegistryEntry = await getExtendedEntityRegistryEntry(
|
||||
hass,
|
||||
stateObj.entity_id
|
||||
).catch(() => undefined);
|
||||
const defaultCode = lockRegistryEntry?.options?.lock?.default_code;
|
||||
|
||||
if (stateObj!.attributes.code_format) {
|
||||
if (stateObj!.attributes.code_format && !defaultCode) {
|
||||
const response = await showEnterCodeDialogDialog(element, {
|
||||
codeFormat: "text",
|
||||
codePattern: stateObj!.attributes.code_format,
|
||||
title: hass.localize(`ui.card.lock.${service}`),
|
||||
submitText: hass.localize(`ui.card.lock.${service}`),
|
||||
});
|
||||
if (!response) {
|
||||
if (response == null) {
|
||||
throw new Error("Code dialog closed");
|
||||
}
|
||||
code = response;
|
||||
|
@@ -207,7 +207,7 @@ export class DialogAreaFilter
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
.handle {
|
||||
cursor: grab;
|
||||
cursor: move;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
|
@@ -425,13 +425,6 @@ class DataEntryFlowDialog extends LitElement {
|
||||
);
|
||||
}
|
||||
);
|
||||
if (this._step?.flow_id) {
|
||||
await this._unsubDataEntryFlowProgressed;
|
||||
// fetch flow after we subscribe to the event, so we don't miss the first event
|
||||
this._processStep(
|
||||
this._params!.flowConfig.fetchFlow(this.hass, this._step.flow_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
@@ -90,7 +90,7 @@ class StepFlowForm extends LitElement {
|
||||
${this._loading
|
||||
? html`
|
||||
<div class="submit-spinner">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
|
@@ -27,7 +27,7 @@ class StepFlowLoading extends LitElement {
|
||||
return html`
|
||||
<div class="init-spinner">
|
||||
${description ? html`<div>${description}</div>` : ""}
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ class StepFlowProgress extends LitElement {
|
||||
${this.flowConfig.renderShowFormProgressHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
<div class="content">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
${this.flowConfig.renderShowFormProgressDescription(
|
||||
this.hass,
|
||||
this.step
|
||||
|
@@ -99,6 +99,8 @@ export class DialogEnterCode
|
||||
id="code"
|
||||
.label=${this.hass.localize("ui.dialogs.enter_code.input_label")}
|
||||
type="password"
|
||||
autoValidate
|
||||
validateOnInitialRender
|
||||
pattern=${ifDefined(this._dialogParams.codePattern)}
|
||||
inputmode="text"
|
||||
></ha-textfield>
|
||||
|
@@ -53,8 +53,8 @@ export class MoreInfoConfigurator extends LitElement {
|
||||
>
|
||||
${this._isConfiguring
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
alt="Configuring"
|
||||
indeterminate
|
||||
aria-label="Configuring"
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.stateObj.attributes.submit_caption}
|
||||
|
@@ -103,8 +103,10 @@ class MoreInfoUpdate extends LitElement {
|
||||
: ""}
|
||||
${supportsFeature(this.stateObj!, UPDATE_SUPPORT_RELEASE_NOTES) &&
|
||||
!this._error
|
||||
? this._releaseNotes === undefined
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
? !this._releaseNotes
|
||||
? html`<div class="flex center">
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>`
|
||||
: html`<hr />
|
||||
<ha-faded>
|
||||
<ha-markdown .content=${this._releaseNotes}></ha-markdown>
|
||||
@@ -254,9 +256,10 @@ class MoreInfoUpdate extends LitElement {
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-circular-progress {
|
||||
width: 100%;
|
||||
.flex.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
mwc-linear-progress {
|
||||
margin-bottom: -8px;
|
||||
|
@@ -214,7 +214,7 @@ export class QuickBar extends LitElement {
|
||||
${!items
|
||||
? html`<ha-circular-progress
|
||||
size="small"
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>`
|
||||
: items.length === 0
|
||||
? html`
|
||||
@@ -375,7 +375,7 @@ export class QuickBar extends LitElement {
|
||||
const spinner = document.createElement("ha-circular-progress");
|
||||
spinner.size = "small";
|
||||
spinner.slot = "meta";
|
||||
spinner.active = true;
|
||||
spinner.indeterminate = true;
|
||||
this._getItemAtIndex(index)?.appendChild(spinner);
|
||||
}
|
||||
|
||||
|
@@ -91,7 +91,7 @@ class DialogRestart extends LitElement {
|
||||
${this._loadingHostInfo
|
||||
? html`
|
||||
<div class="loader">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
|
@@ -85,8 +85,7 @@ export class TTSTryDialog extends LitElement {
|
||||
? html`
|
||||
<ha-circular-progress
|
||||
size="small"
|
||||
active
|
||||
alt=""
|
||||
indeterminate
|
||||
slot="primaryAction"
|
||||
class="loading"
|
||||
></ha-circular-progress>
|
||||
|
@@ -26,7 +26,8 @@
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -40,12 +41,6 @@
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
@media (max-width: 592px) {
|
||||
.content {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@@ -22,7 +22,8 @@
|
||||
.content {
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -36,12 +37,6 @@
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
@media (max-width: 592px) {
|
||||
.content {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body id="particles">
|
||||
|
@@ -35,7 +35,7 @@ class HaInitPage extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<div id="progress-indicator-wrapper">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
<div id="loading-text">
|
||||
${this.migration
|
||||
|
@@ -46,7 +46,7 @@ class HassLoadingScreen extends LitElement {
|
||||
`}
|
||||
</div>`}
|
||||
<div class="content">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
${this.message
|
||||
? html`<div id="loading-text">${this.message}</div>`
|
||||
: nothing}
|
||||
|
@@ -57,7 +57,7 @@ class OnboardingCoreConfig extends LitElement {
|
||||
}
|
||||
if (this._skipCore) {
|
||||
return html`<div class="row center">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>`;
|
||||
}
|
||||
return html`
|
||||
|
@@ -123,7 +123,7 @@ class OnboardingLocation extends LitElement {
|
||||
? html`
|
||||
<ha-circular-progress
|
||||
slot="trailingIcon"
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
></ha-circular-progress>
|
||||
`
|
||||
|
@@ -64,8 +64,7 @@ class PanelCalendar extends LitElement {
|
||||
private _end?: Date;
|
||||
|
||||
private _showPaneController = new ResizeController(this, {
|
||||
callback: (entries: ResizeObserverEntry[]) =>
|
||||
entries[0]?.contentRect.width > 750,
|
||||
callback: (entries) => entries[0]?.contentRect.width > 750,
|
||||
});
|
||||
|
||||
private _mql?: MediaQueryList;
|
||||
|
@@ -227,7 +227,7 @@ export class DialogAddApplicationCredential extends LitElement {
|
||||
${this._loading
|
||||
? html`
|
||||
<div slot="primaryAction" class="submit-spinner">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
|
@@ -101,7 +101,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.no_blueprints"
|
||||
)
|
||||
: html`<ha-circular-progress active></ha-circular-progress>`}
|
||||
: html`<ha-circular-progress indeterminate></ha-circular-progress>`}
|
||||
</div>
|
||||
|
||||
${this.config.use_blueprint.path
|
||||
|
@@ -25,19 +25,16 @@ import {
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||
import {
|
||||
AutomationConfig,
|
||||
AutomationEntity,
|
||||
@@ -112,8 +109,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
|
||||
@state() private _validationErrors?: (string | TemplateResult)[];
|
||||
|
||||
@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
private _configSubscriptions: Record<
|
||||
string,
|
||||
(config?: AutomationConfig) => void
|
||||
@@ -342,8 +337,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
></manual-automation-editor>
|
||||
`
|
||||
: this._mode === "yaml"
|
||||
? html`
|
||||
${this._readOnly
|
||||
? html` ${this._readOnly
|
||||
? html`<ha-alert alert-type="warning">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.read_only"
|
||||
@@ -376,22 +370,13 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
`
|
||||
: ""}
|
||||
<ha-yaml-editor
|
||||
copyClipboard
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this._readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
></ha-yaml-editor>
|
||||
<ha-card outlined>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._copyYaml}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.copy_to_clipboard"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: ``}
|
||||
></ha-yaml-editor>`
|
||||
: nothing}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
@@ -612,15 +597,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
return cleanConfig;
|
||||
}
|
||||
|
||||
private async _copyYaml(): Promise<void> {
|
||||
if (this._yamlEditor?.yaml) {
|
||||
await copyToClipboard(this._yamlEditor.yaml);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _yamlChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (!ev.detail.isValid) {
|
||||
@@ -776,9 +752,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
.content {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
@@ -796,13 +769,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
}
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--actions-border-radius: 0;
|
||||
--code-mirror-height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
.yaml-mode ha-card {
|
||||
overflow: initial;
|
||||
--ha-card-border-radius: 0;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
|
@@ -158,7 +158,7 @@ class HaConfigBackup extends LitElement {
|
||||
${this._backupData.backing_up
|
||||
? html`<ha-circular-progress
|
||||
slot="icon"
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>`
|
||||
: html`<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>`}
|
||||
</ha-fab>
|
||||
|
@@ -163,9 +163,9 @@ class DialogImportBlueprint extends LitElement {
|
||||
>
|
||||
${this._importing
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
.title=${this.hass.localize(
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.importing"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
@@ -183,9 +183,9 @@ class DialogImportBlueprint extends LitElement {
|
||||
>
|
||||
${this._saving
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
.title=${this.hass.localize(
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.saving"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
|
@@ -93,7 +93,7 @@ export class CloudWebhooks extends LitElement {
|
||||
? html`
|
||||
<div class="progress">
|
||||
<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
|
@@ -108,7 +108,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
|
||||
></state-badge>
|
||||
${this.narrow && entity.attributes.in_progress
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
slot="graphic"
|
||||
class="absolute"
|
||||
@@ -128,7 +128,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
|
||||
${!this.narrow
|
||||
? entity.attributes.in_progress
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
slot="meta"
|
||||
></ha-circular-progress>`
|
||||
|
@@ -164,7 +164,9 @@ export class DialogHelperDetail extends LitElement {
|
||||
</mwc-button>
|
||||
`;
|
||||
} else if (this._loading || this._helperFlows === undefined) {
|
||||
content = html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
content = html`<ha-circular-progress
|
||||
indeterminate
|
||||
></ha-circular-progress>`;
|
||||
} else {
|
||||
const items: [string, string][] = [];
|
||||
|
||||
|
@@ -449,7 +449,9 @@ class AddIntegrationDialog extends LitElement {
|
||||
>
|
||||
</lit-virtualizer>
|
||||
</mwc-list>`
|
||||
: html`<ha-circular-progress active></ha-circular-progress>`} `;
|
||||
: html`<div class="flex center">
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>`} `;
|
||||
}
|
||||
|
||||
private _keyFunction = (integration: IntegrationListItem) =>
|
||||
@@ -682,10 +684,12 @@ class AddIntegrationDialog extends LitElement {
|
||||
p > a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-circular-progress {
|
||||
width: 100%;
|
||||
.flex.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
ha-circular-progress {
|
||||
margin: 24px 0;
|
||||
}
|
||||
mwc-list {
|
||||
|
@@ -57,7 +57,7 @@ class DialogMatterAddDevice extends LitElement {
|
||||
)
|
||||
: html`<ha-circular-progress
|
||||
size="large"
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>`}
|
||||
</div>
|
||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
|
@@ -102,7 +102,7 @@ class DialogZHAReconfigureDevice extends LitElement {
|
||||
${this._status === "started"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
<b>
|
||||
|
@@ -98,8 +98,8 @@ class ZHAAddDevicesPage extends LitElement {
|
||||
)}
|
||||
</h1>
|
||||
<ha-circular-progress
|
||||
active
|
||||
alt="Searching"
|
||||
indeterminate
|
||||
aria-label="Searching"
|
||||
></ha-circular-progress>
|
||||
`
|
||||
: html`
|
||||
@@ -236,7 +236,7 @@ class ZHAAddDevicesPage extends LitElement {
|
||||
color: var(--error-color);
|
||||
}
|
||||
ha-circular-progress {
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
}
|
||||
.searching {
|
||||
margin-top: 20px;
|
||||
|
@@ -98,9 +98,9 @@ export class ZHAAddGroupPage extends LitElement {
|
||||
>
|
||||
${this._processingAdd
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
.title=${this.hass!.localize(
|
||||
.ariaLabel=${this.hass!.localize(
|
||||
"ui.panel.config.zha.groups.creating_group"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
|
@@ -120,9 +120,8 @@ class ZHADeviceNeighbors extends LitElement {
|
||||
return html`
|
||||
${!this._devices
|
||||
? html`<ha-circular-progress
|
||||
alt="Loading"
|
||||
size="large"
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>`
|
||||
: html`<ha-data-table
|
||||
.hass=${this.hass}
|
||||
|
@@ -169,12 +169,14 @@ export class ZHAGroupPage extends LitElement {
|
||||
@click=${this._removeMembersFromGroup}
|
||||
class="button"
|
||||
>
|
||||
<ha-circular-progress
|
||||
?active=${this._processingRemove}
|
||||
alt=${this.hass.localize(
|
||||
"ui.panel.config.zha.groups.removing_members"
|
||||
)}
|
||||
></ha-circular-progress>
|
||||
${this._processingRemove
|
||||
? html`<ha-circular-progress
|
||||
indeterminate
|
||||
.ariaLabel=${this.hass.localize(
|
||||
"ui.panel.config.zha.groups.removing_members"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
: nothing}
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.groups.remove_members"
|
||||
)}</mwc-button
|
||||
@@ -208,7 +210,7 @@ export class ZHAGroupPage extends LitElement {
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
size="small"
|
||||
title="Saving"
|
||||
aria-label="Saving"
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.hass!.localize(
|
||||
|
@@ -116,7 +116,10 @@ class DialogZWaveJSAddNode extends LitElement {
|
||||
>
|
||||
${this._status === "loading"
|
||||
? html`<div style="display: flex; justify-content: center;">
|
||||
<ha-circular-progress size="large" active></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
size="large"
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
</div>`
|
||||
: this._status === "choose_strategy"
|
||||
? html`<h3>Choose strategy</h3>
|
||||
@@ -288,7 +291,9 @@ class DialogZWaveJSAddNode extends LitElement {
|
||||
"ui.panel.config.zwave_js.add_node.searching_device"
|
||||
)}
|
||||
</h3>
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.add_node.follow_device_instructions"
|
||||
@@ -304,7 +309,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
||||
)}
|
||||
</h2>
|
||||
<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
@@ -358,7 +363,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
|
@@ -97,7 +97,7 @@ class DialogZWaveJSRebuildNodeRoutes extends LitElement {
|
||||
${this._status === "started"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
|
@@ -68,7 +68,7 @@ class DialogZWaveJSReinterviewNode extends LitElement {
|
||||
${this._status === "started"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
<b>
|
||||
|
@@ -91,7 +91,7 @@ class DialogZWaveJSRemoveFailedNode extends LitElement {
|
||||
${this._status === "started"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
<b>
|
||||
|
@@ -71,7 +71,7 @@ class DialogZWaveJSRemoveNode extends LitElement {
|
||||
${this._status === "started"
|
||||
? html`
|
||||
<div class="flex-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
<div class="status">
|
||||
<p>
|
||||
<b
|
||||
|
@@ -171,7 +171,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
<div class="icon">
|
||||
${this._status === "disconnected"
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>`
|
||||
: html`
|
||||
<ha-svg-icon
|
||||
@@ -457,7 +457,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
: html`
|
||||
<ha-circular-progress
|
||||
size="small"
|
||||
active
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
`}
|
||||
</div>
|
||||
|
@@ -97,7 +97,7 @@ export class SystemLogCard extends LitElement {
|
||||
${this._items === undefined
|
||||
? html`
|
||||
<div class="loading-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
|
@@ -69,7 +69,7 @@ export class HassioHostname extends LitElement {
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._save} .disabled=${this._processing}>
|
||||
${this._processing
|
||||
? html`<ha-circular-progress active size="small">
|
||||
? html`<ha-circular-progress indeterminate size="small">
|
||||
</ha-circular-progress>`
|
||||
: this.hass.localize("ui.common.save")}
|
||||
</mwc-button>
|
||||
|
@@ -126,7 +126,7 @@ export class HassioNetwork extends LitElement {
|
||||
.disabled=${this._scanning}
|
||||
>
|
||||
${this._scanning
|
||||
? html`<ha-circular-progress active size="small">
|
||||
? html`<ha-circular-progress indeterminate size="small">
|
||||
</ha-circular-progress>`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.network.supervisor.scan_ap"
|
||||
@@ -242,7 +242,7 @@ export class HassioNetwork extends LitElement {
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
|
||||
${this._processing
|
||||
? html`<ha-circular-progress active size="small">
|
||||
? html`<ha-circular-progress indeterminate size="small">
|
||||
</ha-circular-progress>`
|
||||
: this.hass.localize("ui.common.save")}
|
||||
</mwc-button>
|
||||
|
@@ -304,7 +304,7 @@ class DialogSystemInformation extends LitElement {
|
||||
if (!this._systemInfo) {
|
||||
sections.push(html`
|
||||
<div class="loading-container">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
@@ -324,7 +324,10 @@ class DialogSystemInformation extends LitElement {
|
||||
|
||||
if (info.type === "pending") {
|
||||
value = html`
|
||||
<ha-circular-progress active size="tiny"></ha-circular-progress>
|
||||
<ha-circular-progress
|
||||
indeterminate
|
||||
size="tiny"
|
||||
></ha-circular-progress>
|
||||
`;
|
||||
} else if (info.type === "failed") {
|
||||
value = html`
|
||||
|
@@ -80,7 +80,7 @@ export class HaBlueprintScriptEditor extends LitElement {
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.no_blueprints"
|
||||
)
|
||||
: html`<ha-circular-progress active></ha-circular-progress>`}
|
||||
: html`<ha-circular-progress indeterminate></ha-circular-progress>`}
|
||||
</div>
|
||||
|
||||
${this.config.use_blueprint.path
|
||||
|
@@ -26,7 +26,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
@@ -38,7 +37,6 @@ import type {
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||
import { validateConfig } from "../../../data/config";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
@@ -94,8 +92,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
|
||||
@state() private _readOnly = false;
|
||||
|
||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
@query("manual-script-editor")
|
||||
private _manualEditor?: HaManualScriptEditor;
|
||||
|
||||
@@ -405,24 +401,14 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
</div>
|
||||
`
|
||||
: this._mode === "yaml"
|
||||
? html`
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this._readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
></ha-yaml-editor>
|
||||
<ha-card outlined>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._copyYaml}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.copy_to_clipboard"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: ``}
|
||||
? html` <ha-yaml-editor
|
||||
copyClipboard
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this._readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
></ha-yaml-editor>`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
@@ -735,15 +721,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
private async _copyYaml(): Promise<void> {
|
||||
if (this._yamlEditor?.yaml) {
|
||||
await copyToClipboard(this._yamlEditor.yaml);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _yamlChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
if (!ev.detail.isValid) {
|
||||
@@ -903,8 +880,11 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
}
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--actions-border-radius: 0;
|
||||
--code-mirror-height: 100%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.yaml-mode ha-card {
|
||||
overflow: initial;
|
||||
|
@@ -106,7 +106,11 @@ class MoveDatadiskDialog extends LitElement {
|
||||
>
|
||||
${this._moving
|
||||
? html`
|
||||
<ha-circular-progress alt="Moving" size="large" active>
|
||||
<ha-circular-progress
|
||||
aria-label="Moving"
|
||||
size="large"
|
||||
indeterminate
|
||||
>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this.hass.localize(
|
||||
|
@@ -194,7 +194,7 @@ export class DialogAddUser extends LitElement {
|
||||
${this._loading
|
||||
? html`
|
||||
<div slot="primaryAction" class="submit-spinner">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
|
@@ -90,7 +90,7 @@ const renderProgress = (
|
||||
return html`❌`;
|
||||
}
|
||||
return html`
|
||||
<ha-circular-progress size="tiny" active></ha-circular-progress>
|
||||
<ha-circular-progress size="tiny" indeterminate></ha-circular-progress>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -184,6 +184,8 @@ class HaPanelDevService extends LitElement {
|
||||
>
|
||||
<div class="card-content">
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
copyClipboard
|
||||
readOnly
|
||||
autoUpdate
|
||||
.value=${this._response}
|
||||
|
@@ -131,7 +131,7 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement {
|
||||
let stats: TemplateResult;
|
||||
|
||||
if (!this._stats5min || !this._statsHour) {
|
||||
stats = html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
stats = html`<ha-circular-progress indeterminate></ha-circular-progress>`;
|
||||
} else if (this._statsHour.length < 1 && this._stats5min.length < 1) {
|
||||
stats = html`<p>
|
||||
${this.hass.localize(
|
||||
|
@@ -156,7 +156,7 @@ class HaPanelDevTemplate extends LitElement {
|
||||
${this._rendering
|
||||
? html`<ha-circular-progress
|
||||
class="render-spinner"
|
||||
active
|
||||
indeterminate
|
||||
size="small"
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
|
@@ -77,7 +77,7 @@ export class DeveloperYamlConfig extends LitElement {
|
||||
? html`<div
|
||||
class="validate-container layout vertical center-center"
|
||||
>
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div> `
|
||||
: nothing
|
||||
: html`
|
||||
@@ -94,7 +94,7 @@ export class DeveloperYamlConfig extends LitElement {
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
${
|
||||
this._validateResult.errors
|
||||
? html`<ha-alert
|
||||
@@ -233,7 +233,7 @@ export class DeveloperYamlConfig extends LitElement {
|
||||
}
|
||||
|
||||
.validate-log {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
|
@@ -193,10 +193,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
||||
</div>
|
||||
${this._isLoading
|
||||
? html`<div class="progress-wrapper">
|
||||
<ha-circular-progress
|
||||
active
|
||||
alt=${this.hass.localize("ui.common.loading")}
|
||||
></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>`
|
||||
: !this._targetPickerValue
|
||||
? html`<div class="start-search">
|
||||
|
@@ -107,10 +107,7 @@ export class HaLogbook extends LitElement {
|
||||
if (this._logbookEntries === undefined) {
|
||||
return html`
|
||||
<div class="progress-wrapper">
|
||||
<ha-circular-progress
|
||||
active
|
||||
alt=${this.hass.localize("ui.common.loading")}
|
||||
></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import { mdiThermostat } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import "../../../components/ha-control-select";
|
||||
import "../../../components/ha-control-select-menu";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import type { HaControlSelectMenu } from "../../../components/ha-control-select-menu";
|
||||
import {
|
||||
ClimateEntity,
|
||||
compareClimateHvacModes,
|
||||
@@ -35,6 +39,9 @@ class HuiClimateHvacModesCardFeature
|
||||
|
||||
@state() _currentHvacMode?: HvacMode;
|
||||
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
@@ -66,8 +73,23 @@ class HuiClimateHvacModesCardFeature
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (this._haSelect && changedProps.has("hass")) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (
|
||||
this.hass &&
|
||||
this.hass.formatEntityAttributeValue !==
|
||||
oldHass?.formatEntityAttributeValue
|
||||
) {
|
||||
this._haSelect.layoutOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const mode = (ev.detail as any).value as HvacMode;
|
||||
const mode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as HvacMode);
|
||||
|
||||
if (mode === this.stateObj!.state) return;
|
||||
|
||||
@@ -111,6 +133,37 @@ class HuiClimateHvacModesCardFeature
|
||||
path: computeHvacModeIcon(mode),
|
||||
}));
|
||||
|
||||
if (this._config.style === "dropdown") {
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-select-menu
|
||||
show-arrow
|
||||
hide-label
|
||||
.label=${this.hass.localize("ui.card.climate.mode")}
|
||||
.value=${this._currentHvacMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiThermostat}></ha-svg-icon>
|
||||
${options.map(
|
||||
(option) => html`
|
||||
<ha-list-item .value=${option.value} graphic="icon">
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${option.path}
|
||||
></ha-svg-icon>
|
||||
${option.label}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-select
|
||||
@@ -131,6 +184,14 @@ class HuiClimateHvacModesCardFeature
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select-menu {
|
||||
box-sizing: border-box;
|
||||
--control-select-menu-height: 40px;
|
||||
--control-select-menu-border-radius: 10px;
|
||||
line-height: 1.2;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
ha-control-select {
|
||||
--control-select-padding: 0;
|
||||
--control-select-thickness: 40px;
|
||||
|
@@ -1,25 +1,34 @@
|
||||
import { mdiPower, mdiWaterPercent } from "@mdi/js";
|
||||
import { mdiTuneVariant } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, PropertyValues, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import "../../../components/ha-control-select-menu";
|
||||
import type { HaControlSelectMenu } from "../../../components/ha-control-select-menu";
|
||||
import {
|
||||
HumidifierEntityFeature,
|
||||
HumidifierEntity,
|
||||
computeHumidiferModeIcon,
|
||||
} from "../../../data/humidifier";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HumidifierEntity, HumidifierState } from "../../../data/humidifier";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature } from "../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { HumidifierModesCardFeatureConfig } from "./types";
|
||||
|
||||
export const supportsHumidifierModesCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
return (
|
||||
domain === "humidifier" &&
|
||||
supportsFeature(stateObj, HumidifierEntityFeature.MODES)
|
||||
);
|
||||
};
|
||||
|
||||
@customElement("hui-humidifier-modes-card-feature")
|
||||
class HuiHumidifierModeCardFeature
|
||||
class HuiHumidifierModesCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@@ -29,14 +38,29 @@ class HuiHumidifierModeCardFeature
|
||||
|
||||
@state() private _config?: HumidifierModesCardFeatureConfig;
|
||||
|
||||
@state() _currentState?: HumidifierState;
|
||||
@state() _currentMode?: string;
|
||||
|
||||
static getStubConfig(): HumidifierModesCardFeatureConfig {
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): HumidifierModesCardFeatureConfig {
|
||||
return {
|
||||
type: "humidifier-modes",
|
||||
style: "dropdown",
|
||||
modes: stateObj?.attributes.available_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/hui-humidifier-modes-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-humidifier-modes-card-feature-editor");
|
||||
}
|
||||
|
||||
public setConfig(config: HumidifierModesCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
@@ -47,33 +71,46 @@ class HuiHumidifierModeCardFeature
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentState = this.stateObj.state as HumidifierState;
|
||||
this._currentMode = this.stateObj.attributes.mode;
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (this._haSelect && changedProps.has("hass")) {
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (
|
||||
this.hass &&
|
||||
this.hass.formatEntityAttributeValue !==
|
||||
oldHass?.formatEntityAttributeValue
|
||||
) {
|
||||
this._haSelect.layoutOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const newState = (ev.detail as any).value as HumidifierState;
|
||||
const mode =
|
||||
(ev.detail as any).value ?? ((ev.target as any).value as string);
|
||||
|
||||
if (newState === this.stateObj!.state) return;
|
||||
const oldMode = this.stateObj!.attributes.mode;
|
||||
|
||||
const oldState = this.stateObj!.state as HumidifierState;
|
||||
this._currentState = newState;
|
||||
if (mode === oldMode) return;
|
||||
|
||||
this._currentMode = mode;
|
||||
|
||||
try {
|
||||
await this._setState(newState);
|
||||
await this._setMode(mode);
|
||||
} catch (err) {
|
||||
this._currentState = oldState;
|
||||
this._currentMode = oldMode;
|
||||
}
|
||||
}
|
||||
|
||||
private async _setState(newState: HumidifierState) {
|
||||
await this.hass!.callService(
|
||||
"humidifier",
|
||||
newState === "on" ? "turn_on" : "turn_off",
|
||||
{
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
}
|
||||
);
|
||||
private async _setMode(mode: string) {
|
||||
await this.hass!.callService("humidifier", "set_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
mode: mode,
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | null {
|
||||
@@ -86,34 +123,75 @@ class HuiHumidifierModeCardFeature
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const options = ["on", "off"].map<ControlSelectOption>((entityState) => ({
|
||||
value: entityState,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, entityState),
|
||||
path: entityState === "on" ? mdiWaterPercent : mdiPower,
|
||||
}));
|
||||
const modes = stateObj.attributes.available_modes || [];
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => (this._config!.modes || []).includes(mode))
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"mode",
|
||||
mode
|
||||
),
|
||||
path: computeHumidiferModeIcon(mode),
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-select
|
||||
.options=${options}
|
||||
.value=${this._currentMode}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-label
|
||||
.ariaLabel=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-select
|
||||
.options=${options}
|
||||
.value=${this._currentState}
|
||||
@value-changed=${this._valueChanged}
|
||||
<ha-control-select-menu
|
||||
show-arrow
|
||||
hide-label
|
||||
.ariaLabel=${this.hass.localize("ui.card.humidifier.state")}
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
|
||||
.value=${this._currentMode}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
</ha-control-select>
|
||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
||||
${options.map(
|
||||
(option) => html`
|
||||
<ha-list-item .value=${option.value} graphic="icon">
|
||||
<ha-svg-icon slot="graphic" .path=${option.path}></ha-svg-icon>
|
||||
${option.label}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-control-select-menu>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select-menu {
|
||||
box-sizing: border-box;
|
||||
--control-select-menu-height: 40px;
|
||||
--control-select-menu-border-radius: 10px;
|
||||
line-height: 1.2;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
ha-control-select {
|
||||
--control-select-color: var(--feature-color);
|
||||
--control-select-padding: 0;
|
||||
@@ -131,6 +209,6 @@ class HuiHumidifierModeCardFeature
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-humidifier-modes-card-feature": HuiHumidifierModeCardFeature;
|
||||
"hui-humidifier-modes-card-feature": HuiHumidifierModesCardFeature;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,136 @@
|
||||
import { mdiPower, mdiWaterPercent } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, PropertyValues, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import "../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../components/ha-control-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HumidifierEntity, HumidifierState } from "../../../data/humidifier";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature } from "../types";
|
||||
import { HumidifierToggleCardFeatureConfig } from "./types";
|
||||
|
||||
export const supportsHumidifierToggleCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
};
|
||||
|
||||
@customElement("hui-humidifier-toggle-card-feature")
|
||||
class HuiHumidifierToggleCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HumidifierEntity;
|
||||
|
||||
@state() private _config?: HumidifierToggleCardFeatureConfig;
|
||||
|
||||
@state() _currentState?: HumidifierState;
|
||||
|
||||
static getStubConfig(): HumidifierToggleCardFeatureConfig {
|
||||
return {
|
||||
type: "humidifier-toggle",
|
||||
};
|
||||
}
|
||||
|
||||
public setConfig(config: HumidifierToggleCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj") && this.stateObj) {
|
||||
this._currentState = this.stateObj.state as HumidifierState;
|
||||
}
|
||||
}
|
||||
|
||||
private async _valueChanged(ev: CustomEvent) {
|
||||
const newState = (ev.detail as any).value as HumidifierState;
|
||||
|
||||
if (newState === this.stateObj!.state) return;
|
||||
|
||||
const oldState = this.stateObj!.state as HumidifierState;
|
||||
this._currentState = newState;
|
||||
|
||||
try {
|
||||
await this._setState(newState);
|
||||
} catch (err) {
|
||||
this._currentState = oldState;
|
||||
}
|
||||
}
|
||||
|
||||
private async _setState(newState: HumidifierState) {
|
||||
await this.hass!.callService(
|
||||
"humidifier",
|
||||
newState === "on" ? "turn_on" : "turn_off",
|
||||
{
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | null {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsHumidifierToggleCardFeature(this.stateObj)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
|
||||
const options = ["on", "off"].map<ControlSelectOption>((entityState) => ({
|
||||
value: entityState,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, entityState),
|
||||
path: entityState === "on" ? mdiWaterPercent : mdiPower,
|
||||
}));
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-select
|
||||
.options=${options}
|
||||
.value=${this._currentState}
|
||||
@value-changed=${this._valueChanged}
|
||||
hide-label
|
||||
.ariaLabel=${this.hass.localize("ui.card.humidifier.state")}
|
||||
style=${styleMap({
|
||||
"--control-select-color": color,
|
||||
})}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
>
|
||||
</ha-control-select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-select {
|
||||
--control-select-color: var(--feature-color);
|
||||
--control-select-padding: 0;
|
||||
--control-select-thickness: 40px;
|
||||
--control-select-border-radius: 10px;
|
||||
--control-select-button-border-radius: 10px;
|
||||
}
|
||||
.container {
|
||||
padding: 0 12px 12px 12px;
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-humidifier-toggle-card-feature": HuiHumidifierToggleCardFeature;
|
||||
}
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing, PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import "../../../components/ha-control-slider";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HumidifierEntity } from "../../../data/humidifier";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature } from "../types";
|
||||
import { TargetHumidityCardFeatureConfig } from "./types";
|
||||
|
||||
export const supportsTargetHumidityCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
return domain === "humidifier";
|
||||
};
|
||||
|
||||
@customElement("hui-target-humidity-card-feature")
|
||||
class HuiTargetHumidityCardFeature
|
||||
extends LitElement
|
||||
implements LovelaceCardFeature
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public stateObj?: HumidifierEntity;
|
||||
|
||||
@state() private _config?: TargetHumidityCardFeatureConfig;
|
||||
|
||||
@state() private _targetHumidity?: number;
|
||||
|
||||
static getStubConfig(): TargetHumidityCardFeatureConfig {
|
||||
return {
|
||||
type: "target-humidity",
|
||||
};
|
||||
}
|
||||
|
||||
public setConfig(config: TargetHumidityCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProp: PropertyValues): void {
|
||||
super.willUpdate(changedProp);
|
||||
if (changedProp.has("stateObj")) {
|
||||
this._targetHumidity = this.stateObj!.attributes.humidity;
|
||||
}
|
||||
}
|
||||
|
||||
private get _step() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private get _min() {
|
||||
return this.stateObj!.attributes.min_humidity ?? 0;
|
||||
}
|
||||
|
||||
private get _max() {
|
||||
return this.stateObj!.attributes.max_humidity ?? 100;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._targetHumidity = value;
|
||||
this._callService();
|
||||
}
|
||||
|
||||
private _callService() {
|
||||
this.hass!.callService("humidifier", "set_humidity", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
humidity: this._targetHumidity,
|
||||
});
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this.stateObj ||
|
||||
!supportsTargetHumidityCardFeature(this.stateObj)
|
||||
) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-slider
|
||||
.value=${this.stateObj.attributes.humidity}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.step=${this._step}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass.formatEntityAttributeName(
|
||||
this.stateObj,
|
||||
"humidity"
|
||||
)}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
></ha-control-slider>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-control-slider {
|
||||
--control-slider-color: var(--feature-color);
|
||||
--control-slider-background: var(--feature-color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-thickness: 40px;
|
||||
--control-slider-border-radius: 10px;
|
||||
}
|
||||
.container {
|
||||
padding: 0 12px 12px 12px;
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-target-humidity-card-feature": HuiTargetHumidityCardFeature;
|
||||
}
|
||||
}
|
@@ -37,6 +37,7 @@ export interface AlarmModesCardFeatureConfig {
|
||||
|
||||
export interface ClimateHvacModesCardFeatureConfig {
|
||||
type: "climate-hvac-modes";
|
||||
style?: "dropdown" | "icons";
|
||||
hvac_modes?: HvacMode[];
|
||||
}
|
||||
|
||||
@@ -55,6 +56,10 @@ export interface NumericInputCardFeatureConfig {
|
||||
style?: "buttons" | "slider";
|
||||
}
|
||||
|
||||
export interface TargetHumidityCardFeatureConfig {
|
||||
type: "target-humidity";
|
||||
}
|
||||
|
||||
export interface TargetTemperatureCardFeatureConfig {
|
||||
type: "target-temperature";
|
||||
}
|
||||
@@ -66,6 +71,12 @@ export interface WaterHeaterOperationModesCardFeatureConfig {
|
||||
|
||||
export interface HumidifierModesCardFeatureConfig {
|
||||
type: "humidifier-modes";
|
||||
style?: "dropdown" | "icons";
|
||||
modes?: string[];
|
||||
}
|
||||
|
||||
export interface HumidifierToggleCardFeatureConfig {
|
||||
type: "humidifier-toggle";
|
||||
}
|
||||
|
||||
export const VACUUM_COMMANDS = [
|
||||
@@ -101,11 +112,13 @@ export type LovelaceCardFeatureConfig =
|
||||
| CoverTiltPositionCardFeatureConfig
|
||||
| CoverTiltCardFeatureConfig
|
||||
| FanSpeedCardFeatureConfig
|
||||
| HumidifierToggleCardFeatureConfig
|
||||
| HumidifierModesCardFeatureConfig
|
||||
| LawnMowerCommandsCardFeatureConfig
|
||||
| LightBrightnessCardFeatureConfig
|
||||
| LightColorTempCardFeatureConfig
|
||||
| VacuumCommandsCardFeatureConfig
|
||||
| TargetHumidityCardFeatureConfig
|
||||
| TargetTemperatureCardFeatureConfig
|
||||
| WaterHeaterOperationModesCardFeatureConfig
|
||||
| SelectOptionsCardFeatureConfig
|
||||
|
@@ -51,7 +51,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
||||
entity: foundEntities[0] || "",
|
||||
features: [
|
||||
{
|
||||
type: "humidifier-modes",
|
||||
type: "humidifier-toggle",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -127,6 +127,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
||||
<ha-card>
|
||||
<p class="title">${name}</p>
|
||||
<ha-state-control-humidifier-humidity
|
||||
prevent-interaction-on-scroll
|
||||
show-current
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
@@ -168,11 +169,14 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
||||
.title {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
padding: 12px 36px 16px 36px;
|
||||
line-height: 36px;
|
||||
padding: 8px 30px 8px 30px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ha-state-control-humidifier-humidity {
|
||||
|
@@ -101,6 +101,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
|
||||
this.updateComplete.then(() => this._measureCard());
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
@@ -339,15 +341,12 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this._attachObserver();
|
||||
this._measureCard();
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (!this.hasUpdated) {
|
||||
this._measureCard();
|
||||
}
|
||||
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
@@ -468,6 +467,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
|
||||
private _measureCard() {
|
||||
const card = this.shadowRoot!.querySelector("ha-card");
|
||||
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ export class HuiStartingCard extends LitElement implements LovelaceCard {
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
<ha-circular-progress active></ha-circular-progress>
|
||||
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||
${this.hass.localize("ui.panel.lovelace.cards.starting.description")}
|
||||
</div>
|
||||
`;
|
||||
@@ -59,7 +59,7 @@ export class HuiStartingCard extends LitElement implements LovelaceCard {
|
||||
height: calc(100vh - var(--header-height));
|
||||
}
|
||||
ha-circular-progress {
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.content {
|
||||
height: 100%;
|
||||
|
@@ -119,6 +119,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
<ha-card>
|
||||
<p class="title">${name}</p>
|
||||
<ha-state-control-climate-temperature
|
||||
prevent-interaction-on-scroll
|
||||
show-current
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
@@ -160,11 +161,14 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
.title {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
padding: 12px 36px 16px 36px;
|
||||
line-height: 36px;
|
||||
padding: 8px 30px 8px 30px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
ha-state-control-climate-temperature {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user