Compare commits

...
Author SHA1 Message Date
Paul Bottein 1b44c7ce64 Add strategy editor
rename clean function

Fix types
2023-11-15 14:46:31 +01:00
Paul BotteinandGitHub de370d6384 Add todo entity_id in URL (#18653) 2023-11-15 11:26:03 +01:00
Paul BotteinandGitHub 1cf928b425 Improve Lovelace types (#18652) 2023-11-15 11:13:49 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
b9c62c34a0 Update dependency lint-staged to v15.1.0 (#18660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 10:49:33 +01:00
renovate[bot]andGitHub 37554213dc Update dependency @bundle-stats/plugin-webpack-filter to v4.8.0 (#18654) 2023-11-14 20:37:44 -05:00
renovate[bot]andGitHub 3845fd2ef9 Update dependency @types/tar to v6.1.9 (#18650) 2023-11-14 20:35:55 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
837b36a430 Update dependency marked to v9.1.6 (#18644)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 19:46:13 +01:00
karwostsandGitHub bff09a7a36 Fix login password form (#18619) 2023-11-13 19:45:34 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
a5e5efa526 Update dependency @codemirror/autocomplete to v6.11.0 (#18633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 22:29:54 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1cd5b9ff37 Update babel monorepo to v7.23.3 (#18632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 21:16:45 -05:00
renovate[bot]andGitHub 6a11155dd7 Update dependency vis-data to v7.1.8 (#18625) 2023-11-11 21:09:28 -05:00
renovate[bot]andGitHub 1acb76d2f0 Update dependency @types/leaflet to v1.9.8 (#18622) 2023-11-12 02:07:18 +00:00
renovate[bot]andGitHub 2e8a40efd5 Update dependency @types/ua-parser-js to v0.7.39 (#18616) 2023-11-11 20:58:50 -05:00
renovate[bot]andGitHub 845a0e4e39 Update dependency @types/js-yaml to v4.0.9 (#18621) 2023-11-11 20:56:30 -05:00
Paul BotteinandGitHub 9417f13cc9 Add unit for number tile feature (#18602) 2023-11-10 19:30:07 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
d3f52f3bc0 Update dependency @types/tar to v6.1.8 (#18614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 18:17:57 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
7d0ec00fde Update dependency @types/sortablejs to v1.15.5 (#18613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 13:06:13 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
6dbb5dd4be Update dependency @types/qrcode to v1.5.5 (#18610)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 13:05:36 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
f30e7948cb Update dependency @types/luxon to v3.3.4 (#18605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 13:04:49 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
41472d8160 Update dependency @types/serve-handler to v6.1.4 (#18612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:43:05 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
e596d32426 Update dependency @types/mocha to v10.0.4 (#18606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:41:33 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
89de62812f Update dependency @types/leaflet-draw to v1.0.10 (#18603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:39:56 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3e391fcbe6 Update dependency @types/html-minifier-terser to v7.0.2 (#18601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:37:31 -05:00
karwostsandGitHub 708ab2ed69 Localize hide/show password (#18607) 2023-11-10 18:12:36 +01:00
Paul BotteinandGitHub 1d45cb78fe Rename tooltipUnit to unit for slider (#18599) 2023-11-10 18:08:12 +01:00
d3f6ebd1d0 Add unit to temperature tile number button group (#17841)
* Add unit to temperature tile number button group

* Update gallery

* Use blank before unit

* Hide unit if no space

* Fix build

---------

Co-authored-by: Paul Bottein <[email protected]>
2023-11-10 14:22:10 +01:00
21644c70b3 Add number tile feature (#18562)
* hui-number-tile-feature

* Added support for number domain

* Apply suggestions from code review (thanks @piitaya)

Co-authored-by: Paul Bottein <[email protected]>

* Fixed the callback to use the correct service

* fix formatting

---------

Co-authored-by: Paul Bottein <[email protected]>
2023-11-10 08:34:29 +00:00
karwostsandGitHub eb75389cac Localize clear in entity filter (#18598)
Localize  in entity filter
2023-11-10 08:27:36 +01:00
Bram KragtenandGitHub cd4f3a091b Fix add device dialog (#18591) 2023-11-10 08:27:02 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
19c1973ec1 Update dependency @types/chromecast-caf-sender to v1.0.8 (#18597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 03:35:04 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
a8efbe5b06 Update dependency @types/chromecast-caf-receiver to v6.0.12 (#18595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 03:22:14 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
44b5c66c19 Update dependency @types/babel__plugin-transform-runtime to v7.9.5 (#18594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 22:08:58 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3fa3bba4d8 Update formatjs monorepo (#18592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 22:07:01 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ab09c821a2 Update typescript-eslint monorepo to v6.10.0 (#18586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 22:05:26 -05:00
Paul BotteinandGitHub 37b7395986 Migrate chips to Material 3 chips. (#18513) 2023-11-09 17:57:37 +01:00
Bram KragtenandGitHub 4933413140 Fix virtualizer with v2.0.10 (#18584) 2023-11-09 11:53:35 -05:00
Bram KragtenandGitHub 1c16bd5ab2 rollback lit-labs/virtualizer to 2.0.8 (#18580) 2023-11-09 16:26:09 +01:00
Paul BotteinandGitHub 3a926c6f83 Add pointer-events none to slider tooltip (#18577) 2023-11-09 14:57:23 +01:00
7a619ad55a Only update dev tools yaml editor on entity change or submit (#18575)
Co-authored-by: Simon Lamon <[email protected]>
2023-11-09 10:00:24 +01:00
Paul BotteinandGitHub bc21425981 Add tooltip to tile sliders and more info sliders (#18567) 2023-11-08 12:47:55 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
f2505c0798 Update dependency eslint to v8.53.0 (#18558)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 11:48:22 +01:00
Steve RepsherandGitHub cee8c756fc Remove dependency esprima (#18564) 2023-11-08 11:24:03 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
396b3aace9 Update dependency @codemirror/view to v6.22.0 (#18553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 18:02:11 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
28dc1e4da9 Update vaadinWebComponents monorepo to v24.2.2 (#18552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 17:59:54 -05:00
Kendell RandGitHub aa0419e783 Minify logos (#18542) 2023-11-07 13:39:38 +01:00
Jan BouwhuisandGitHub fd9c24d05e Add localization to service call exceptions (#18447) 2023-11-07 12:04:53 +01:00
Paul BotteinandGitHub 8bdbe8c6a6 Use strict comparison for above condition in conditional card (#18560) 2023-11-07 12:04:04 +01:00
Steve RepsherandGitHub 39550cefa0 Add some Renovate groups and enable NVM updates (#18559) 2023-11-07 11:53:47 +01:00
David BellandGitHub 589e3b63c7 Don't display hidden/non-visible calendars on the Calendar panel (#18541) 2023-11-06 15:09:54 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
0cc38278b9 Update dependency @lit-labs/virtualizer to v2.0.10 (#18547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:56:45 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
aafdf7bed7 Update dependency vis-network to v9.1.9 (#18548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:55:24 +01:00
Simon LamonandGitHub a7f6ce3079 Fix refresh in developer state tools (#18532) 2023-11-06 15:53:23 +01:00
karwostsandGitHub 3d03f74d66 Improve template trigger/condition descriptions (#18504) 2023-11-06 10:04:58 +01:00
tzagimandGitHub e09a6a23fc Fix for RTL (#18509) 2023-11-06 10:02:44 +01:00
Kendell RandGitHub 9e4bb6ed0c Fix "ttme" typo on the demo page (#18543) 2023-11-06 10:02:00 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
c45c8ab5c0 Update dependency sinon to v17.0.1 (#18535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-05 14:16:21 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
5ef6973933 Update dependency marked to v9.1.5 (#18523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-05 14:14:50 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1df344580a Update dependency eslint-plugin-lit-a11y to v4.1.1 (#18534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-05 14:13:25 -05:00
renovate[bot]andGitHub 25acc479b9 Update dependency core-js to v3.33.2 (#18515) 2023-11-02 20:54:59 -04:00
renovate[bot]andGitHub c9f051374b Update dependency punycode to v2.3.1 (#18516) 2023-11-02 20:50:03 -04:00
renovate[bot]andGitHub bf5116fc0b Update typescript-eslint monorepo to v6.9.1 (#18518) 2023-11-02 20:47:03 -04:00
renovate[bot]andGitHub edc8cb1d2e Update dependency @rollup/plugin-replace to v5.0.5 (#18505) 2023-11-02 20:42:05 -04:00
Bram KragtenandGitHub a60bb3ae0a Clean up auth flow translation placeholders (#18502) 2023-11-02 21:02:42 +01:00
Paul BotteinandGitHub 784f753f07 Display default action label in action selector (#18398) 2023-11-02 16:10:15 +01:00
Paul BotteinandGitHub e63c7e3763 Add trigger selector for blueprint (#18469) 2023-11-02 16:01:59 +01:00
karwostsandGitHub cfa522068c Localize trigger condition and add list formatting (#18488) 2023-11-01 15:42:43 +01:00
schelvandGitHub 5f2375fe84 feat: Dynamically adjust gradient percentages based on min/max values (#18479) 2023-11-01 15:41:26 +01:00
Kendell RandGitHub a9e34d7590 Stop using ha-markdown on the auth page (#18480) 2023-11-01 15:36:17 +01:00
Paul BotteinandGitHub 463cfb869f Add and and or condition to conditional card (#18409) 2023-11-01 15:28:37 +01:00
Paul BotteinandGitHub acb5a2b283 Add test button to conditional card (#18411) 2023-11-01 15:25:56 +01:00
Bram KragtenandGitHub 08597d6e91 Bump node to v20 (#18497) 2023-11-01 15:22:23 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Steve RepsherBram Kragten
9478485268 Update Yarn to v4 (#18414)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <[email protected]>
Co-authored-by: Bram Kragten <[email protected]>
2023-11-01 10:16:46 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
0af9ec2fff Pin dependencies (#18498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-01 09:51:15 +00:00
Kendell RandGitHub f6e74de05e Properly set direction styles (#18483) 2023-11-01 10:38:02 +01:00
Bram KragtenandGitHub 8b0373d5c0 Revert bump to @lit/[email protected] (#18496) 2023-11-01 10:35:50 +01:00
karwostsandGitHub d4218250af Fix choose add option (#18493) 2023-11-01 09:37:26 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Steve Repsher
78783942be Update dependency @lit-labs/observers to v2.0.2 (#18484)
* Update dependency @lit-labs/observers to v2.0.2

* Resolve @lit/reactive-element to v1

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <[email protected]>
2023-10-31 14:42:28 -04:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Steve Repsher
17ad8013ca Update dependency @lit-labs/context to v0.5.1 (#18208)
* Update dependency @lit-labs/context to v0.5.1

* Switch to @lit/context and resolve reactive-element to v1

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <[email protected]>
2023-10-31 13:53:53 -04:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Steve Repsher
f70767f084 Update dependency @lit-labs/motion to v1.0.6 (#18205)
* Update dependency @lit-labs/motion to v1.0.6

* Resolve to lit v2

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <[email protected]>
2023-10-31 14:31:42 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Steve Repsher
3972394d82 Update dependency @lit-labs/virtualizer to v2.0.9 (#18206)
* Update dependency @lit-labs/virtualizer to v2.0.9

* Resolve to lit v2

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <[email protected]>
2023-10-31 13:22:54 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
5331fb4f5f Update dependency marked to v9.1.3 (#18485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-31 08:44:18 -04:00
Matthias de BaatandGitHub 16df352ba8 Update ha-dialogs.markdown (#18487) 2023-10-31 12:27:46 +00:00
Bram Kragten 8e1e75dee1 Bumped version to 20231030.0 2023-10-30 21:44:17 +01:00
Bram KragtenandGitHub d27b4e04a9 Adapt to new todo services (#18477) 2023-10-30 20:32:32 +00:00
36f7b34ac5 Use trigger and condition type translation labels (#18413)
Co-authored-by: Bram Kragten <[email protected]>
2023-10-30 20:54:30 +01:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
c0ff24bf1b Update dependency ua-parser-js to v1.0.37 (#18473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-30 12:29:17 -04:00
abb5aa348f Allow individual item deletion in To-do list (#18464)
Co-authored-by: Bram Kragten <[email protected]>
2023-10-30 14:21:29 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Bram Kragten
37ab5cbdc3 Update dependency fuse.js to v7 (#18462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bram Kragten <[email protected]>
2023-10-30 15:09:12 +01:00
Bram KragtenandGitHub 42be3b331c Fix buttons (#18472) 2023-10-30 13:56:48 +01:00
Paul BotteinandGitHub 71e05f79c2 Update ts-lit-plugin to 2.0.1 (#18471) 2023-10-30 13:40:07 +01:00
karwostsandGitHub c5a0a5bbbf Fix statistics bar chart time axis (#18450) 2023-10-30 13:39:15 +01:00
240 changed files with 9220 additions and 8197 deletions
+1 -1
View File
@@ -1 +1 @@
18
lts/iron
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-874
View File
File diff suppressed because one or more lines are too long
+893
View File
File diff suppressed because one or more lines are too long
+5 -7
View File
@@ -1,11 +1,9 @@
compressionLevel: mixed
defaultSemverRangePrefix: ""
enableGlobalCache: false
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.6.4.cjs
yarnPath: .yarn/releases/yarn-4.0.1.cjs
+1 -5
View File
@@ -12,11 +12,7 @@ module.exports.sourceMapURL = () => {
};
// Files from NPM Packages that should not be imported
// eslint-disable-next-line unused-imports/no-unused-vars
module.exports.ignorePackages = ({ latestBuild }) => [
// Part of yaml.js and only used for !!js functions that we don't use
require.resolve("esprima"),
];
module.exports.ignorePackages = () => [];
// Files from NPM packages that we should replace with empty file
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
+16 -12
View File
@@ -3,7 +3,7 @@ import { mdiCast, mdiCastConnected } from "@mdi/js";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { CastManager } from "../../../../src/cast/cast_manager";
import {
@@ -22,8 +22,9 @@ import "../../../../src/components/ha-svg-icon";
import {
getLegacyLovelaceCollection,
getLovelaceCollection,
LovelaceConfig,
} from "../../../../src/data/lovelace";
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout";
@@ -38,10 +39,10 @@ class HcCast extends LitElement {
@state() private askWrite = false;
@state() private lovelaceConfig?: LovelaceConfig | null;
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) {
if (this.lovelaceViews === undefined) {
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
}
@@ -86,9 +87,10 @@ class HcCast extends LitElement {
attr-for-selected="data-path"
.selected=${this.castManager.status.lovelacePath || ""}
>
${(this.lovelaceConfig
? this.lovelaceConfig.views
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
${(
this.lovelaceViews ?? [
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
]
).map(
(view, idx) => html`
<paper-icon-item
@@ -136,11 +138,15 @@ class HcCast extends LitElement {
llColl.refresh().then(
() => {
llColl.subscribe((config) => {
this.lovelaceConfig = config;
if (isStrategyDashboard(config)) {
this.lovelaceViews = null;
} else {
this.lovelaceViews = config.views;
}
});
},
async () => {
this.lovelaceConfig = null;
this.lovelaceViews = null;
}
);
@@ -159,9 +165,7 @@ class HcCast extends LitElement {
toggleAttribute(
this,
"hide-icons",
this.lovelaceConfig
? !this.lovelaceConfig.views.some((view) => view.icon)
: true
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
);
}
+2 -4
View File
@@ -1,7 +1,5 @@
import {
LovelaceCardConfig,
LovelaceConfig,
} from "../../../../src/data/lovelace";
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { castContext } from "../cast_context";
export const castDemoLovelace: () => LovelaceConfig = () => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mockHistory } from "../../../../demo/src/stubs/history";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import {
MockHomeAssistant,
provideHass,
+3 -2
View File
@@ -1,7 +1,7 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
import { HomeAssistant } from "../../../../src/types";
@@ -14,7 +14,8 @@ import "./hc-launch-screen";
class HcLovelace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
@property({ attribute: false })
public lovelaceConfig!: LovelaceConfig;
@property() public viewPath?: string | number;
+29 -12
View File
@@ -21,18 +21,26 @@ import {
import { atLeastVersion } from "../../../../src/common/config/version";
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
import {
fetchResources,
getLegacyLovelaceCollection,
getLovelaceCollection,
} from "../../../../src/data/lovelace";
import {
isStrategyDashboard,
LegacyLovelaceConfig,
LovelaceConfig,
} from "../../../../src/data/lovelace";
LovelaceDashboardStrategyConfig,
} from "../../../../src/data/lovelace/config/types";
import { fetchResources } from "../../../../src/data/lovelace/resource";
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
import { HassElement } from "../../../../src/state/hass-element";
import { castContext } from "../cast_context";
import "./hc-launch-screen";
const DEFAULT_STRATEGY = "original-states";
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
strategy: {
type: "original-states",
},
};
let resourcesLoaded = false;
@customElement("hc-main")
@@ -93,7 +101,7 @@ export class HcMain extends HassElement {
.lovelaceConfig=${this._lovelaceConfig}
.viewPath=${this._lovelacePath}
.urlPath=${this._urlPath}
@config-refresh=${this._generateLovelaceConfig}
@config-refresh=${this._generateDefaultLovelaceConfig}
></hc-lovelace>
`;
}
@@ -284,9 +292,20 @@ export class HcMain extends HassElement {
// configuration.
try {
await llColl.refresh();
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
this._handleNewLovelaceConfig(lovelaceConfig)
);
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
if (isStrategyDashboard(rawConfig)) {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
);
const config = await generateLovelaceDashboardStrategy(
rawConfig.strategy,
this.hass!
);
this._handleNewLovelaceConfig(config);
} else {
this._handleNewLovelaceConfig(rawConfig);
}
});
} catch (err: any) {
if (
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
@@ -300,7 +319,7 @@ export class HcMain extends HassElement {
}
// Generate a Lovelace config.
this._unsubLovelace = () => undefined;
await this._generateLovelaceConfig();
await this._generateDefaultLovelaceConfig();
}
}
if (!resourcesLoaded) {
@@ -316,15 +335,13 @@ export class HcMain extends HassElement {
this._sendStatus();
}
private async _generateLovelaceConfig() {
private async _generateDefaultLovelaceConfig() {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
);
this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy(
{
type: DEFAULT_STRATEGY,
},
DEFAULT_CONFIG.strategy,
this.hass!
)
);
+1 -1
View File
@@ -1,5 +1,5 @@
import { LocalizeFunc } from "../../../src/common/translations/localize";
import { LovelaceConfig } from "../../../src/data/lovelace";
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
import { Entity } from "../../../src/fake_data/entity";
export interface DemoConfig {
+1 -1
View File
@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import "../../../src/components/ha-card";
import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace";
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
import {
+3 -13
View File
@@ -74,19 +74,9 @@
<body>
<div id="ha-launch-screen">
<div class="ha-launch-screen-spacer"></div>
<svg
viewBox="0 0 240 240"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
fill="#F2F4F9"
/>
<path
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
fill="#18BCF2"
/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
</svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
</div>
+65 -27
View File
@@ -1,8 +1,10 @@
import { css, html, LitElement, TemplateResult } from "lit";
import { css, html, LitElement, TemplateResult, nothing } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-chip";
import "../../../../src/components/ha-chip-set";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/chips/ha-input-chip";
import "../../../../src/components/chips/ha-filter-chip";
import "../../../../src/components/ha-svg-icon";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
@@ -10,10 +12,6 @@ const chips: {
icon?: string;
content?: string;
}[] = [
{},
{
icon: mdiHomeAssistant,
},
{
content: "Content",
},
@@ -29,31 +27,73 @@ export class DemoHaChips extends LitElement {
return html`
<ha-card header="ha-chip demo">
<div class="card-content">
${chips.map(
(chip) => html`
<ha-chip .hasIcon=${chip.icon !== undefined}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-chip>
`
)}
</div>
</ha-card>
<ha-card header="ha-chip-set demo">
<div class="card-content">
<p>Action chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-chip .hasIcon=${chip.icon !== undefined}>
<ha-assist-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-assist-chip>
`
)}
${chips.map(
(chip) => html`
<ha-assist-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-assist-chip>
`
)}
</ha-chip-set>
<p>Filter chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-filter-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-filter-chip>
`
)}
${chips.map(
(chip) => html`
<ha-filter-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-filter-chip>
`
)}
</ha-chip-set>
<p>Input chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-input-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-chip>
</ha-input-chip>
`
)}
${chips.map(
(chip) => html`
<ha-input-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-input-chip>
`
)}
</ha-chip-set>
@@ -68,12 +108,10 @@ export class DemoHaChips extends LitElement {
max-width: 600px;
margin: 24px auto;
}
ha-chip {
margin-bottom: 4px;
}
.card-content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
`;
}
@@ -11,6 +11,7 @@ const buttons: {
min?: number;
max?: number;
step?: number;
unit?: string;
class?: string;
}[] = [
{
@@ -29,6 +30,11 @@ const buttons: {
label: "Custom",
class: "custom",
},
{
id: "unit",
label: "With unit",
unit: "m",
},
];
@customElement("demo-components-ha-control-number-buttons")
@@ -50,6 +56,7 @@ export class DemoHarControlNumberButtons extends LitElement {
<pre>Config: ${JSON.stringify(config)}</pre>
<ha-control-number-buttons
.value=${this.value}
.unit=${config.unit}
.min=${config.min}
.max=${config.max}
.step=${config.step}
@@ -9,6 +9,7 @@ const sliders: {
id: string;
label: string;
mode?: "start" | "end" | "cursor";
unit?: string;
class?: string;
}[] = [
{
@@ -31,18 +32,21 @@ const sliders: {
label: "Slider (start mode) and custom style",
mode: "start",
class: "custom",
unit: "mm",
},
{
id: "slider-end-custom",
label: "Slider (end mode) and custom style",
mode: "end",
class: "custom",
unit: "mm",
},
{
id: "slider-cursor-custom",
label: "Slider (cursor mode) and custom style",
mode: "cursor",
class: "custom",
unit: "mm",
},
];
@@ -93,6 +97,7 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-labelledby=${id}
.unit=${config.unit}
>
</ha-control-slider>
</div>
@@ -114,6 +119,7 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-label=${label}
.unit=${config.unit}
>
</ha-control-slider>
`;
@@ -5,9 +5,22 @@ subtitle: Dialogs provide important prompts in a user flow.
# Material Design 3
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
# Highlighted guidelines
# Guidelines
## Design
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
- Destructive actions should be a red warning button.
- Alert or confirmation dialogs only have buttons and no X-icon.
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
## Content
@@ -17,14 +30,6 @@ Our dialogs are based on the latest version of Material Design. Specs and guidel
- If users become unsure, they read the description. Make sure this explains what will happen.
- Strive for minimalism.
## Buttons and X-icon
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
- Destructive actions should be a red warning button.
- Alert or confirmation dialogs only have buttons and no X-icon.
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
## Example
### Confirmation dialog
-1
View File
@@ -10,7 +10,6 @@ import { computeStateDisplay } from "../../../../src/common/entity/compute_state
import "../../../../src/components/data-table/ha-data-table";
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
import "../../../../src/components/entity/state-badge";
import "../../../../src/components/ha-chip";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
+1 -1
View File
@@ -2,7 +2,7 @@ import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-misc-util-long-press")
+150 -91
View File
@@ -31,8 +31,8 @@ import { navigate } from "../../../../src/common/navigate";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-chip";
import "../../../../src/components/ha-chip-set";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/ha-markdown";
import "../../../../src/components/ha-settings-row";
import "../../../../src/components/ha-svg-icon";
@@ -78,6 +78,7 @@ import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-has
import { hassioStyle } from "../../resources/hassio-style";
import "../../update-available/update-available-card";
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
const STAGE_ICON = {
stable: mdiCheckCircle,
@@ -234,28 +235,32 @@ class HassioAddonInfo extends LitElement {
<ha-chip-set class="capabilities">
${this.addon.stage !== "stable"
? html` <ha-chip
hasIcon
class=${classMap({
yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated",
})}
@click=${this._showMoreInfo}
id="stage"
>
<ha-svg-icon
slot="icon"
.path=${STAGE_ICON[this.addon.stage]}
? html`
<ha-assist-chip
filled
class=${classMap({
yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated",
})}
@click=${this._showMoreInfo}
id="stage"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
`addon.dashboard.capability.stages.${this.addon.stage}`
)
)}
>
</ha-svg-icon>
${this.supervisor.localize(
`addon.dashboard.capability.stages.${this.addon.stage}`
)}
</ha-chip>`
<ha-svg-icon
slot="icon"
.path=${STAGE_ICON[this.addon.stage]}
>
</ha-svg-icon>
</ha-assist-chip>
`
: ""}
<ha-chip
hasIcon
<ha-assist-chip
filled
class=${classMap({
green: Number(this.addon.rating) >= 6,
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
@@ -263,138 +268,183 @@ class HassioAddonInfo extends LitElement {
})}
@click=${this._showMoreInfo}
id="rating"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.rating"
)
)}
>
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
</ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.rating"
)}
</ha-chip>
</ha-assist-chip>
${this.addon.host_network
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="host_network"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.host"
)
)}
>
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.host"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.full_access
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="full_access"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.hardware"
)
)}
>
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.hardware"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.homeassistant_api
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="homeassistant_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.core"
)
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiHomeAssistant}
></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.core"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this._computeHassioApi
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="hassio_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
`addon.dashboard.capability.role.${this.addon.hassio_role}`
) || this.addon.hassio_role
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiHomeAssistant}
></ha-svg-icon>
${this.supervisor.localize(
`addon.dashboard.capability.role.${this.addon.hassio_role}`
) || this.addon.hassio_role}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.docker_api
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.docker"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="docker_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.docker"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
${this.addon.host_pid
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.host_pid"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="host_pid"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.host_pid"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
${this.addon.apparmor !== "default"
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
class=${this._computeApparmorClassName}
id="apparmor"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.apparmor"
)
)}
>
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.apparmor"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.auth_api
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.auth"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="auth_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.auth"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
${this.addon.ingress
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="ingress"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.ingress"
)
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiCursorDefaultClickOutline}
></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.ingress"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.signed
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="signed">
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.signed"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="signed"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.signed"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
</ha-chip-set>
@@ -1185,23 +1235,35 @@ class HassioAddonInfo extends LitElement {
.description a {
color: var(--primary-color);
}
ha-chip {
text-transform: capitalize;
--ha-chip-text-color: var(--text-primary-color);
--ha-chip-background-color: var(--primary-color);
ha-assist-chip {
--md-sys-color-primary: var(--text-primary-color);
--md-sys-color-on-surface: var(--text-primary-color);
--ha-assist-chip-filled-container-color: var(--primary-color);
}
.red {
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
--ha-assist-chip-filled-container-color: var(
--label-badge-red,
#df4c1e
);
}
.blue {
--ha-chip-background-color: var(--label-badge-blue, #039be5);
--ha-assist-chip-filled-container-color: var(
--label-badge-blue,
#039be5
);
}
.green {
--ha-chip-background-color: var(--label-badge-green, #0da035);
--ha-assist-chip-filled-container-color: var(
--label-badge-green,
#0da035
);
}
.yellow {
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
--ha-assist-chip-filled-container-color: var(
--label-badge-yellow,
#f4b400
);
}
.capabilities {
margin-bottom: 16px;
@@ -1260,9 +1322,6 @@ class HassioAddonInfo extends LitElement {
}
@media (max-width: 720px) {
ha-chip {
line-height: 36px;
}
.addon-options {
max-width: 100%;
}
@@ -1,8 +1,9 @@
import Fuse from "fuse.js";
import type { IFuseOptions } from "fuse.js";
import { StoreAddon } from "../../../src/data/supervisor/store";
export function filterAndSort(addons: StoreAddon[], filter: string) {
const options: Fuse.IFuseOptions<StoreAddon> = {
const options: IFuseOptions<StoreAddon> = {
keys: ["name", "description", "slug"],
isCaseSensitive: false,
minMatchCharLength: 2,
+54 -54
View File
@@ -27,22 +27,22 @@
"dependencies": {
"@babel/runtime": "7.23.2",
"@braintree/sanitize-url": "6.0.4",
"@codemirror/autocomplete": "6.10.2",
"@codemirror/autocomplete": "6.11.0",
"@codemirror/commands": "6.3.0",
"@codemirror/language": "6.9.2",
"@codemirror/legacy-modes": "6.3.3",
"@codemirror/search": "6.5.4",
"@codemirror/state": "6.3.1",
"@codemirror/view": "6.21.4",
"@codemirror/view": "6.22.0",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.11.1",
"@formatjs/intl-displaynames": "6.6.1",
"@formatjs/intl-datetimeformat": "6.11.2",
"@formatjs/intl-displaynames": "6.6.2",
"@formatjs/intl-getcanonicallocales": "2.3.0",
"@formatjs/intl-listformat": "7.5.0",
"@formatjs/intl-locale": "3.4.0",
"@formatjs/intl-numberformat": "8.8.0",
"@formatjs/intl-pluralrules": "5.2.7",
"@formatjs/intl-relativetimeformat": "11.2.7",
"@formatjs/intl-listformat": "7.5.1",
"@formatjs/intl-locale": "3.4.1",
"@formatjs/intl-numberformat": "8.8.1",
"@formatjs/intl-pluralrules": "5.2.8",
"@formatjs/intl-relativetimeformat": "11.2.8",
"@fullcalendar/core": "6.1.9",
"@fullcalendar/daygrid": "6.1.9",
"@fullcalendar/interaction": "6.1.9",
@@ -51,9 +51,9 @@
"@fullcalendar/timegrid": "6.1.9",
"@lezer/highlight": "1.1.6",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.4",
"@lit-labs/observers": "2.0.1",
"@lit-labs/virtualizer": "2.0.7",
"@lit-labs/motion": "1.0.6",
"@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.10",
"@lrnwebcomponents/simple-tooltip": "7.0.18",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
@@ -94,8 +94,8 @@
"@polymer/paper-toast": "3.0.1",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.2.1",
"@vaadin/vaadin-themable-mixin": "24.2.1",
"@vaadin/combo-box": "24.2.2",
"@vaadin/vaadin-themable-mixin": "24.2.2",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -105,28 +105,28 @@
"app-datepicker": "5.1.1",
"chart.js": "4.4.0",
"comlink": "4.4.1",
"core-js": "3.33.1",
"core-js": "3.33.2",
"cropperjs": "1.6.1",
"date-fns": "2.30.0",
"date-fns-tz": "2.0.0",
"deep-clone-simple": "1.1.1",
"deep-freeze": "0.0.1",
"fuse.js": "6.6.2",
"fuse.js": "7.0.0",
"google-timezones-json": "1.2.0",
"hls.js": "1.4.12",
"home-assistant-js-websocket": "9.1.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.5.4",
"intl-messageformat": "10.5.5",
"js-yaml": "4.1.0",
"leaflet": "1.9.4",
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.4.3",
"marked": "9.1.2",
"marked": "9.1.6",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
"punycode": "2.3.0",
"punycode": "2.3.1",
"qr-scanner": "1.4.2",
"qrcode": "1.5.3",
"resize-observer-polyfill": "1.5.1",
@@ -138,10 +138,10 @@
"tinykeys": "2.1.0",
"tsparticles-engine": "2.12.0",
"tsparticles-preset-links": "2.12.0",
"ua-parser-js": "1.0.36",
"ua-parser-js": "1.0.37",
"unfetch": "5.0.0",
"vis-data": "7.1.7",
"vis-network": "9.1.8",
"vis-data": "7.1.8",
"vis-network": "9.1.9",
"vue": "2.7.15",
"vue2-daterange-picker": "0.6.8",
"weekstart": "2.0.0",
@@ -154,12 +154,12 @@
"xss": "1.0.14"
},
"devDependencies": {
"@babel/core": "7.23.2",
"@babel/plugin-proposal-decorators": "7.23.2",
"@babel/plugin-transform-runtime": "7.23.2",
"@babel/preset-env": "7.23.2",
"@babel/preset-typescript": "7.23.2",
"@bundle-stats/plugin-webpack-filter": "4.7.8",
"@babel/core": "7.23.3",
"@babel/plugin-proposal-decorators": "7.23.3",
"@babel/plugin-transform-runtime": "7.23.3",
"@babel/preset-env": "7.23.3",
"@babel/preset-typescript": "7.23.3",
"@bundle-stats/plugin-webpack-filter": "4.8.0",
"@koa/cors": "4.0.0",
"@lokalise/node-api": "12.0.0",
"@octokit/auth-oauth-device": "6.0.1",
@@ -170,33 +170,32 @@
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-json": "6.0.1",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.4",
"@types/babel__plugin-transform-runtime": "7.9.4",
"@types/chromecast-caf-receiver": "6.0.11",
"@types/chromecast-caf-sender": "1.0.7",
"@types/esprima": "4.0.5",
"@rollup/plugin-replace": "5.0.5",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.12",
"@types/chromecast-caf-sender": "1.0.8",
"@types/glob": "8.1.0",
"@types/html-minifier-terser": "7.0.1",
"@types/js-yaml": "4.0.8",
"@types/leaflet": "1.9.7",
"@types/leaflet-draw": "1.0.9",
"@types/luxon": "3.3.3",
"@types/mocha": "10.0.3",
"@types/qrcode": "1.5.4",
"@types/serve-handler": "6.1.3",
"@types/sortablejs": "1.15.4",
"@types/tar": "6.1.7",
"@types/ua-parser-js": "0.7.38",
"@types/html-minifier-terser": "7.0.2",
"@types/js-yaml": "4.0.9",
"@types/leaflet": "1.9.8",
"@types/leaflet-draw": "1.0.10",
"@types/luxon": "3.3.4",
"@types/mocha": "10.0.4",
"@types/qrcode": "1.5.5",
"@types/serve-handler": "6.1.4",
"@types/sortablejs": "1.15.5",
"@types/tar": "6.1.9",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "6.9.0",
"@typescript-eslint/parser": "6.9.0",
"@typescript-eslint/eslint-plugin": "6.10.0",
"@typescript-eslint/parser": "6.10.0",
"@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.52.0",
"eslint": "8.53.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "17.1.0",
"eslint-config-prettier": "9.0.0",
@@ -204,10 +203,9 @@
"eslint-plugin-disable": "2.0.3",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-lit": "1.10.1",
"eslint-plugin-lit-a11y": "4.1.0",
"eslint-plugin-lit-a11y": "4.1.1",
"eslint-plugin-unused-imports": "3.0.0",
"eslint-plugin-wc": "2.0.4",
"esprima": "4.0.1",
"fancy-log": "2.0.0",
"fs-extra": "11.1.1",
"glob": "10.3.10",
@@ -221,7 +219,7 @@
"husky": "8.0.3",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.0.2",
"lint-staged": "15.1.0",
"lit-analyzer": "2.0.1",
"lodash.template": "4.5.0",
"magic-string": "0.30.5",
@@ -236,12 +234,12 @@
"rollup-plugin-terser": "7.0.2",
"rollup-plugin-visualizer": "5.9.2",
"serve-handler": "6.1.5",
"sinon": "17.0.0",
"sinon": "17.0.1",
"source-map-url": "0.4.1",
"systemjs": "6.14.2",
"tar": "6.2.0",
"terser-webpack-plugin": "5.3.9",
"ts-lit-plugin": "2.0.0",
"ts-lit-plugin": "2.0.1",
"typescript": "5.2.2",
"vinyl-buffer": "1.0.1",
"vinyl-source-stream": "2.0.0",
@@ -257,9 +255,11 @@
"resolutions": {
"@polymer/polymer": "patch:@polymer/[email protected]#./.yarn/patches/@polymer/polymer/pr-5569.patch",
"@material/mwc-button@^0.25.3": "^0.27.0",
"lit@^2.7.4 || ^3.0.0": "^2.7.4",
"lit": "2.8.0",
"clean-css": "5.3.2",
"@lit/reactive-element": "1.6.3",
"[email protected]": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
"[email protected]": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
},
"packageManager": "yarn@3.6.4"
"packageManager": "yarn@4.0.1"
}
+1 -23
View File
@@ -1,23 +1 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="480.000000pt" height="480.000000pt" viewBox="0 0 480.000000 480.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,480.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2313 4666 c-23 -7 -56 -23 -75 -34 -47 -30 -2059 -2048 -2095 -2102
-45 -67 -77 -135 -109 -230 l-29 -85 0 -995 0 -995 27 -51 c31 -59 93 -118
152 -145 39 -18 83 -19 1001 -19 l960 0 -406 405 c-395 395 -406 406 -433 395
-15 -5 -63 -10 -107 -10 -429 0 -566 577 -181 767 67 34 86 38 164 42 105 4
165 -13 246 -67 113 -74 175 -190 176 -327 1 -44 -3 -96 -7 -115 l-8 -35 316
-315 315 -315 0 1160 -1 1160 -51 35 c-260 177 -226 567 62 704 82 39 209 48
293 21 239 -78 354 -352 242 -575 -32 -63 -89 -125 -141 -156 l-44 -26 0 -811
0 -812 315 315 c218 217 313 320 309 330 -14 35 -16 134 -4 190 26 122 111
227 230 284 82 39 209 48 293 21 115 -38 214 -130 258 -242 19 -46 23 -78 24
-153 0 -86 -3 -101 -32 -163 -40 -84 -118 -163 -198 -202 -49 -23 -77 -29
-150 -33 -50 -2 -108 1 -130 7 l-40 11 -437 -438 -438 -437 0 -307 0 -308 998
0 c981 0 998 1 1042 21 58 26 115 81 148 144 l27 50 0 995 0 995 -33 95 c-72
209 -6 135 -1147 1278 -840 843 -1040 1037 -1082 1059 -64 31 -159 39 -220 19z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="640" viewBox="0 0 240 240"><path d="M120.001 0c-3.787 0-7.573 1.499-10.444 4.49L12.211 105.905a25.921 25.921 0 0 0-2.098 2.501 35.25 35.25 0 0 0-1.96 2.942c-3.01 5.021-5.285 11.318-6.074 16.898-.03.21-.088.429-.11.636a27.355 27.355 0 0 0-.213 3.317v93.023a14.78 14.78 90 0 0 14.78 14.78h90.92L67.422 198.29a20.2 20.2 90 1 1 12.542-13.06l31.17 32.474V98.726a20.2 20.2 90 1 1 17.734 0v83.44l31.001-32.299a20.2 20.2 90 1 1 12.267 13.357l-43.269 45.082V240h94.9a14.479 14.479 90 0 0 14.478-14.479v-93.314c0-1.059-.069-2.168-.214-3.314-.7-5.73-3.06-12.327-6.183-17.537a35.801 35.801 0 0 0-1.955-2.937 26.271 26.271 0 0 0-2.102-2.506L130.444 4.486C127.573 1.494 123.786-.002 120.001 0"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 747 B

+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20231027.0"
version = "20231030.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
+13 -1
View File
@@ -1,5 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"configMigration": true,
"extends": [
":ignoreModulesAndTests",
":label(Dependencies)",
@@ -10,7 +11,7 @@
"group:recommended",
"npm:unpublishSafe"
],
"enabledManagers": ["npm"],
"enabledManagers": ["npm", "nvm"],
"postUpdateOptions": ["yarnDedupeHighest"],
"lockFileMaintenance": {
"description": ["Run after patch releases but before next beta"],
@@ -28,11 +29,22 @@
"matchPackageNames": ["vue"],
"allowedVersions": "< 3"
},
{
"description": "Group MDI packages",
"groupName": "Material Design Icons",
"matchPackageNames": ["@mdi/js", "@mdi/svg"]
},
{
"description": "Group tsparticles engine and presets",
"groupName": "tsparticles",
"matchPackageNames": ["tsparticles-engine"],
"matchPackagePrefixes": ["tsparticles-preset-"]
},
{
"description": "Group and temporarily disable WDS packages",
"groupName": "Web Dev Server",
"matchPackagePrefixes": ["@web/dev-server"],
"enabled": false
}
]
}
+5 -23
View File
@@ -8,7 +8,6 @@ import "../components/ha-alert";
import "../components/ha-checkbox";
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
import "../components/ha-formfield";
import "../components/ha-markdown";
import { AuthProvider, autocompleteLoginFields } from "../data/auth";
import {
DataEntryFlowStep,
@@ -182,24 +181,13 @@ export class HaAuthFlow extends LitElement {
case "abort":
return html`
${this.localize("ui.panel.page-authorize.abort_intro")}:
<ha-markdown
allowsvg
breaks
.content=${this.localize(
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
)}
></ha-markdown>
${this.localize(
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
)}
`;
case "form":
return html`
${this._computeStepDescription(step)
? html`
<ha-markdown
breaks
.content=${this._computeStepDescription(step)}
></ha-markdown>
`
: nothing}
${this._computeStepDescription(step)}
<ha-auth-form
.data=${this._stepData}
.schema=${autocompleteLoginFields(step.data_schema)}
@@ -314,13 +302,7 @@ export class HaAuthFlow extends LitElement {
private _computeStepDescription(step: DataEntryFlowStepForm) {
const resourceKey =
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description` as const;
const args: string[] = [];
const placeholders = step.description_placeholders || {};
Object.keys(placeholders).forEach((key) => {
args.push(key);
args.push(placeholders[key]);
});
return this.localize(resourceKey, ...args);
return this.localize(resourceKey, step.description_placeholders);
}
private _computeLabelCallback(step: DataEntryFlowStepForm) {
+2 -2
View File
@@ -58,10 +58,10 @@ const matchMaxScale = (
return outputColors.map((value) => Math.round(value * factor));
};
const mired2kelvin = (miredTemperature: number) =>
export const mired2kelvin = (miredTemperature: number) =>
Math.floor(1000000 / miredTemperature);
const kelvin2mired = (kelvintTemperature: number) =>
export const kelvin2mired = (kelvintTemperature: number) =>
Math.floor(1000000 / kelvintTemperature);
export const rgbww2rgb = (
+4 -11
View File
@@ -18,6 +18,7 @@ import { blankBeforePercent } from "../translations/blank_before_percent";
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,
@@ -55,20 +56,12 @@ export const computeAttributeValueDisplay = (
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
}
if (unit === "%") {
return `${formattedValue}${blankBeforePercent(locale)}${unit}`;
}
if (unit === "°") {
return `${formattedValue}${unit}`;
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
unit = config.unit_system.temperature;
}
if (unit) {
return `${formattedValue} ${unit}`;
}
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
return `${formattedValue} ${config.unit_system.temperature}`;
return `${formattedValue}${blankBeforeUnit(unit, locale)}${unit}`;
}
return formattedValue;
+14 -10
View File
@@ -3,13 +3,13 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { FrontendLocaleData, TimeZone } from "../../data/translation";
import {
updateIsInstallingFromAttributes,
UPDATE_SUPPORT_PROGRESS,
updateIsInstallingFromAttributes,
} from "../../data/update";
import { HomeAssistant } from "../../types";
import {
formatDuration,
UNIT_TO_MILLISECOND_CONVERT,
formatDuration,
} from "../datetime/duration";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
@@ -19,10 +19,10 @@ import {
getNumberFormatOptions,
isNumericFromAttributes,
} from "../number/format_number";
import { blankBeforePercent } from "../translations/blank_before_percent";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
import { supportsFeatureFromAttributes } from "./supports-feature";
import { blankBeforeUnit } from "../translations/blank_before_unit";
export const computeStateDisplaySingleEntity = (
localize: LocalizeFunc,
@@ -108,16 +108,20 @@ export const computeStateDisplayFromEntityAttributes = (
// fallback to default
}
}
const unit = !attributes.unit_of_measurement
? ""
: attributes.unit_of_measurement === "%"
? blankBeforePercent(locale) + "%"
: ` ${attributes.unit_of_measurement}`;
return `${formatNumber(
const value = formatNumber(
state,
locale,
getNumberFormatOptions({ state, attributes } as HassEntity, entity)
)}${unit}`;
);
const unit = attributes.unit_of_measurement;
if (unit) {
return `${value}${blankBeforeUnit(unit)}${unit}`;
}
return value;
}
const domain = computeDomain(entityId);
@@ -0,0 +1,15 @@
import { FrontendLocaleData } from "../../data/translation";
import { blankBeforePercent } from "./blank_before_percent";
export const blankBeforeUnit = (
unit: string,
localeOptions?: FrontendLocaleData
): string => {
if (unit === "°") {
return "";
}
if (localeOptions && unit === "%") {
return blankBeforePercent(localeOptions);
}
return " ";
};
+1
View File
@@ -23,6 +23,7 @@ export function computeDirectionStyles(isRTL: boolean, element: LitElement) {
}
export function setDirectionStyles(direction: string, element: LitElement) {
document.dir = direction;
element.style.direction = direction;
element.style.setProperty("--direction", direction);
element.style.setProperty(
+1 -1
View File
@@ -134,7 +134,7 @@ _adapters._date.override({
this.options.config
);
case "week":
return formatDate(
return formatDateVeryShort(
new Date(time),
this.options.locale,
this.options.config
+15 -1
View File
@@ -73,6 +73,8 @@ export class StatisticsChart extends LitElement {
@property({ type: Boolean }) public isLoadingData = false;
@property() public period?: string;
@state() private _chartData: ChartData = { datasets: [] };
@state() private _statisticIds: string[] = [];
@@ -92,7 +94,12 @@ export class StatisticsChart extends LitElement {
}
public willUpdate(changedProps: PropertyValues) {
if (!this.hasUpdated || changedProps.has("unit")) {
if (
!this.hasUpdated ||
changedProps.has("unit") ||
changedProps.has("period") ||
changedProps.has("chartType")
) {
this._createOptions();
}
if (
@@ -160,6 +167,7 @@ export class StatisticsChart extends LitElement {
},
},
ticks: {
source: this.chartType === "bar" ? "data" : undefined,
maxRotation: 0,
sampleSize: 5,
autoSkipPadding: 20,
@@ -173,6 +181,12 @@ export class StatisticsChart extends LitElement {
},
time: {
tooltipFormat: "datetime",
unit:
this.chartType === "bar" &&
this.period &&
["hour", "day", "week", "month"].includes(this.period)
? this.period
: undefined,
},
},
y: {
+53
View File
@@ -0,0 +1,53 @@
import { MdAssistChip } from "@material/web/chips/assist-chip";
import { css, html } from "lit";
import { customElement, property } from "lit/decorators";
@customElement("ha-assist-chip")
export class HaAssistChip extends MdAssistChip {
@property({ type: Boolean, reflect: true }) filled = false;
static override styles = [
...super.styles,
css`
:host {
--md-sys-color-primary: var(--primary-text-color);
--md-sys-color-on-surface: var(--primary-text-color);
--md-assist-chip-container-shape: 16px;
--md-assist-chip-outline-color: var(--outline-color);
--md-assist-chip-label-text-weight: 400;
--ha-assist-chip-filled-container-color: rgba(
var(--rgb-primary-text-color),
0.15
);
}
/** Material 3 doesn't have a filled chip, so we have to make our own **/
.filled {
display: flex;
pointer-events: none;
border-radius: inherit;
inset: 0;
position: absolute;
background-color: var(--ha-assist-chip-filled-container-color);
}
/** Set the size of mdc icons **/
::slotted([slot="icon"]) {
display: flex;
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
}
`,
];
protected override renderOutline() {
if (this.filled) {
return html`<span class="filled"></span>`;
}
return super.renderOutline();
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-assist-chip": HaAssistChip;
}
}
+11
View File
@@ -0,0 +1,11 @@
import { MdChipSet } from "@material/web/chips/chip-set";
import { customElement } from "lit/decorators";
@customElement("ha-chip-set")
export class HaChipSet extends MdChipSet {}
declare global {
interface HTMLElementTagNameMap {
"ha-chip-set": HaChipSet;
}
}
+41
View File
@@ -0,0 +1,41 @@
import { MdFilterChip } from "@material/web/chips/filter-chip";
import { css, html } from "lit";
import { customElement, property } from "lit/decorators";
@customElement("ha-filter-chip")
export class HaFilterChip extends MdFilterChip {
@property({ type: Boolean, reflect: true, attribute: "no-leading-icon" })
noLeadingIcon = false;
static override styles = [
...super.styles,
css`
:host {
--md-sys-color-primary: var(--primary-text-color);
--md-sys-color-on-surface: var(--primary-text-color);
--md-sys-color-on-surface-variant: var(--primary-text-color);
--md-sys-color-on-secondary-container: var(--primary-text-color);
--md-filter-chip-container-shape: 16px;
--md-filter-chip-outline-color: var(--outline-color);
--md-filter-chip-selected-container-color: rgba(
var(--rgb-primary-text-color),
0.15
);
}
`,
];
protected renderLeadingIcon() {
if (this.noLeadingIcon) {
// eslint-disable-next-line lit/prefer-nothing
return html``;
}
return super.renderLeadingIcon();
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-filter-chip": HaFilterChip;
}
}
+35
View File
@@ -0,0 +1,35 @@
import { MdInputChip } from "@material/web/chips/input-chip";
import { css } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-input-chip")
export class HaInputChip extends MdInputChip {
static override styles = [
...super.styles,
css`
:host {
--md-sys-color-primary: var(--primary-text-color);
--md-sys-color-on-surface: var(--primary-text-color);
--md-sys-color-on-surface-variant: var(--primary-text-color);
--md-sys-color-on-secondary-container: var(--primary-text-color);
--md-input-chip-container-shape: 16px;
--md-input-chip-outline-color: var(--outline-color);
--md-input-chip-selected-container-color: rgba(
var(--rgb-primary-text-color),
0.15
);
}
/** Set the size of mdc icons **/
::slotted([slot="icon"]) {
display: flex;
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-input-chip": HaInputChip;
}
}
+1 -2
View File
@@ -21,8 +21,7 @@ export class HaButton extends Button {
display: flex;
}
.slot-container {
width: 100%;
overflow: hidden;
overflow: var(--button-slot-container-overflow, visible);
}
`,
];
-38
View File
@@ -1,38 +0,0 @@
// @ts-ignore
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
import {
css,
CSSResultGroup,
html,
LitElement,
TemplateResult,
unsafeCSS,
} from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-chip-set")
export class HaChipSet extends LitElement {
protected render(): TemplateResult {
return html`
<div class="mdc-chip-set">
<slot></slot>
</div>
`;
}
static get styles(): CSSResultGroup {
return css`
${unsafeCSS(chipStyles)}
slot::slotted(ha-chip) {
margin: 4px 4px 4px 0;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-chip-set": HaChipSet;
}
}
-85
View File
@@ -1,85 +0,0 @@
// @ts-ignore
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
import { css, CSSResultGroup, html, LitElement, nothing, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators";
@customElement("ha-chip")
export class HaChip extends LitElement {
@property({ type: Boolean }) public hasIcon = false;
@property({ type: Boolean }) public hasTrailingIcon = false;
@property({ type: Boolean }) public noText = false;
protected render() {
return html`
<div class="mdc-chip ${this.noText ? "no-text" : ""}">
${this.hasIcon
? html`<div class="mdc-chip__icon mdc-chip__icon--leading">
<slot name="icon"></slot>
</div>`
: nothing}
<div class="mdc-chip__ripple"></div>
<span role="gridcell">
<span role="button" tabindex="0" class="mdc-chip__primary-action">
<span class="mdc-chip__text"><slot></slot></span>
</span>
</span>
${this.hasTrailingIcon
? html`<div class="mdc-chip__icon mdc-chip__icon--trailing">
<slot name="trailing-icon"></slot>
</div>`
: nothing}
</div>
`;
}
static get styles(): CSSResultGroup {
return css`
${unsafeCSS(chipStyles)}
.mdc-chip {
background-color: var(
--ha-chip-background-color,
rgba(var(--rgb-primary-text-color), 0.15)
);
color: var(--ha-chip-text-color, var(--primary-text-color));
}
.mdc-chip.no-text {
padding: 0 10px;
}
.mdc-chip:hover {
color: var(--ha-chip-text-color, var(--primary-text-color));
}
.mdc-chip__icon--leading,
.mdc-chip__icon--trailing {
--mdc-icon-size: 18px;
line-height: 14px;
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
}
.mdc-chip.mdc-chip--selected .mdc-chip__checkmark,
.mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
margin-right: -4px;
margin-inline-start: -4px;
margin-inline-end: 4px;
direction: var(--direction);
}
span[role="gridcell"] {
line-height: 14px;
}
:host {
outline: none;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-chip": HaChip;
}
}
+27 -5
View File
@@ -1,11 +1,19 @@
import { mdiMinus, mdiPlus } from "@mdi/js";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import {
CSSResultGroup,
LitElement,
TemplateResult,
css,
html,
nothing,
} from "lit";
import { customElement, property, query } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import { conditionalClamp } from "../common/number/clamp";
import { formatNumber } from "../common/number/format_number";
import { blankBeforeUnit } from "../common/translations/blank_before_unit";
import { FrontendLocaleData } from "../data/translation";
import { fireEvent } from "../common/dom/fire_event";
const A11Y_KEY_CODES = new Set([
"ArrowRight",
@@ -34,6 +42,8 @@ export class HaControlNumberButton extends LitElement {
@property({ type: Number }) public max?: number;
@property() public unit?: string;
@property({ attribute: "false" })
public formatOptions: Intl.NumberFormatOptions = {};
@@ -114,26 +124,28 @@ export class HaControlNumberButton extends LitElement {
}
protected render(): TemplateResult {
const displayedValue =
const value =
this.value != null
? formatNumber(this.value, this.locale, this.formatOptions)
: "";
const unit = this.unit ? `${blankBeforeUnit(this.unit)}${this.unit}` : "";
return html`
<div class="container">
<div
id="input"
class="value"
role="number-button"
role="spinbutton"
.tabIndex=${this.disabled ? "-1" : "0"}
aria-valuenow=${this.value}
aria-valuetext=${`${value}${unit}`}
aria-valuemin=${this.min}
aria-valuemax=${this.max}
aria-label=${ifDefined(this.label)}
?disabled=${this.disabled}
@keydown=${this._handleKeyDown}
>
${displayedValue}
${value} ${unit ? html`<span class="unit">${unit}</span>` : nothing}
</div>
<button
class="button minus"
@@ -185,6 +197,8 @@ export class HaControlNumberButton extends LitElement {
position: relative;
width: 100%;
height: 100%;
container-type: inline-size;
container-name: container;
}
.value {
display: flex;
@@ -249,6 +263,14 @@ export class HaControlNumberButton extends LitElement {
.button.plus {
right: 0;
}
.unit {
white-space: pre;
}
@container container (max-width: 100px) {
.unit {
display: none;
}
}
`;
}
}
+200 -30
View File
@@ -4,13 +4,17 @@ import {
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, query } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import { FrontendLocaleData } from "../data/translation";
import { formatNumber } from "../common/number/format_number";
import { blankBeforeUnit } from "../common/translations/blank_before_unit";
declare global {
interface HASSDomEvents {
@@ -29,13 +33,21 @@ const A11Y_KEY_CODES = new Set([
"End",
]);
type TooltipPosition = "top" | "bottom" | "left" | "right";
type TooltipMode = "never" | "always" | "interaction";
type SliderMode = "start" | "end" | "cursor";
@customElement("ha-control-slider")
export class HaControlSlider extends LitElement {
@property({ attribute: false }) public locale?: FrontendLocaleData;
@property({ type: Boolean, reflect: true })
public disabled = false;
@property()
public mode?: "start" | "end" | "cursor" = "start";
public mode?: SliderMode = "start";
@property({ type: Boolean, reflect: true })
public vertical = false;
@@ -46,6 +58,15 @@ export class HaControlSlider extends LitElement {
@property({ type: Boolean, attribute: "inverted" })
public inverted = false;
@property({ attribute: "tooltip-position" })
public tooltipPosition?: TooltipPosition;
@property()
public unit?: string;
@property({ attribute: "tooltip-mode" })
public tooltipMode: TooltipMode = "interaction";
@property({ type: Number })
public value?: number;
@@ -58,11 +79,14 @@ export class HaControlSlider extends LitElement {
@property({ type: Number })
public max = 100;
private _mc?: HammerManager;
@property({ type: Boolean, reflect: true })
@state()
public pressed = false;
@state()
public tooltipVisible = false;
private _mc?: HammerManager;
valueToPercentage(value: number) {
const percentage =
(this.boundedValue(value) - this.min) / (this.max - this.min);
@@ -98,6 +122,7 @@ export class HaControlSlider extends LitElement {
if (changedProps.has("value")) {
const valuenow = this.steppedValue(this.value ?? 0);
this.setAttribute("aria-valuenow", valuenow.toString());
this.setAttribute("aria-valuetext", this._formatValue(valuenow));
}
if (changedProps.has("min")) {
this.setAttribute("aria-valuemin", this.min.toString());
@@ -143,11 +168,13 @@ export class HaControlSlider extends LitElement {
this._mc.on("panstart", () => {
if (this.disabled) return;
this.pressed = true;
this._showTooltip();
savedValue = this.value;
});
this._mc.on("pancancel", () => {
if (this.disabled) return;
this.pressed = false;
this._hideTooltip();
this.value = savedValue;
});
this._mc.on("panmove", (e) => {
@@ -160,6 +187,7 @@ export class HaControlSlider extends LitElement {
this._mc.on("panend", (e) => {
if (this.disabled) return;
this.pressed = false;
this._hideTooltip();
const percentage = this._getPercentageFromEvent(e);
this.value = this.steppedValue(this.percentageToValue(percentage));
fireEvent(this, "slider-moved", { value: undefined });
@@ -191,6 +219,21 @@ export class HaControlSlider extends LitElement {
return Math.max(this.step, (this.max - this.min) / 10);
}
_showTooltip() {
if (this._tooltipTimeout != null) window.clearTimeout(this._tooltipTimeout);
this.tooltipVisible = true;
}
_hideTooltip(delay?: number) {
if (!delay) {
this.tooltipVisible = false;
return;
}
this._tooltipTimeout = window.setTimeout(() => {
this.tooltipVisible = false;
}, delay);
}
_handleKeyDown(e: KeyboardEvent) {
if (!A11Y_KEY_CODES.has(e.code)) return;
e.preventDefault();
@@ -220,12 +263,16 @@ export class HaControlSlider extends LitElement {
this.value = this.max;
break;
}
this._showTooltip();
fireEvent(this, "slider-moved", { value: this.value });
}
private _tooltipTimeout?: number;
_handleKeyUp(e: KeyboardEvent) {
if (!A11Y_KEY_CODES.has(e.code)) return;
e.preventDefault();
this._hideTooltip(500);
fireEvent(this, "value-changed", { value: this.value });
}
@@ -242,36 +289,76 @@ export class HaControlSlider extends LitElement {
return Math.max(Math.min(1, (x - offset) / total), 0);
};
private _formatValue(value: number) {
const formattedValue = formatNumber(value, this.locale);
const formattedUnit = this.unit
? `${blankBeforeUnit(this.unit, this.locale)}${this.unit}`
: "";
return `${formattedValue}${formattedUnit}`;
}
private _renderTooltip() {
if (this.tooltipMode === "never") return nothing;
const position = this.tooltipPosition ?? (this.vertical ? "left" : "top");
const visible =
this.tooltipMode === "always" ||
(this.tooltipVisible && this.tooltipMode === "interaction");
const value = this.steppedValue(this.value ?? 0);
return html`
<span
aria-hidden="true"
class="tooltip ${classMap({
visible,
[position]: true,
[this.mode ?? "start"]: true,
"show-handle": this.showHandle,
})}"
>
${this._formatValue(value)}
</span>
`;
}
protected render(): TemplateResult {
return html`
<div
id="slider"
class="slider"
class="container${classMap({
pressed: this.pressed,
})}"
style=${styleMap({
"--value": `${this.valueToPercentage(this.value ?? 0)}`,
})}
>
<div class="slider-track-background"></div>
<slot name="background"></slot>
${this.mode === "cursor"
? this.value != null
? html`
<div id="slider" class="slider">
<div class="slider-track-background"></div>
<slot name="background"></slot>
${this.mode === "cursor"
? this.value != null
? html`
<div
class=${classMap({
"slider-track-cursor": true,
})}
></div>
`
: null
: html`
<div
class=${classMap({
"slider-track-cursor": true,
"slider-track-bar": true,
[this.mode ?? "start"]: true,
"show-handle": this.showHandle,
})}
></div>
`
: null
: html`
<div
class=${classMap({
"slider-track-bar": true,
[this.mode ?? "start"]: true,
"show-handle": this.showHandle,
})}
></div>
`}
`}
</div>
${this._renderTooltip()}
</div>
`;
}
@@ -285,6 +372,7 @@ export class HaControlSlider extends LitElement {
--control-slider-background-opacity: 0.2;
--control-slider-thickness: 40px;
--control-slider-border-radius: 10px;
--control-slider-tooltip-font-size: 14px;
height: var(--control-slider-thickness);
width: 100%;
border-radius: var(--control-slider-border-radius);
@@ -298,6 +386,89 @@ export class HaControlSlider extends LitElement {
width: var(--control-slider-thickness);
height: 100%;
}
.container {
position: relative;
height: 100%;
width: 100%;
--handle-size: 4px;
--handle-margin: calc(var(--control-slider-thickness) / 8);
}
.tooltip {
pointer-events: none;
user-select: none;
position: absolute;
background-color: var(--clear-background-color);
color: var(--primary-text-color);
font-size: var(--control-slider-tooltip-font-size);
border-radius: 0.8em;
padding: 0.2em 0.4em;
opacity: 0;
white-space: nowrap;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
transition:
opacity 180ms ease-in-out,
left 180ms ease-in-out,
bottom 180ms ease-in-out;
--handle-spacing: calc(2 * var(--handle-margin) + var(--handle-size));
--slider-tooltip-margin: -4px;
--slider-tooltip-range: 100%;
--slider-tooltip-offset: 0px;
--slider-tooltip-position: calc(
min(
max(
var(--value) * var(--slider-tooltip-range) +
var(--slider-tooltip-offset),
0%
),
100%
)
);
}
.tooltip.start {
--slider-tooltip-offset: calc(-0.5 * (var(--handle-spacing)));
}
.tooltip.end {
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
}
.tooltip.cursor {
--slider-tooltip-range: calc(100% - var(--handle-spacing));
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
}
.tooltip.show-handle {
--slider-tooltip-range: calc(100% - var(--handle-spacing));
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
}
.tooltip.visible {
opacity: 1;
}
.tooltip.top {
transform: translate3d(-50%, -100%, 0);
top: var(--slider-tooltip-margin);
left: 50%;
}
.tooltip.bottom {
transform: translate3d(-50%, 100%, 0);
bottom: var(--slider-tooltip-margin);
left: 50%;
}
.tooltip.left {
transform: translate3d(-100%, 50%, 0);
bottom: 50%;
left: var(--slider-tooltip-margin);
}
.tooltip.right {
transform: translate3d(100%, 50%, 0);
bottom: 50%;
right: var(--slider-tooltip-margin);
}
:host(:not([vertical])) .tooltip.top,
:host(:not([vertical])) .tooltip.bottom {
left: var(--slider-tooltip-position);
}
:host([vertical]) .tooltip.right,
:host([vertical]) .tooltip.left {
bottom: var(--slider-tooltip-position);
}
.slider {
position: relative;
height: 100%;
@@ -328,8 +499,6 @@ export class HaControlSlider extends LitElement {
}
.slider .slider-track-bar {
--border-radius: var(--control-slider-border-radius);
--handle-size: 4px;
--handle-margin: calc(var(--control-slider-thickness) / 8);
--slider-size: 100%;
position: absolute;
height: 100%;
@@ -432,7 +601,6 @@ export class HaControlSlider extends LitElement {
.slider .slider-track-cursor {
--cursor-size: calc(var(--control-slider-thickness) / 4);
--handle-size: 4px;
position: absolute;
background-color: white;
border-radius: var(--handle-size);
@@ -462,9 +630,11 @@ export class HaControlSlider extends LitElement {
height: var(--handle-size);
width: 50%;
}
:host([pressed]) .slider-track-bar,
:host([pressed]) .slider-track-cursor {
.pressed .tooltip {
transition: opacity 180ms ease-in-out;
}
.pressed .slider-track-bar,
.pressed .slider-track-cursor {
transition: none;
}
:host(:disabled) .slider {
+8 -1
View File
@@ -19,11 +19,14 @@ import type {
HaFormStringData,
HaFormStringSchema,
} from "./types";
import { HomeAssistant } from "../../types";
const MASKED_FIELDS = ["password", "secret", "token"];
@customElement("ha-form-string")
export class HaFormString extends LitElement implements HaFormElement {
@property() public hass?: HomeAssistant;
@property() public schema!: HaFormStringSchema;
@property() public data!: HaFormStringData;
@@ -78,7 +81,11 @@ export class HaFormString extends LitElement implements HaFormElement {
return html`
<ha-icon-button
toggles
.label=${`${this.unmaskedPassword ? "Hide" : "Show"} password`}
.label=${this.hass?.localize(
this.unmaskedPassword
? "ui.components.selectors.text.hide_password"
: "ui.components.selectors.text.show_password"
) || (this.unmaskedPassword ? "Hide password" : "Show password")}
@click=${this.toggleUnmaskedPassword}
.path=${this.unmaskedPassword ? mdiEyeOff : mdiEye}
></ha-icon-button>
+60
View File
@@ -0,0 +1,60 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-label")
class HaLabel extends LitElement {
protected render(): TemplateResult {
return html`
<span class="label">
<slot name="icon"></slot>
<slot></slot>
</div>
`;
}
static get styles(): CSSResultGroup {
return [
css`
:host {
--ha-label-text-color: var(--primary-text-color);
--ha-label-icon-color: var(--primary-text-color);
--ha-label-background-color: rgba(
var(--rgb-primary-text-color),
0.15
);
}
.label {
display: inline-flex;
flex-direction: row;
align-items: center;
font-size: 12px;
font-weight: 500;
line-height: 16px;
letter-spacing: 0.1px;
vertical-align: middle;
height: 32px;
padding: 0 16px;
border-radius: 18px;
background-color: var(--ha-label-background-color);
color: var(--ha-label-text-color);
--mdc-icon-size: 18px;
}
::slotted([slot="icon"]) {
margin-right: 8px;
margin-left: -8px;
display: flex;
color: var(--ha-label-icon-color);
}
span {
display: inline-flex;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-label": HaLabel;
}
}
+5 -9
View File
@@ -4,18 +4,14 @@ import { customElement } from "lit/decorators";
@customElement("ha-logo-svg")
export class HaLogoSvg extends LitElement {
protected render(): TemplateResult {
return html`<svg
viewBox="0 0 240 240"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
return html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
fill="#F2F4F9"
fill="#18BCF2"
d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"
/>
<path
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
fill="#18BCF2"
fill="#F2F4F9"
d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"
/>
</svg>`;
}
+6 -9
View File
@@ -3,12 +3,9 @@ import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { titleCase } from "../common/string/title-case";
import {
fetchConfig,
LovelaceConfig,
LovelaceViewConfig,
} from "../data/lovelace";
import { ValueChangedEvent, HomeAssistant, PanelInfo } from "../types";
import { fetchConfig } from "../data/lovelace/config/types";
import { LovelaceViewRawConfig } from "../data/lovelace/config/view";
import { HomeAssistant, PanelInfo, ValueChangedEvent } from "../types";
import "./ha-combo-box";
import type { HaComboBox } from "./ha-combo-box";
import "./ha-icon";
@@ -32,7 +29,7 @@ const rowRenderer: ComboBoxLitRenderer<NavigationItem> = (item) => html`
const createViewNavigationItem = (
prefix: string,
view: LovelaceViewConfig,
view: LovelaceViewRawConfig,
index: number
) => ({
path: `/${prefix}/${view.path ?? index}`,
@@ -121,7 +118,7 @@ export class HaNavigationPicker extends LitElement {
panel.url_path === "lovelace" ? null : panel.url_path,
true
)
.then((config) => [panel.id, config] as [string, LovelaceConfig])
.then((config) => [panel.id, config] as [string, typeof config])
.catch((_) => [panel.id, undefined] as [string, undefined])
)
);
@@ -135,7 +132,7 @@ export class HaNavigationPicker extends LitElement {
const config = panelViewConfig.get(panel.id);
if (!config) continue;
if (!config || !("views" in config)) continue;
config.views.forEach((view, index) =>
this.navigationItems.push(
@@ -1,9 +1,13 @@
import { css, html, LitElement } from "lit";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import type { ColorTempSelector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import "../ha-labeled-slider";
import { generateColorTemperatureGradient } from "../../dialogs/more-info/components/lights/light-color-temp-picker";
import { mired2kelvin } from "../../common/color/convert-light-color";
@customElement("ha-selector-color_temp")
export class HaColorTempSelector extends LitElement {
@@ -22,13 +26,21 @@ export class HaColorTempSelector extends LitElement {
@property({ type: Boolean }) public required = true;
protected render() {
const min = this.selector.color_temp?.min_mireds ?? 153;
const max = this.selector.color_temp?.max_mireds ?? 500;
const gradient = this._generateTemperatureGradient(min, max);
return html`
<ha-labeled-slider
style=${styleMap({
"--ha-slider-background": `linear-gradient( to var(--float-end), ${gradient})`,
})}
labeled
icon="hass:thermometer"
.caption=${this.label || ""}
.min=${this.selector.color_temp?.min_mireds ?? 153}
.max=${this.selector.color_temp?.max_mireds ?? 500}
.min=${min}
.max=${max}
.value=${this.value}
.disabled=${this.disabled}
.helper=${this.helper}
@@ -38,22 +50,16 @@ export class HaColorTempSelector extends LitElement {
`;
}
private _generateTemperatureGradient = memoizeOne(
(min: number, max: number) =>
generateColorTemperatureGradient(mired2kelvin(min), mired2kelvin(max))
);
private _valueChanged(ev: CustomEvent) {
fireEvent(this, "value-changed", {
value: Number((ev.detail as any).value),
});
}
static styles = css`
ha-labeled-slider {
--ha-slider-background: linear-gradient(
to var(--float-end),
rgb(255, 160, 0) 0%,
white 50%,
rgb(166, 209, 255) 100%
);
}
`;
}
declare global {
@@ -1,5 +1,5 @@
import "@material/mwc-list/mwc-list-item";
import { mdiClose, mdiDrag } from "@mdi/js";
import { mdiDrag } from "@mdi/js";
import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
@@ -12,9 +12,9 @@ import type { SelectOption, SelectSelector } from "../../data/selector";
import { sortableStyles } from "../../resources/ha-sortable-style";
import { SortableInstance } from "../../resources/sortable";
import type { HomeAssistant } from "../../types";
import "../chips/ha-chip-set";
import "../chips/ha-input-chip";
import "../ha-checkbox";
import "../ha-chip";
import "../ha-chip-set";
import "../ha-combo-box";
import type { HaComboBox } from "../ha-combo-box";
import "../ha-formfield";
@@ -65,7 +65,7 @@ export class HaSelectSelector extends LitElement {
{
animation: 150,
fallbackClass: "sortable-fallback",
draggable: "ha-chip",
draggable: "ha-input-chip",
onChoose: (evt: SortableEvent) => {
(evt.item as any).placeholder =
document.createComment("sort-placeholder");
@@ -199,30 +199,31 @@ export class HaSelectSelector extends LitElement {
${repeat(
value,
(item) => item,
(item, idx) => html`
<ha-chip
hasTrailingIcon
.hasIcon=${this.selector.select?.reorder}
>
${this.selector.select?.reorder
? html`
<ha-svg-icon
slot="icon"
.path=${mdiDrag}
data-handle
></ha-svg-icon>
`
: nothing}
${options.find((option) => option.value === item)
?.label || item}
<ha-svg-icon
slot="trailing-icon"
.path=${mdiClose}
(item, idx) => {
const label =
options.find((option) => option.value === item)?.label ||
item;
return html`
<ha-input-chip
.idx=${idx}
@click=${this._removeItem}
></ha-svg-icon>
</ha-chip>
`
@remove=${this._removeItem}
.label=${label}
selected
>
${this.selector.select?.reorder
? html`
<ha-svg-icon
slot="icon"
.path=${mdiDrag}
data-handle
></ha-svg-icon>
`
: nothing}
${options.find((option) => option.value === item)
?.label || item}
</ha-input-chip>
`;
}
)}
</ha-chip-set>
`
@@ -354,6 +355,7 @@ export class HaSelectSelector extends LitElement {
}
private async _removeItem(ev) {
ev.stopPropagation();
const value: string[] = [...ensureArray(this.value!)];
value.splice(ev.target.idx, 1);
@@ -431,6 +433,9 @@ export class HaSelectSelector extends LitElement {
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
ha-chip-set {
padding: 8px 0;
}
`,
];
}
@@ -76,7 +76,11 @@ export class HaTextSelector extends LitElement {
${this.selector.text?.type === "password"
? html`<ha-icon-button
toggles
.label=${`${this._unmaskedPassword ? "Hide" : "Show"} password`}
.label=${this.hass.localize(
this._unmaskedPassword
? "ui.components.selectors.text.hide_password"
: "ui.components.selectors.text.show_password"
)}
@click=${this._toggleUnmaskedPassword}
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
></ha-icon-button>`
@@ -0,0 +1,56 @@
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { Trigger } from "../../data/automation";
import { TriggerSelector } from "../../data/selector";
import "../../panels/config/automation/trigger/ha-automation-trigger";
import { HomeAssistant } from "../../types";
@customElement("ha-selector-trigger")
export class HaTriggerSelector extends LitElement {
@property() public hass!: HomeAssistant;
@property() public selector!: TriggerSelector;
@property() public value?: Trigger;
@property() public label?: string;
@property({ type: Boolean, reflect: true }) public disabled = false;
protected render() {
return html`
${this.label ? html`<label>${this.label}</label>` : nothing}
<ha-automation-trigger
.disabled=${this.disabled}
.triggers=${this.value || []}
.hass=${this.hass}
.nested=${this.selector.trigger?.nested}
.reOrderMode=${this.selector.trigger?.reorder_mode}
></ha-automation-trigger>
`;
}
static get styles(): CSSResultGroup {
return css`
ha-automation-trigger {
display: block;
margin-bottom: 16px;
}
:host([disabled]) ha-automation-trigger {
opacity: var(--light-disabled-opacity);
pointer-events: none;
}
label {
display: block;
margin-bottom: 4px;
font-weight: 500;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-trigger": HaTriggerSelector;
}
}
@@ -1,10 +1,10 @@
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { ActionConfig } from "../../data/lovelace/config/action";
import { UiActionSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../../panels/lovelace/components/hui-action-editor";
import { ActionConfig } from "../../data/lovelace";
import { HomeAssistant } from "../../types";
@customElement("ha-selector-ui_action")
export class HaSelectorUiAction extends LitElement {
@@ -25,6 +25,7 @@ export class HaSelectorUiAction extends LitElement {
.hass=${this.hass}
.config=${this.value}
.actions=${this.selector.ui_action?.actions}
.defaultAction=${this.selector.ui_action?.default_action}
.tooltipText=${this.helper}
@value-changed=${this._valueChanged}
></hui-action-editor>
@@ -1,7 +1,6 @@
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { ActionConfig } from "../../data/lovelace";
import { UiColorSelector } from "../../data/selector";
import "../../panels/lovelace/components/hui-color-picker";
import { HomeAssistant } from "../../types";
@@ -12,7 +11,7 @@ export class HaSelectorUiColor extends LitElement {
@property() public selector!: UiColorSelector;
@property() public value?: ActionConfig;
@property() public value?: string;
@property() public label?: string;
@@ -44,6 +44,7 @@ const LOAD_ELEMENTS = {
icon: () => import("./ha-selector-icon"),
media: () => import("./ha-selector-media"),
theme: () => import("./ha-selector-theme"),
trigger: () => import("./ha-selector-trigger"),
tts: () => import("./ha-selector-tts"),
tts_voice: () => import("./ha-selector-tts-voice"),
location: () => import("./ha-selector-location"),
+1 -1
View File
@@ -41,7 +41,7 @@ import { toggleAttribute } from "../common/dom/toggle_attribute";
import { stringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl";
import { throttle } from "../common/util/throttle";
import { ActionHandlerDetail } from "../data/lovelace";
import { ActionHandlerDetail } from "../data/lovelace/action_handler";
import {
PersistentNotification,
subscribeNotifications,
@@ -899,6 +899,7 @@ export class HaMediaPlayerBrowse extends LitElement {
overflow-y: auto;
box-sizing: border-box;
height: 100%;
position: relative;
}
/* HEADER */
+32 -19
View File
@@ -642,10 +642,7 @@ const tryDescribeTrigger = (
}
// Device Trigger
if (trigger.platform === "device") {
if (!trigger.device_id) {
return "Device trigger";
}
if (trigger.platform === "device" && trigger.device_id) {
const config = trigger as DeviceTrigger;
const localized = localizeDeviceAutomationTrigger(
hass,
@@ -661,9 +658,12 @@ const tryDescribeTrigger = (
}`;
}
return `${
trigger.platform ? trigger.platform.replace(/_/g, " ") : "Unknown"
} trigger`;
return (
hass.localize(
`ui.panel.config.automation.editor.triggers.type.${trigger.platform}.label`
) ||
hass.localize(`ui.panel.config.automation.editor.triggers.unknown_trigger`)
);
};
export const describeCondition = (
@@ -1074,10 +1074,7 @@ const tryDescribeCondition = (
);
}
if (condition.condition === "device") {
if (!condition.device_id) {
return "Device condition";
}
if (condition.condition === "device" && condition.device_id) {
const config = condition as DeviceCondition;
const localized = localizeDeviceAutomationCondition(
hass,
@@ -1093,14 +1090,30 @@ const tryDescribeCondition = (
}`;
}
if (condition.condition === "trigger") {
if (!condition.id) {
return "Trigger condition";
}
return `When triggered by ${condition.id}`;
if (condition.condition === "template") {
return hass.localize(
`${conditionsTranslationBaseKey}.template.description.full`
);
}
return `${
condition.condition ? condition.condition.replace(/_/g, " ") : "Unknown"
} condition`;
if (condition.condition === "trigger" && condition.id != null) {
return hass.localize(
`${conditionsTranslationBaseKey}.trigger.description.full`,
{
id: formatListWithOrs(
hass.locale,
ensureArray(condition.id).map((id) => id.toString())
),
}
);
}
return (
hass.localize(
`ui.panel.config.automation.editor.conditions.type.${condition.condition}.label`
) ||
hass.localize(
`ui.panel.config.automation.editor.conditions.unknown_condition`
)
);
};
+2 -1
View File
@@ -139,7 +139,8 @@ export const getCalendars = (hass: HomeAssistant): Calendar[] =>
.filter(
(eid) =>
computeDomain(eid) === "calendar" &&
!isUnavailableState(hass.states[eid].state)
!isUnavailableState(hass.states[eid].state) &&
hass.entities[eid]?.hidden !== true
)
.sort()
.map((eid, idx) => ({
+2 -2
View File
@@ -34,7 +34,7 @@ export const TEMPERATURE_ATTRIBUTES = new Set([
"max_temp",
]);
export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = {
export const DOMAIN_ATTRIBUTES_UNITS = {
climate: {
humidity: "%",
current_humidity: "%",
@@ -74,4 +74,4 @@ export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = {
sensor: {
battery_level: "%",
},
};
} as const satisfies Record<string, Record<string, string>>;
+3 -272
View File
@@ -2,9 +2,7 @@ import {
Connection,
getCollection,
HassEventBase,
HassServiceTarget,
} from "home-assistant-js-websocket";
import { HASSDomEvent } from "../common/dom/fire_event";
import { HuiErrorCard } from "../panels/lovelace/cards/hui-error-card";
import {
Lovelace,
@@ -12,90 +10,13 @@ import {
LovelaceCard,
} from "../panels/lovelace/types";
import { HomeAssistant } from "../types";
import { fetchConfig, LegacyLovelaceConfig } from "./lovelace/config/types";
import { LovelaceViewConfig } from "./lovelace/config/view";
export interface LovelacePanelConfig {
mode: "yaml" | "storage";
}
export type LovelaceStrategyConfig = {
type: string;
[key: string]: any;
};
export interface LovelaceConfig {
title?: string;
strategy?: LovelaceStrategyConfig;
views: LovelaceViewConfig[];
background?: string;
}
export interface LegacyLovelaceConfig extends LovelaceConfig {
resources?: LovelaceResource[];
}
export interface LovelaceResource {
id: string;
type: "css" | "js" | "module" | "html";
url: string;
}
export interface LovelaceResourcesMutableParams {
res_type: LovelaceResource["type"];
url: string;
}
export type LovelaceDashboard =
| LovelaceYamlDashboard
| LovelaceStorageDashboard;
interface LovelaceGenericDashboard {
id: string;
url_path: string;
require_admin: boolean;
show_in_sidebar: boolean;
icon?: string;
title: string;
}
export interface LovelaceYamlDashboard extends LovelaceGenericDashboard {
mode: "yaml";
filename: string;
}
export interface LovelaceStorageDashboard extends LovelaceGenericDashboard {
mode: "storage";
}
export interface LovelaceDashboardMutableParams {
require_admin: boolean;
show_in_sidebar: boolean;
icon?: string;
title: string;
}
export interface LovelaceDashboardCreateParams
extends LovelaceDashboardMutableParams {
url_path: string;
mode: "storage";
}
export interface LovelaceViewConfig {
index?: number;
title?: string;
type?: string;
strategy?: LovelaceStrategyConfig;
badges?: Array<string | LovelaceBadgeConfig>;
cards?: LovelaceCardConfig[];
path?: string;
icon?: string;
theme?: string;
panel?: boolean;
background?: string;
visible?: boolean | ShowViewConfig[];
subview?: boolean;
back_path?: string;
}
export interface LovelaceViewElement extends HTMLElement {
hass?: HomeAssistant;
lovelace?: Lovelace;
@@ -107,89 +28,6 @@ export interface LovelaceViewElement extends HTMLElement {
setConfig(config: LovelaceViewConfig): void;
}
export interface ShowViewConfig {
user?: string;
}
export interface LovelaceBadgeConfig {
type?: string;
[key: string]: any;
}
export interface LovelaceCardConfig {
index?: number;
view_index?: number;
view_layout?: any;
type: string;
[key: string]: any;
}
export interface ToggleActionConfig extends BaseActionConfig {
action: "toggle";
}
export interface CallServiceActionConfig extends BaseActionConfig {
action: "call-service";
service: string;
target?: HassServiceTarget;
// "service_data" is kept for backwards compatibility. Replaced by "data".
service_data?: Record<string, unknown>;
data?: Record<string, unknown>;
}
export interface NavigateActionConfig extends BaseActionConfig {
action: "navigate";
navigation_path: string;
navigation_replace?: boolean;
}
export interface UrlActionConfig extends BaseActionConfig {
action: "url";
url_path: string;
}
export interface MoreInfoActionConfig extends BaseActionConfig {
action: "more-info";
}
export interface AssistActionConfig extends BaseActionConfig {
action: "assist";
pipeline_id?: string;
start_listening?: boolean;
}
export interface NoActionConfig extends BaseActionConfig {
action: "none";
}
export interface CustomActionConfig extends BaseActionConfig {
action: "fire-dom-event";
}
export interface BaseActionConfig {
action: string;
confirmation?: ConfirmationRestrictionConfig;
}
export interface ConfirmationRestrictionConfig {
text?: string;
exemptions?: RestrictionConfig[];
}
export interface RestrictionConfig {
user: string;
}
export type ActionConfig =
| ToggleActionConfig
| CallServiceActionConfig
| NavigateActionConfig
| UrlActionConfig
| MoreInfoActionConfig
| AssistActionConfig
| NoActionConfig
| CustomActionConfig;
type LovelaceUpdatedEvent = HassEventBase & {
event_type: "lovelace_updated";
data: {
@@ -198,101 +36,6 @@ type LovelaceUpdatedEvent = HassEventBase & {
};
};
export const fetchResources = (conn: Connection): Promise<LovelaceResource[]> =>
conn.sendMessagePromise({
type: "lovelace/resources",
});
export const createResource = (
hass: HomeAssistant,
values: LovelaceResourcesMutableParams
) =>
hass.callWS<LovelaceResource>({
type: "lovelace/resources/create",
...values,
});
export const updateResource = (
hass: HomeAssistant,
id: string,
updates: Partial<LovelaceResourcesMutableParams>
) =>
hass.callWS<LovelaceResource>({
type: "lovelace/resources/update",
resource_id: id,
...updates,
});
export const deleteResource = (hass: HomeAssistant, id: string) =>
hass.callWS({
type: "lovelace/resources/delete",
resource_id: id,
});
export const fetchDashboards = (
hass: HomeAssistant
): Promise<LovelaceDashboard[]> =>
hass.callWS({
type: "lovelace/dashboards/list",
});
export const createDashboard = (
hass: HomeAssistant,
values: LovelaceDashboardCreateParams
) =>
hass.callWS<LovelaceDashboard>({
type: "lovelace/dashboards/create",
...values,
});
export const updateDashboard = (
hass: HomeAssistant,
id: string,
updates: Partial<LovelaceDashboardMutableParams>
) =>
hass.callWS<LovelaceDashboard>({
type: "lovelace/dashboards/update",
dashboard_id: id,
...updates,
});
export const deleteDashboard = (hass: HomeAssistant, id: string) =>
hass.callWS({
type: "lovelace/dashboards/delete",
dashboard_id: id,
});
export const fetchConfig = (
conn: Connection,
urlPath: string | null,
force: boolean
): Promise<LovelaceConfig> =>
conn.sendMessagePromise({
type: "lovelace/config",
url_path: urlPath,
force,
});
export const saveConfig = (
hass: HomeAssistant,
urlPath: string | null,
config: LovelaceConfig
): Promise<void> =>
hass.callWS({
type: "lovelace/config/save",
url_path: urlPath,
config,
});
export const deleteConfig = (
hass: HomeAssistant,
urlPath: string | null
): Promise<void> =>
hass.callWS({
type: "lovelace/config/delete",
url_path: urlPath,
});
export const subscribeLovelaceUpdates = (
conn: Connection,
urlPath: string | null,
@@ -324,7 +67,7 @@ export const getLovelaceCollection = (
const fetchLegacyConfig = (
conn: Connection,
force: boolean
): Promise<LovelaceConfig> =>
): Promise<LegacyLovelaceConfig> =>
conn.sendMessagePromise({
type: "lovelace/config",
force,
@@ -347,15 +90,3 @@ export const getLegacyLovelaceCollection = (conn: Connection) =>
)
)
);
export interface ActionHandlerOptions {
hasHold?: boolean;
hasDoubleClick?: boolean;
disabled?: boolean;
}
export interface ActionHandlerDetail {
action: "hold" | "tap" | "double_tap";
}
export type ActionHandlerEvent = HASSDomEvent<ActionHandlerDetail>;
+13
View File
@@ -0,0 +1,13 @@
import { HASSDomEvent } from "../../common/dom/fire_event";
export interface ActionHandlerOptions {
hasHold?: boolean;
hasDoubleClick?: boolean;
disabled?: boolean;
}
export interface ActionHandlerDetail {
action: "hold" | "tap" | "double_tap";
}
export type ActionHandlerEvent = HASSDomEvent<ActionHandlerDetail>;
+67
View File
@@ -0,0 +1,67 @@
import type { HassServiceTarget } from "home-assistant-js-websocket";
export interface ToggleActionConfig extends BaseActionConfig {
action: "toggle";
}
export interface CallServiceActionConfig extends BaseActionConfig {
action: "call-service";
service: string;
target?: HassServiceTarget;
// "service_data" is kept for backwards compatibility. Replaced by "data".
service_data?: Record<string, unknown>;
data?: Record<string, unknown>;
}
export interface NavigateActionConfig extends BaseActionConfig {
action: "navigate";
navigation_path: string;
navigation_replace?: boolean;
}
export interface UrlActionConfig extends BaseActionConfig {
action: "url";
url_path: string;
}
export interface MoreInfoActionConfig extends BaseActionConfig {
action: "more-info";
}
export interface AssistActionConfig extends BaseActionConfig {
action: "assist";
pipeline_id?: string;
start_listening?: boolean;
}
export interface NoActionConfig extends BaseActionConfig {
action: "none";
}
export interface CustomActionConfig extends BaseActionConfig {
action: "fire-dom-event";
}
export interface BaseActionConfig {
action: string;
confirmation?: ConfirmationRestrictionConfig;
}
export interface ConfirmationRestrictionConfig {
text?: string;
exemptions?: RestrictionConfig[];
}
export interface RestrictionConfig {
user: string;
}
export type ActionConfig =
| ToggleActionConfig
| CallServiceActionConfig
| NavigateActionConfig
| UrlActionConfig
| MoreInfoActionConfig
| AssistActionConfig
| NoActionConfig
| CustomActionConfig;
+4
View File
@@ -0,0 +1,4 @@
export interface LovelaceBadgeConfig {
type?: string;
[key: string]: any;
}
+7
View File
@@ -0,0 +1,7 @@
export interface LovelaceCardConfig {
index?: number;
view_index?: number;
view_layout?: any;
type: string;
[key: string]: any;
}
+4
View File
@@ -0,0 +1,4 @@
export interface LovelaceStrategyConfig {
type: string;
[key: string]: any;
}
+63
View File
@@ -0,0 +1,63 @@
import type { Connection } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../../../types";
import type { LovelaceResource } from "../resource";
import type { LovelaceStrategyConfig } from "./strategy";
import type { LovelaceViewRawConfig } from "./view";
export interface LovelaceDashboardBaseConfig {}
export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
title?: string;
background?: string;
views: LovelaceViewRawConfig[];
}
export interface LovelaceDashboardStrategyConfig
extends LovelaceDashboardBaseConfig {
strategy: LovelaceStrategyConfig;
}
export interface LegacyLovelaceConfig extends LovelaceConfig {
resources?: LovelaceResource[];
}
export type LovelaceRawConfig =
| LovelaceConfig
| LovelaceDashboardStrategyConfig;
export function isStrategyDashboard(
config: LovelaceRawConfig
): config is LovelaceDashboardStrategyConfig {
return "strategy" in config;
}
export const fetchConfig = (
conn: Connection,
urlPath: string | null,
force: boolean
): Promise<LovelaceRawConfig> =>
conn.sendMessagePromise({
type: "lovelace/config",
url_path: urlPath,
force,
});
export const saveConfig = (
hass: HomeAssistant,
urlPath: string | null,
config: LovelaceRawConfig
): Promise<void> =>
hass.callWS({
type: "lovelace/config/save",
url_path: urlPath,
config,
});
export const deleteConfig = (
hass: HomeAssistant,
urlPath: string | null
): Promise<void> =>
hass.callWS({
type: "lovelace/config/delete",
url_path: urlPath,
});
+40
View File
@@ -0,0 +1,40 @@
import type { LovelaceBadgeConfig } from "./badge";
import type { LovelaceCardConfig } from "./card";
import type { LovelaceStrategyConfig } from "./strategy";
export interface ShowViewConfig {
user?: string;
}
export interface LovelaceBaseViewConfig {
index?: number;
title?: string;
path?: string;
icon?: string;
theme?: string;
panel?: boolean;
background?: string;
visible?: boolean | ShowViewConfig[];
subview?: boolean;
back_path?: string;
}
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
type?: string;
badges?: Array<string | LovelaceBadgeConfig>;
cards?: LovelaceCardConfig[];
}
export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig {
strategy: LovelaceStrategyConfig;
}
export type LovelaceViewRawConfig =
| LovelaceViewConfig
| LovelaceStrategyViewConfig;
export function isStrategyView(
view: LovelaceViewRawConfig
): view is LovelaceStrategyViewConfig {
return "strategy" in view;
}
+69
View File
@@ -0,0 +1,69 @@
import type { HomeAssistant } from "../../types";
export type LovelaceDashboard =
| LovelaceYamlDashboard
| LovelaceStorageDashboard;
interface LovelaceGenericDashboard {
id: string;
url_path: string;
require_admin: boolean;
show_in_sidebar: boolean;
icon?: string;
title: string;
}
export interface LovelaceYamlDashboard extends LovelaceGenericDashboard {
mode: "yaml";
filename: string;
}
export interface LovelaceStorageDashboard extends LovelaceGenericDashboard {
mode: "storage";
}
export interface LovelaceDashboardMutableParams {
require_admin: boolean;
show_in_sidebar: boolean;
icon?: string;
title: string;
}
export interface LovelaceDashboardCreateParams
extends LovelaceDashboardMutableParams {
url_path: string;
mode: "storage";
}
export const fetchDashboards = (
hass: HomeAssistant
): Promise<LovelaceDashboard[]> =>
hass.callWS({
type: "lovelace/dashboards/list",
});
export const createDashboard = (
hass: HomeAssistant,
values: LovelaceDashboardCreateParams
) =>
hass.callWS<LovelaceDashboard>({
type: "lovelace/dashboards/create",
...values,
});
export const updateDashboard = (
hass: HomeAssistant,
id: string,
updates: Partial<LovelaceDashboardMutableParams>
) =>
hass.callWS<LovelaceDashboard>({
type: "lovelace/dashboards/update",
dashboard_id: id,
...updates,
});
export const deleteDashboard = (hass: HomeAssistant, id: string) =>
hass.callWS({
type: "lovelace/dashboards/delete",
dashboard_id: id,
});
+44
View File
@@ -0,0 +1,44 @@
import type { Connection } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../../types";
export type LovelaceResource = {
id: string;
type: "css" | "js" | "module" | "html";
url: string;
};
export type LovelaceResourcesMutableParams = {
res_type: LovelaceResource["type"];
url: string;
};
export const fetchResources = (conn: Connection): Promise<LovelaceResource[]> =>
conn.sendMessagePromise({
type: "lovelace/resources",
});
export const createResource = (
hass: HomeAssistant,
values: LovelaceResourcesMutableParams
) =>
hass.callWS<LovelaceResource>({
type: "lovelace/resources/create",
...values,
});
export const updateResource = (
hass: HomeAssistant,
id: string,
updates: Partial<LovelaceResourcesMutableParams>
) =>
hass.callWS<LovelaceResource>({
type: "lovelace/resources/update",
resource_id: id,
...updates,
});
export const deleteResource = (hass: HomeAssistant, id: string) =>
hass.callWS({
type: "lovelace/resources/delete",
resource_id: id,
});
+3 -2
View File
@@ -1,8 +1,9 @@
import { LovelaceConfig, LovelaceResource } from "./lovelace";
import { LovelaceRawConfig } from "./lovelace/config/types";
import { LovelaceResource } from "./lovelace/resource";
import { RecorderInfo } from "./recorder";
export interface WindowWithPreloads extends Window {
llConfProm?: Promise<LovelaceConfig>;
llConfProm?: Promise<LovelaceRawConfig>;
llResProm?: Promise<LovelaceResource[]>;
recorderInfoProm?: Promise<RecorderInfo>;
}
+9
View File
@@ -49,6 +49,7 @@ export type Selector =
| TemplateSelector
| ThemeSelector
| TimeSelector
| TriggerSelector
| TTSSelector
| TTSVoiceSelector
| UiActionSelector
@@ -373,6 +374,13 @@ export interface TimeSelector {
time: {} | null;
}
export interface TriggerSelector {
trigger: {
reorder_mode?: boolean;
nested?: boolean;
} | null;
}
export interface TTSSelector {
tts: { language?: string } | null;
}
@@ -384,6 +392,7 @@ export interface TTSVoiceSelector {
export interface UiActionSelector {
ui_action: {
actions?: UiAction[];
default_action?: UiAction;
} | null;
}
+10 -5
View File
@@ -61,7 +61,12 @@ export const updateItem = (
entity_id: string,
item: TodoItem
): Promise<ServiceCallResponse> =>
hass.callService("todo", "update_item", item, { entity_id });
hass.callService(
"todo",
"update_item",
{ item: item.uid, rename: item.summary, status: item.status },
{ entity_id }
);
export const createItem = (
hass: HomeAssistant,
@@ -70,9 +75,9 @@ export const createItem = (
): Promise<ServiceCallResponse> =>
hass.callService(
"todo",
"create_item",
"add_item",
{
summary,
item: summary,
},
{ entity_id }
);
@@ -84,9 +89,9 @@ export const deleteItem = (
): Promise<ServiceCallResponse> =>
hass.callService(
"todo",
"delete_item",
"remove_item",
{
uid,
item: uid,
},
{ entity_id }
);
+1
View File
@@ -61,6 +61,7 @@ export type TranslationCategory =
| "state"
| "entity"
| "entity_component"
| "exceptions"
| "config"
| "config_panel"
| "options"
@@ -415,6 +415,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
line-height: 64px;
letter-spacing: -0.25px;
margin: 0;
direction: ltr;
}
.temperature span {
display: inline-flex;
@@ -6,6 +6,7 @@ import { stateColorCss } from "../../../../common/entity/state_color";
import "../../../../components/ha-control-slider";
import { CoverEntity } from "../../../../data/cover";
import { UNAVAILABLE } from "../../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
import { HomeAssistant } from "../../../../types";
@customElement("ha-more-info-cover-position")
@@ -60,6 +61,8 @@ export class HaMoreInfoCoverPosition extends LitElement {
"--control-slider-background": color,
})}
.disabled=${this.stateObj.state === UNAVAILABLE}
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_position}
.locale=${this.hass.locale}
>
</ha-control-slider>
`;
@@ -76,6 +79,7 @@ export class HaMoreInfoCoverPosition extends LitElement {
--control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
}
`;
}
@@ -13,6 +13,7 @@ import { stateColorCss } from "../../../../common/entity/state_color";
import "../../../../components/ha-control-slider";
import { CoverEntity } from "../../../../data/cover";
import { UNAVAILABLE } from "../../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
import { HomeAssistant } from "../../../../types";
export function generateTiltSliderTrackBackgroundGradient() {
@@ -96,6 +97,8 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
"--control-slider-background": color,
})}
.disabled=${this.stateObj.state === UNAVAILABLE}
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_tilt_position}
.locale=${this.hass.locale}
>
<div slot="background" class="gradient"></div>
</ha-control-slider>
@@ -113,6 +116,7 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
--control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
}
.gradient {
background: -webkit-linear-gradient(top, ${GRADIENT});
@@ -8,6 +8,7 @@ import "../../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../../components/ha-control-select";
import "../../../../components/ha-control-slider";
import { UNAVAILABLE } from "../../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
import {
computeFanSpeedCount,
computeFanSpeedIcon,
@@ -130,6 +131,8 @@ export class HaMoreInfoFanSpeed extends LitElement {
"--control-slider-background": color,
})}
.disabled=${this.stateObj.state === UNAVAILABLE}
.unit=${DOMAIN_ATTRIBUTES_UNITS.fan.percentage}
.locale=${this.hass.locale}
>
</ha-control-slider>
`;
@@ -146,6 +149,7 @@ export class HaMoreInfoFanSpeed extends LitElement {
--control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
}
ha-control-select {
height: 45vh;
@@ -77,6 +77,8 @@ export class HaMoreInfoLightBrightness extends LitElement {
"--control-slider-background": color,
})}
.disabled=${this.stateObj.state === UNAVAILABLE}
unit="%"
.locale=${this.hass.locale}
>
</ha-control-slider>
`;
@@ -93,6 +95,7 @@ export class HaMoreInfoLightBrightness extends LitElement {
--control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
}
`;
}
@@ -26,6 +26,7 @@ import {
LightEntity,
} from "../../../../data/light";
import { HomeAssistant } from "../../../../types";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
declare global {
interface HASSDomEvents {
@@ -93,6 +94,8 @@ class LightColorTempPicker extends LitElement {
"--gradient": gradient,
})}
.disabled=${this.stateObj.state === UNAVAILABLE}
.unit=${DOMAIN_ATTRIBUTES_UNITS.light.color_temp_kelvin}
.locale=${this.hass.locale}
>
</ha-control-slider>
`;
@@ -193,6 +196,7 @@ class LightColorTempPicker extends LitElement {
top,
var(--gradient)
);
--control-slider-tooltip-font-size: 20px;
--control-slider-background-opacity: 1;
}
`,
@@ -434,6 +434,7 @@ class MoreInfoClimate extends LitElement {
font-size: 22px;
font-weight: 500;
line-height: 28px;
direction: ltr;
}
ha-select {
width: 100%;
@@ -33,36 +33,12 @@ class MoreInfoCover extends LitElement {
@property({ attribute: false }) public stateObj?: CoverEntity;
@state() private _livePosition?: number;
@state() private _liveTilt?: number;
@state() private _mode?: Mode;
private _setMode(ev) {
this._mode = ev.currentTarget.mode;
}
private _positionSliderMoved(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._livePosition = value;
}
private _positionValueChanged() {
this._livePosition = undefined;
}
private _tiltSliderMoved(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._liveTilt = value;
}
private _tiltValueChanged() {
this._liveTilt = undefined;
}
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (changedProps.has("stateObj") && this.stateObj) {
@@ -77,20 +53,11 @@ class MoreInfoCover extends LitElement {
}
private get _stateOverride() {
const liveValue = this._livePosition ?? this._liveTilt;
const forcedState =
liveValue != null ? (liveValue ? "open" : "closed") : undefined;
const stateDisplay = this.hass.formatEntityState(
this.stateObj!,
forcedState
);
const stateDisplay = this.hass.formatEntityState(this.stateObj!);
const positionStateDisplay = computeCoverPositionStateDisplay(
this.stateObj!,
this.hass,
liveValue
this.hass
);
if (positionStateDisplay) {
@@ -147,8 +114,6 @@ class MoreInfoCover extends LitElement {
<ha-more-info-cover-position
.stateObj=${this.stateObj}
.hass=${this.hass}
@slider-moved=${this._positionSliderMoved}
@value-changed=${this._positionValueChanged}
></ha-more-info-cover-position>
`
: nothing}
@@ -157,8 +122,6 @@ class MoreInfoCover extends LitElement {
<ha-more-info-cover-tilt-position
.stateObj=${this.stateObj}
.hass=${this.hass}
@slider-moved=${this._tiltSliderMoved}
@value-changed=${this._tiltValueChanged}
></ha-more-info-cover-tilt-position>
`
: nothing}
@@ -40,18 +40,6 @@ class MoreInfoFan extends LitElement {
@state() public _presetMode?: string;
@state() private _liveSpeed?: number;
private _speedSliderMoved(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._liveSpeed = value;
}
private _speedValueChanged() {
this._liveSpeed = undefined;
}
private _toggle = () => {
const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on";
forwardHaptic("light");
@@ -104,23 +92,14 @@ class MoreInfoFan extends LitElement {
}
private get _stateOverride() {
const liveValue = this._liveSpeed;
const forcedState =
liveValue != null ? (liveValue ? "on" : "off") : undefined;
const stateDisplay = this.hass.formatEntityState(
this.stateObj!,
forcedState
);
const stateDisplay = this.hass.formatEntityState(this.stateObj!);
const positionStateDisplay = computeFanSpeedStateDisplay(
this.stateObj!,
this.hass,
liveValue
this.hass
);
if (positionStateDisplay && (stateActive(this.stateObj!) || liveValue)) {
if (positionStateDisplay && stateActive(this.stateObj!)) {
return positionStateDisplay;
}
return stateDisplay;
@@ -165,8 +144,6 @@ class MoreInfoFan extends LitElement {
<ha-more-info-fan-speed
.stateObj=${this.stateObj}
.hass=${this.hass}
@slider-moved=${this._speedSliderMoved}
@value-changed=${this._speedValueChanged}
>
</ha-more-info-fan-speed>
`
@@ -250,6 +250,7 @@ class MoreInfoHumidifier extends LitElement {
font-size: 22px;
font-weight: 500;
line-height: 28px;
direction: ltr;
}
`,
];
@@ -60,29 +60,10 @@ class MoreInfoLight extends LitElement {
@state() private _effect?: string;
@state() private _selectedBrightness?: number;
@state() private _colorTempPreview?: number;
@state() private _mainControl: MainControl = "brightness";
private _brightnessChanged(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._selectedBrightness = (value * 255) / 100;
}
private _tempColorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
if (ev.detail && "color_temp_kelvin" in ev.detail) {
this._colorTempPreview = ev.detail.color_temp_kelvin;
} else {
this._colorTempPreview = undefined;
}
}
protected updated(changedProps: PropertyValues<typeof this>): void {
if (changedProps.has("stateObj")) {
this._selectedBrightness = this.stateObj?.attributes.brightness;
this._effect = this.stateObj?.attributes.effect;
}
}
@@ -98,19 +79,8 @@ class MoreInfoLight extends LitElement {
}
private get _stateOverride() {
if (this._colorTempPreview) {
return this.hass.formatEntityAttributeValue(
this.stateObj!,
"color_temp_kelvin",
this._colorTempPreview
);
}
if (this._selectedBrightness) {
return this.hass.formatEntityAttributeValue(
this.stateObj!,
"brightness",
this._selectedBrightness
);
if (this.stateObj?.attributes.brightness) {
return this.hass.formatEntityAttributeValue(this.stateObj!, "brightness");
}
return undefined;
}
@@ -168,7 +138,6 @@ class MoreInfoLight extends LitElement {
<ha-more-info-light-brightness
.stateObj=${this.stateObj}
.hass=${this.hass}
@slider-moved=${this._brightnessChanged}
>
</ha-more-info-light-brightness>
`
@@ -187,7 +156,6 @@ class MoreInfoLight extends LitElement {
<light-color-temp-picker
.hass=${this.hass}
.stateObj=${this.stateObj}
@color-hovered=${this._tempColorHovered}
>
</light-color-temp-picker>
`
@@ -243,6 +243,7 @@ class MoreInfoWaterHeater extends LitElement {
font-size: 22px;
font-weight: 500;
line-height: 28px;
direction: ltr;
}
`,
];
@@ -356,6 +356,7 @@ class MoreInfoWeather extends LitElement {
.templow {
min-width: 48px;
text-align: right;
direction: ltr;
}
.templow {
+14 -15
View File
@@ -27,7 +27,7 @@ import {
fuzzyFilterSort,
} from "../../common/string/filter/sequence-matching";
import { debounce } from "../../common/util/debounce";
import "../../components/ha-chip";
import "../../components/ha-label";
import "../../components/ha-circular-progress";
import "../../components/ha-icon-button";
import "../../components/ha-list-item";
@@ -326,19 +326,17 @@ export class QuickBar extends LitElement {
hasMeta
>
<span>
<ha-chip
<ha-label
.label=${item.categoryText}
hasIcon
class="command-category ${item.categoryKey}"
>
${item.iconPath
? html`<ha-svg-icon
.path=${item.iconPath}
slot="icon"
></ha-svg-icon>`
: ""}
${item.categoryText}</ha-chip
>
? html`
<ha-svg-icon .path=${item.iconPath} slot="icon"></ha-svg-icon>
`
: nothing}
${item.categoryText}
</ha-label>
</span>
<span class="command-text">${item.primaryText}</span>
@@ -766,6 +764,7 @@ export class QuickBar extends LitElement {
haStyleDialog,
css`
mwc-list {
position: relative;
--mdc-list-vertical-padding: 0;
}
.heading {
@@ -815,20 +814,20 @@ export class QuickBar extends LitElement {
}
.command-category {
--ha-chip-icon-color: #585858;
--ha-chip-text-color: #212121;
--ha-label-icon-color: #585858;
--ha-label-text-color: #212121;
}
.command-category.reload {
--ha-chip-background-color: #cddc39;
--ha-label-background-color: #cddc39;
}
.command-category.navigation {
--ha-chip-background-color: var(--light-primary-color);
--ha-label-background-color: var(--light-primary-color);
}
.command-category.server_control {
--ha-chip-background-color: var(--warning-color);
--ha-label-background-color: var(--warning-color);
}
span.command-text {
+2 -1
View File
@@ -15,7 +15,8 @@ import { hassUrl } from "../data/auth";
import { isExternal } from "../data/external";
import { getRecorderInfo } from "../data/recorder";
import { subscribeFrontendUserData } from "../data/frontend";
import { fetchConfig, fetchResources } from "../data/lovelace";
import { fetchConfig } from "../data/lovelace/config/types";
import { fetchResources } from "../data/lovelace/resource";
import { subscribePanels } from "../data/ws-panels";
import { subscribeThemes } from "../data/ws-themes";
import { subscribeRepairsIssueRegistry } from "../data/repairs";
File diff suppressed because one or more lines are too long
+3 -13
View File
@@ -50,19 +50,9 @@
<body>
<div id="ha-launch-screen">
<div class="ha-launch-screen-spacer"></div>
<svg
viewBox="0 0 240 240"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
fill="#F2F4F9"
/>
<path
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
fill="#18BCF2"
/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
</svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
</div>
@@ -584,6 +584,10 @@ class DialogCalendarEventEditor extends LitElement {
return [
haStyleDialog,
css`
ha-dialog {
--mdc-dialog-min-width: min(600px, 95vw);
--mdc-dialog-max-width: min(600px, 95vw);
}
state-info {
line-height: 40px;
}
@@ -1,14 +1,14 @@
import type { SelectedDetail } from "@material/mwc-list";
import { formatInTimeZone, toDate } from "date-fns-tz";
import { css, html, LitElement, PropertyValues, nothing } from "lit";
import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import type { Options, WeekdayStr } from "rrule";
import { ByWeekday, RRule, Weekday } from "rrule";
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { LocalizeKeys } from "../../common/translations/localize";
import "../../components/ha-chip";
import "../../components/chips/ha-chip-set";
import "../../components/chips/ha-filter-chip";
import "../../components/ha-date-input";
import "../../components/ha-list-item";
import "../../components/ha-select";
@@ -16,17 +16,17 @@ import type { HaSelect } from "../../components/ha-select";
import "../../components/ha-textfield";
import { HomeAssistant } from "../../types";
import {
DEFAULT_COUNT,
MonthlyRepeatItem,
RepeatEnd,
RepeatFrequency,
convertFrequency,
convertRepeatFrequency,
DEFAULT_COUNT,
getMonthdayRepeatFromRule,
getMonthlyRepeatItems,
getMonthlyRepeatWeekdayFromRule,
getWeekday,
getWeekdays,
MonthlyRepeatItem,
RepeatEnd,
RepeatFrequency,
ruleByWeekDay,
untilValue,
} from "./recurrence";
@@ -240,22 +240,24 @@ export class RecurrenceRuleEditor extends LitElement {
renderWeekly() {
return html`
${this.renderInterval()}
<div class="weekdays">
<ha-chip-set class="weekdays">
${this._allWeekdays!.map(
(item) => html`
<ha-chip
<ha-filter-chip
no-leading-icon
.value=${item}
class=${classMap({ active: this._weekday.has(item) })}
.selected=${this._weekday.has(item)}
@click=${this._onWeekdayToggle}
>${this.hass.localize(
.label=${this.hass.localize(
`ui.components.calendar.event.repeat.weekly.weekday.${
item.toLowerCase() as Lowercase<WeekdayStr>
}`
)}</ha-chip
)}
>
</ha-filter-chip>
`
)}
</div>
</ha-chip-set>
`;
}
@@ -379,10 +381,10 @@ export class RecurrenceRuleEditor extends LitElement {
private _onWeekdayToggle(e: MouseEvent) {
const target = e.currentTarget as any;
const value = target.value as WeekdayStr;
if (!target.classList.contains("active")) {
this._weekday.add(value);
} else {
if (this._weekday.has(value)) {
this._weekday.delete(value);
} else {
this._weekday.add(value);
}
this.requestUpdate("_weekday");
}
@@ -504,8 +506,6 @@ export class RecurrenceRuleEditor extends LitElement {
margin-bottom: 16px;
}
.weekdays {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
}
ha-textfield:last-child,
@@ -513,11 +513,6 @@ export class RecurrenceRuleEditor extends LitElement {
.weekdays:last-child {
margin-bottom: 0;
}
.active {
--ha-chip-background-color: var(--primary-color);
--ha-chip-text-color: var(--text-primary-color);
}
`;
}
@@ -342,7 +342,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
private _duplicateOption(ev) {
const index = (ev.target as any).idx;
this._addOption(deepClone(ensureArray(this.action.choose)[index]));
this._createOption(deepClone(ensureArray(this.action.choose)[index]));
}
protected firstUpdated() {
@@ -399,11 +399,15 @@ export class HaChooseAction extends LitElement implements ActionElement {
});
}
private _addOption(opt?: ChooseActionChoice) {
private _addOption() {
this._createOption({ conditions: [], sequence: [] });
}
private _createOption(opt: ChooseActionChoice) {
const choose = this.action.choose
? [...ensureArray(this.action.choose)]
: [];
choose.push(opt ?? { conditions: [], sequence: [] });
choose.push(opt);
fireEvent(this, "value-changed", {
value: { ...this.action, choose },
});
@@ -27,7 +27,7 @@ import type {
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-button-related-filter-menu";
import "../../../components/ha-chip";
import "../../../components/ha-label";
import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-overflow-menu";
@@ -202,11 +202,11 @@ class HaAutomationPicker extends LitElement {
template: (automation) =>
automation.disabled
? html`
<ha-chip>
<ha-label>
${this.hass.localize(
"ui.panel.config.automation.picker.disabled"
)}
</ha-chip>
</ha-label>
`
: "",
};
@@ -8,16 +8,17 @@ import {
} from "@mdi/js";
import { HassEntities, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
css,
html,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/chips/ha-assist-chip";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-icon-button";
@@ -34,9 +35,9 @@ import {
subscribeRepairsIssueRegistry,
} from "../../../data/repairs";
import {
UpdateEntity,
checkForEntityUpdates,
filterUpdateEntitiesWithInstall,
UpdateEntity,
} from "../../../data/update";
import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
@@ -231,15 +232,17 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
></ha-config-repairs>
${totalRepairIssues > repairsIssues.length
? html`
<a class="button" href="/config/repairs">
${this.hass.localize(
<ha-assist-chip
href="/config/repairs"
.label=${this.hass.localize(
"ui.panel.config.repairs.more_repairs",
{
count:
totalRepairIssues - repairsIssues.length,
}
)}
</a>
>
</ha-assist-chip>
`
: ""}
`
@@ -257,15 +260,17 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
></ha-config-updates>
${totalUpdates > canInstallUpdates.length
? html`
<a class="button" href="/config/updates">
${this.hass.localize(
<ha-assist-chip
href="/config/updates"
label=${this.hass.localize(
"ui.panel.config.updates.more_updates",
{
count:
totalUpdates - canInstallUpdates.length,
}
)}
</a>
>
</ha-assist-chip>
`
: ""}
`
@@ -349,13 +354,8 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
text-decoration: none;
color: var(--primary-text-color);
}
a.button {
display: inline-block;
color: var(--primary-text-color);
padding: 6px 16px;
ha-assist-chip {
margin: 8px 16px 16px 16px;
border-radius: 32px;
border: 1px solid var(--divider-color);
}
.title {
font-size: 16px;
@@ -1,8 +1,8 @@
import { css, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-chip";
import "../../../../components/ha-chip-set";
import "../../../../components/chips/ha-assist-chip";
import "../../../../components/chips/ha-chip-set";
import { showAutomationEditor } from "../../../../data/automation";
import {
DeviceAction,
@@ -77,17 +77,18 @@ export abstract class HaDeviceAutomationCard<
<ha-chip-set>
${automations.map(
(automation, idx) => html`
<ha-chip
<ha-assist-chip
filled
.index=${idx}
@click=${this._handleAutomationClicked}
class=${automation.metadata?.secondary ? "secondary" : ""}
>
${this._localizeDeviceAutomation(
.label=${this._localizeDeviceAutomation(
this.hass,
this.entityReg!,
automation
)}
</ha-chip>
>
</ha-assist-chip>
`
)}
</ha-chip-set>
@@ -128,7 +129,10 @@ export abstract class HaDeviceAutomationCard<
color: var(--primary-text-color);
}
.secondary {
--ha-chip-background-color: rgba(var(--rgb-primary-text-color), 0.07);
--ha-assist-chip-filled-container-color: rgba(
var(--rgb-primary-text-color),
0.07
);
}
button.link {
color: var(--primary-color);
@@ -508,7 +508,9 @@ export class HaConfigDeviceDashboard extends LitElement {
? html`<mwc-list-item @click=${this._clearFilter}
>${this.hass.localize("ui.components.data-table.filtering_by")}
${activeFilters.join(", ")}
<span class="clear">Clear</span></mwc-list-item
<span class="clear"
>${this.hass.localize("ui.common.clear")}</span
></mwc-list-item
>`
: ""}
<ha-check-list-item
@@ -663,7 +663,9 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
"ui.components.data-table.filtering_by"
)}
${activeFilters.join(", ")}
<span class="clear">Clear</span></mwc-list-item
<span class="clear"
>${this.hass.localize("ui.common.clear")}</span
></mwc-list-item
>`
: ""}
<ha-check-list-item
@@ -1,6 +1,6 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list";
import Fuse from "fuse.js";
import Fuse, { IFuseOptions } from "fuse.js";
import { HassConfig } from "home-assistant-js-websocket";
import {
css,
@@ -139,12 +139,13 @@ class AddIntegrationDialog extends LitElement {
}
if (this._initialFilter !== undefined && this._filter === "") {
this._initialFilter = undefined;
this._filter = "";
this._filter = undefined;
this._width = undefined;
this._height = undefined;
} else if (
this.hasUpdated &&
changedProps.has("_filter") &&
!changedProps.has("_open") &&
(!this._width || !this._height)
) {
// Store the width and height so that when we search, box doesn't jump
@@ -239,7 +240,7 @@ class AddIntegrationDialog extends LitElement {
});
if (filter) {
const options: Fuse.IFuseOptions<IntegrationListItem> = {
const options: IFuseOptions<IntegrationListItem> = {
keys: [
{ name: "name", weight: 5 },
{ name: "domain", weight: 5 },
@@ -678,6 +679,9 @@ class AddIntegrationDialog extends LitElement {
justify-content: center;
margin: 24px 0;
}
mwc-list {
position: relative;
}
lit-virtualizer {
contain: size layout !important;
}

Some files were not shown because too many files have changed in this diff Show More