mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-27 04:52:56 +00:00
Compare commits
571 Commits
feature/po
...
20260325.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf542197e0 | ||
|
|
5c2627624a | ||
|
|
698ded9d85 | ||
|
|
9a7fb96873 | ||
|
|
204c5b5e14 | ||
|
|
8ea3acfa98 | ||
|
|
306739773e | ||
|
|
8b3fa3adac | ||
|
|
37a1d59a24 | ||
|
|
6812884e00 | ||
|
|
bf7ef1f7ae | ||
|
|
fe57f601ba | ||
|
|
c89d478440 | ||
|
|
fa27d26e5f | ||
|
|
18f411ef53 | ||
|
|
24826e92f0 | ||
|
|
ea9d369d88 | ||
|
|
a9b026d0ef | ||
|
|
35339906ec | ||
|
|
ce23f716cc | ||
|
|
aaf8fa199f | ||
|
|
fba430d507 | ||
|
|
59361cbd38 | ||
|
|
b558117d8c | ||
|
|
a7c8347751 | ||
|
|
31ca9c849a | ||
|
|
6252d7e8f5 | ||
|
|
f42986adf6 | ||
|
|
9e70ea3723 | ||
|
|
de3b7bf513 | ||
|
|
2c5f491c9e | ||
|
|
1ef13c5100 | ||
|
|
c166335aca | ||
|
|
c64ec21eca | ||
|
|
8d62056f4a | ||
|
|
62e73608b6 | ||
|
|
aa66d8891c | ||
|
|
494a96c635 | ||
|
|
36d77f54ce | ||
|
|
12fec9f580 | ||
|
|
5f1f55448a | ||
|
|
837e345ecf | ||
|
|
0929d7d18a | ||
|
|
70991d3c1e | ||
|
|
82e5bd62a1 | ||
|
|
b8adf4e866 | ||
|
|
111be984e0 | ||
|
|
78a2cbb532 | ||
|
|
34b09b140b | ||
|
|
f173f901c5 | ||
|
|
ebb6ac8d8b | ||
|
|
abe214a33a | ||
|
|
248332ae27 | ||
|
|
82fc2fccdc | ||
|
|
c8f30a7ee4 | ||
|
|
77f48d91cd | ||
|
|
caa707a7b1 | ||
|
|
0bed0fa37e | ||
|
|
5b6309d984 | ||
|
|
264818bc70 | ||
|
|
d664ab6836 | ||
|
|
a6c4184054 | ||
|
|
cb6985eb7c | ||
|
|
d466ab63bd | ||
|
|
1132cdb364 | ||
|
|
0f9d48a03d | ||
|
|
7e085d9b08 | ||
|
|
1a62c7296c | ||
|
|
be1921229c | ||
|
|
640558ad35 | ||
|
|
99636c9719 | ||
|
|
87758cc228 | ||
|
|
60e8b8b505 | ||
|
|
3c012c30ac | ||
|
|
84d234a330 | ||
|
|
a12543fe74 | ||
|
|
cc53f977a2 | ||
|
|
71541625d7 | ||
|
|
43da700ccc | ||
|
|
efbbdbf3e8 | ||
|
|
eee6f79639 | ||
|
|
9381bbd656 | ||
|
|
2724087290 | ||
|
|
6bdf1ccd8c | ||
|
|
79743c0afa | ||
|
|
15b1df5a58 | ||
|
|
8222d9796c | ||
|
|
3337b414d7 | ||
|
|
ce90d83c92 | ||
|
|
1f6d0d2e63 | ||
|
|
0327b02d0b | ||
|
|
780db9b066 | ||
|
|
89755f274d | ||
|
|
6ea15f507a | ||
|
|
c506fa8990 | ||
|
|
9470863808 | ||
|
|
4bdac1f385 | ||
|
|
5bbfa36228 | ||
|
|
a8070b322c | ||
|
|
9cbc44123e | ||
|
|
c8f4c892f9 | ||
|
|
40b9f9dccb | ||
|
|
823c222a55 | ||
|
|
02acd2996c | ||
|
|
c462fc0639 | ||
|
|
903553dab9 | ||
|
|
25a1c14523 | ||
|
|
f03eee6cb2 | ||
|
|
a2a38e1da7 | ||
|
|
88c063ba2a | ||
|
|
984b50bac7 | ||
|
|
09e4355451 | ||
|
|
7ee76538ae | ||
|
|
eb8b2a9d17 | ||
|
|
10e8c2a148 | ||
|
|
e1a8616ab0 | ||
|
|
ccdd71dd64 | ||
|
|
d3e1d55686 | ||
|
|
4f916abcbf | ||
|
|
4548f9daae | ||
|
|
4020bcec42 | ||
|
|
22c0035e60 | ||
|
|
6b6ad8dd2c | ||
|
|
3bbc3403d6 | ||
|
|
9979bb13ea | ||
|
|
8ac831679d | ||
|
|
db05b07997 | ||
|
|
dba8cefa67 | ||
|
|
6935c55c3c | ||
|
|
635a1185a3 | ||
|
|
585c894c5a | ||
|
|
f9d052a818 | ||
|
|
a29132441d | ||
|
|
479d52bf1d | ||
|
|
d96d78d6f6 | ||
|
|
f80cba341f | ||
|
|
77ee966442 | ||
|
|
2fec5a497e | ||
|
|
ed75d96d3c | ||
|
|
0fac47992b | ||
|
|
91a608c4c5 | ||
|
|
df61953ed4 | ||
|
|
2cda46b4bb | ||
|
|
037190a393 | ||
|
|
ebe0154e32 | ||
|
|
efa73067f6 | ||
|
|
fdb40c9d01 | ||
|
|
5a7ddb4972 | ||
|
|
dbe46d3b3f | ||
|
|
eb43d85439 | ||
|
|
1bbfb79ddb | ||
|
|
cf50db350f | ||
|
|
e04a0ec7dc | ||
|
|
e08576a6dc | ||
|
|
a7831f86ee | ||
|
|
c66e5b379b | ||
|
|
e819c30151 | ||
|
|
e278e33375 | ||
|
|
8313be8e7e | ||
|
|
6d95a59ca0 | ||
|
|
a498ad3d06 | ||
|
|
c4a2229baa | ||
|
|
15245af52d | ||
|
|
c697735e46 | ||
|
|
ddec792ae3 | ||
|
|
cfd0e72609 | ||
|
|
8a5bcd67ab | ||
|
|
a794a80228 | ||
|
|
41ed7d2877 | ||
|
|
b0b86e7ba8 | ||
|
|
e67c4842d4 | ||
|
|
d9c39640e0 | ||
|
|
a8478ab346 | ||
|
|
3ac2434b6f | ||
|
|
f2f1044992 | ||
|
|
53bc66883a | ||
|
|
d795bd1f61 | ||
|
|
869e1d32b3 | ||
|
|
3370bfa9dd | ||
|
|
b1921d1b66 | ||
|
|
c2a2b382e9 | ||
|
|
7d95c2b6cb | ||
|
|
67536a8a64 | ||
|
|
3d89ad4f91 | ||
|
|
36e08367d9 | ||
|
|
6e3cf3e42f | ||
|
|
fe53656c7e | ||
|
|
14615191f4 | ||
|
|
86ca8ebf71 | ||
|
|
16c1db5346 | ||
|
|
b9568c079e | ||
|
|
0f9d90c217 | ||
|
|
ad092df9e0 | ||
|
|
484473080e | ||
|
|
97dcc62ae7 | ||
|
|
d12d4811b4 | ||
|
|
3747abe1a9 | ||
|
|
8541494b3d | ||
|
|
2d1e211034 | ||
|
|
147a21db27 | ||
|
|
e2e5feb8d5 | ||
|
|
619c75ac8b | ||
|
|
5b09101903 | ||
|
|
408efd2e2c | ||
|
|
fd0b503a21 | ||
|
|
1e9a1eaa04 | ||
|
|
b42d52b854 | ||
|
|
21a5e643ac | ||
|
|
52ac052baf | ||
|
|
a13ffa3c35 | ||
|
|
c51535eb79 | ||
|
|
5ea05768a3 | ||
|
|
d10376e9d8 | ||
|
|
e21f7baa93 | ||
|
|
14098e0d7e | ||
|
|
bd8c407690 | ||
|
|
c452acf6cf | ||
|
|
814106e4aa | ||
|
|
a2254eb048 | ||
|
|
fa5e0bb0c7 | ||
|
|
e8e62a0aa0 | ||
|
|
4893b631a5 | ||
|
|
d851a5bdf3 | ||
|
|
148d01b05b | ||
|
|
171875c65c | ||
|
|
6a61186507 | ||
|
|
1690991726 | ||
|
|
4904566460 | ||
|
|
b3b8f1a38a | ||
|
|
3bb870f52a | ||
|
|
95a7b91ea5 | ||
|
|
9c4aacdb1f | ||
|
|
3feb40a8f4 | ||
|
|
7a310812e0 | ||
|
|
ee77619da3 | ||
|
|
cfa8eb5370 | ||
|
|
d9d2d6aa03 | ||
|
|
1f46f477c7 | ||
|
|
52667b3266 | ||
|
|
c790d2356c | ||
|
|
f24c009dd7 | ||
|
|
8d42395938 | ||
|
|
1a6d46a7ff | ||
|
|
b286b07cfd | ||
|
|
1859d35f7b | ||
|
|
29b3303f94 | ||
|
|
5dc2c51251 | ||
|
|
571fccf4a0 | ||
|
|
8f958147dd | ||
|
|
41753e48bf | ||
|
|
d496b2a6fe | ||
|
|
3260c48130 | ||
|
|
a356c153f8 | ||
|
|
b60dc94196 | ||
|
|
c5cb196aa1 | ||
|
|
5ddb31ce7b | ||
|
|
7465c9874c | ||
|
|
bbe81ca8f1 | ||
|
|
51ecdb0851 | ||
|
|
268c0a60f0 | ||
|
|
8745cfa1ed | ||
|
|
8bcccb5792 | ||
|
|
0844d9c2c0 | ||
|
|
9d3aafe219 | ||
|
|
0cf3e34956 | ||
|
|
7d20b04469 | ||
|
|
6f6ba71e61 | ||
|
|
d61de4d2df | ||
|
|
f8dcee6867 | ||
|
|
0f975acca1 | ||
|
|
9e57dacb3c | ||
|
|
2650c1c0b2 | ||
|
|
6957e1f4d9 | ||
|
|
32be26320d | ||
|
|
cdbaa85beb | ||
|
|
b58fc68ea8 | ||
|
|
4792659c61 | ||
|
|
eeae66e026 | ||
|
|
60b4b1c4e2 | ||
|
|
877c5e8d16 | ||
|
|
11fd10a011 | ||
|
|
623baea59d | ||
|
|
052909ea01 | ||
|
|
5e3c80ef6e | ||
|
|
6e2fdbb396 | ||
|
|
5b981062f6 | ||
|
|
5a2d7f100a | ||
|
|
b62e6c9328 | ||
|
|
537ab2debb | ||
|
|
04d31e4ea9 | ||
|
|
3d0c7b6aab | ||
|
|
87d3d06e79 | ||
|
|
872d1c684f | ||
|
|
d9d2ebfb9d | ||
|
|
e26315c4d2 | ||
|
|
d9a2e1cbee | ||
|
|
b7d6652e5c | ||
|
|
b188da0b5c | ||
|
|
9f87257f41 | ||
|
|
f4e409f157 | ||
|
|
44d727763c | ||
|
|
2f87614c4c | ||
|
|
697a945056 | ||
|
|
67dbacce52 | ||
|
|
e891ae6bdd | ||
|
|
a11d1cfecf | ||
|
|
933bb4a2cc | ||
|
|
1ca1ef3c32 | ||
|
|
f529a349b9 | ||
|
|
b3b35e4299 | ||
|
|
59364d05ec | ||
|
|
6444c0df15 | ||
|
|
8f704d8c0a | ||
|
|
aeddeb3bd7 | ||
|
|
1377e3a363 | ||
|
|
e47bf22798 | ||
|
|
b3f1ba9433 | ||
|
|
829c5ca4df | ||
|
|
fbd4fc6d5f | ||
|
|
7612ec1979 | ||
|
|
71a5a1d3b4 | ||
|
|
ad91d360f8 | ||
|
|
a88f492c05 | ||
|
|
0fc85a52a0 | ||
|
|
e01dc89a48 | ||
|
|
52c36db5d4 | ||
|
|
f217dab8d4 | ||
|
|
5709af57de | ||
|
|
bb16cc8c00 | ||
|
|
17c6dc52a8 | ||
|
|
1b8211db6d | ||
|
|
2b2bb77a2b | ||
|
|
64749350ef | ||
|
|
043d4eed85 | ||
|
|
2f2e64bb1d | ||
|
|
b74b02c09f | ||
|
|
ab4c3a4316 | ||
|
|
b89b02e044 | ||
|
|
daff323606 | ||
|
|
356bceb1cc | ||
|
|
acb5af2f41 | ||
|
|
17314fe62b | ||
|
|
3826a69465 | ||
|
|
a68acdf9dd | ||
|
|
bb56bbb1ba | ||
|
|
76aef60c05 | ||
|
|
23af40743b | ||
|
|
c4326b4f3a | ||
|
|
d248f5614f | ||
|
|
a4da7b26ea | ||
|
|
3c49cdf3c0 | ||
|
|
26af81d1a4 | ||
|
|
2a08f2d79b | ||
|
|
a5be02b743 | ||
|
|
4228871f00 | ||
|
|
9a7a8fd377 | ||
|
|
8b82882e15 | ||
|
|
15de137591 | ||
|
|
465c10b945 | ||
|
|
457c51cf58 | ||
|
|
640f2b9245 | ||
|
|
852caa32be | ||
|
|
67ccfa0f6e | ||
|
|
c3cc566fe3 | ||
|
|
38d02a3f30 | ||
|
|
ad1d1e2260 | ||
|
|
b2eb8ec968 | ||
|
|
7b8884f0fd | ||
|
|
aff1fedc9d | ||
|
|
8f5059c24a | ||
|
|
1e72ad1411 | ||
|
|
c9f96bbe69 | ||
|
|
616c3d4657 | ||
|
|
b1ceece224 | ||
|
|
d695c4c845 | ||
|
|
fdbeb12622 | ||
|
|
29ede122a1 | ||
|
|
519d3d0e53 | ||
|
|
2701015eda | ||
|
|
1991a9e493 | ||
|
|
2b72c54194 | ||
|
|
a7cb2fe7a7 | ||
|
|
51ea0c8201 | ||
|
|
ead7081bc6 | ||
|
|
ee982b1899 | ||
|
|
e8b100a39e | ||
|
|
50c361db62 | ||
|
|
e7a8d15a13 | ||
|
|
fbd0409837 | ||
|
|
a0d100611f | ||
|
|
a969bf1065 | ||
|
|
a153330610 | ||
|
|
bd2f1ca3a8 | ||
|
|
3263034416 | ||
|
|
82b28b547a | ||
|
|
61c2c750b4 | ||
|
|
117690ee70 | ||
|
|
e753de85eb | ||
|
|
a240019968 | ||
|
|
0bdf4b8777 | ||
|
|
6337828ed8 | ||
|
|
b8e5af652b | ||
|
|
e4ae29e8b5 | ||
|
|
08231dbbb0 | ||
|
|
0ca656933d | ||
|
|
b23cf8eba4 | ||
|
|
61b546415d | ||
|
|
4e1b709303 | ||
|
|
34e65b302d | ||
|
|
336d0e1b9d | ||
|
|
030a9a492c | ||
|
|
2685a007e7 | ||
|
|
9ca1cfbf4a | ||
|
|
0793af6846 | ||
|
|
bb7f441d8d | ||
|
|
2813ed7938 | ||
|
|
9ebfa4029b | ||
|
|
6190ba18ea | ||
|
|
81feea1109 | ||
|
|
be430931cc | ||
|
|
e07194027a | ||
|
|
58d4cf8d84 | ||
|
|
d3453aff37 | ||
|
|
64ff2e414c | ||
|
|
2ca25c980f | ||
|
|
73d93bc601 | ||
|
|
5ca6a8aced | ||
|
|
7ff4993e0b | ||
|
|
4e6fbacccc | ||
|
|
2958d49e36 | ||
|
|
92289dc7ea | ||
|
|
f6c1a890e4 | ||
|
|
d06321ed43 | ||
|
|
3c3d8d9974 | ||
|
|
4f39fa482d | ||
|
|
5d0fe3236c | ||
|
|
b86142ae50 | ||
|
|
5d2f3ee5e8 | ||
|
|
e3f7c631a7 | ||
|
|
49f9d95853 | ||
|
|
db3d7701b5 | ||
|
|
3e55acf531 | ||
|
|
f102618d9d | ||
|
|
a3c02b511d | ||
|
|
74111d248e | ||
|
|
17d9cd192f | ||
|
|
f8161b3505 | ||
|
|
6070c1907a | ||
|
|
ce5991582c | ||
|
|
d17217fc90 | ||
|
|
86b4bd0013 | ||
|
|
108ba3abd6 | ||
|
|
d38a2894c4 | ||
|
|
4c70376a62 | ||
|
|
8d69bd1401 | ||
|
|
5dfecd3693 | ||
|
|
efd51d2234 | ||
|
|
668299c16a | ||
|
|
5e155a4030 | ||
|
|
809fa10135 | ||
|
|
1cbc38f231 | ||
|
|
9ed39bb523 | ||
|
|
4e3d66cf40 | ||
|
|
2eaad79d1c | ||
|
|
afef7a2c0f | ||
|
|
18d5224002 | ||
|
|
dbffdfeaca | ||
|
|
0a4b7917ab | ||
|
|
e1524358d9 | ||
|
|
8774f9c3fc | ||
|
|
f9a9aeacab | ||
|
|
b798fee116 | ||
|
|
b25f731f0f | ||
|
|
26a7372c5e | ||
|
|
70d3409d62 | ||
|
|
0711ecddab | ||
|
|
bcfaa67eba | ||
|
|
1b60e6e04e | ||
|
|
a1a634f6dc | ||
|
|
55f48fbb56 | ||
|
|
ca4d66b94c | ||
|
|
51fd2eedd9 | ||
|
|
434a7c2e93 | ||
|
|
b849fecf0b | ||
|
|
3a48e1996f | ||
|
|
8299386737 | ||
|
|
5e58ff476f | ||
|
|
758d955053 | ||
|
|
1efd5d26f0 | ||
|
|
36979f10cc | ||
|
|
812c59fcb4 | ||
|
|
0c34165bcf | ||
|
|
8c2bfbe9ce | ||
|
|
8f721d74e2 | ||
|
|
63782e6ef3 | ||
|
|
eaad2295a9 | ||
|
|
e74eee3d34 | ||
|
|
cc39010839 | ||
|
|
7f97425214 | ||
|
|
8fac6e63de | ||
|
|
2ac8fe2b21 | ||
|
|
45ca1b2cdc | ||
|
|
0667f1e789 | ||
|
|
db49678ccb | ||
|
|
2ca7e9f71e | ||
|
|
8d883450a8 | ||
|
|
2c136e00f5 | ||
|
|
6f82478598 | ||
|
|
1093bd890f | ||
|
|
456c638750 | ||
|
|
d98e373f64 | ||
|
|
649516c9fa | ||
|
|
bbc4fb96b2 | ||
|
|
0ae639aeb0 | ||
|
|
0e7e41065e | ||
|
|
685843f112 | ||
|
|
5e1a99d94a | ||
|
|
d843349865 | ||
|
|
ec23164aa9 | ||
|
|
e74ef11101 | ||
|
|
a222f6a736 | ||
|
|
ef3dd16d45 | ||
|
|
5d4e1d205e | ||
|
|
1ee5ebbe75 | ||
|
|
59d705aa3d | ||
|
|
332e108dae | ||
|
|
3c15b29d0a | ||
|
|
130c708e23 | ||
|
|
588a14a8a7 | ||
|
|
a1ef6ad266 | ||
|
|
a6c1f87730 | ||
|
|
49252a3808 | ||
|
|
c7877fe38f | ||
|
|
e355a61d8f | ||
|
|
f2e19e51ce | ||
|
|
fd9ab8f561 | ||
|
|
faa1b3c98f | ||
|
|
acc4a84fc9 | ||
|
|
4d723dac37 | ||
|
|
f1d4d0ef98 | ||
|
|
88180a2708 | ||
|
|
258d87e3d5 | ||
|
|
55f22ba61a | ||
|
|
812f3ca8b9 | ||
|
|
7f880d11a0 | ||
|
|
6b2452c538 | ||
|
|
c2cbf8bd21 | ||
|
|
224bcece9c | ||
|
|
dc84b7698f | ||
|
|
bc22e6a9bd | ||
|
|
d44874783a | ||
|
|
8d1bb5c867 | ||
|
|
da1b528eee | ||
|
|
756138408a | ||
|
|
3c8f112565 | ||
|
|
2521f3dde4 | ||
|
|
56390aa01a | ||
|
|
9aac5b19da | ||
|
|
24afc3dc88 | ||
|
|
873c7b2947 | ||
|
|
648db4276b | ||
|
|
f86c3e7856 | ||
|
|
1d0251cc28 | ||
|
|
518cf87847 | ||
|
|
81a9216c44 | ||
|
|
f0e10e0058 | ||
|
|
5df8ea4f07 | ||
|
|
73f081f5cc | ||
|
|
f0d1db1da6 | ||
|
|
c658eb414b | ||
|
|
bac493b72b |
9
.github/copilot-instructions.md
vendored
9
.github/copilot-instructions.md
vendored
@@ -464,7 +464,14 @@ this.hass.localize("ui.panel.config.updates.update_available", {
|
||||
|
||||
### Pull Requests
|
||||
|
||||
When creating a pull request, you **must** use the PR template located at `.github/PULL_REQUEST_TEMPLATE.md`. Read the template file and use its full content as the PR body, filling in each section appropriately. Do not omit, reorder, or rewrite the template sections. Do not check the checklist items on behalf of the user — those are the user's responsibility to review and check. If the PR includes UI changes, remind the user to add screenshots or a short video to the PR after creating it.
|
||||
When creating a pull request, you **must** use the PR template located at `.github/PULL_REQUEST_TEMPLATE.md`. Read the template file and use its full content as the PR body, filling in each section appropriately.
|
||||
|
||||
- Do not omit, reorder, or rewrite the template sections
|
||||
- Check the appropriate "Type of change" box based on the changes
|
||||
- Do not check the checklist items on behalf of the user — those are the user's responsibility to review and check
|
||||
- If the PR includes UI changes, remind the user to add screenshots or a short video to the PR after creating it
|
||||
- Be simple and user friendly — explain what the change does, not implementation details
|
||||
- Use markdown so the user can copy it
|
||||
|
||||
### Text and Copy Guidelines
|
||||
|
||||
|
||||
4
.github/workflows/cast_deployment.yaml
vendored
4
.github/workflows/cast_deployment.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
12
.github/workflows/ci.yaml
vendored
12
.github/workflows/ci.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
||||
- name: Setup lint cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: |
|
||||
node_modules/.cache/prettier
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -89,13 +89,13 @@ jobs:
|
||||
env:
|
||||
IS_TEST: "true"
|
||||
- name: Upload bundle stats
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: frontend-bundle-stats
|
||||
path: build/stats/*.json
|
||||
if-no-files-found: error
|
||||
- name: Upload frontend build
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: frontend-build
|
||||
path: hass_frontend/
|
||||
|
||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -36,14 +36,14 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -57,4 +57,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
|
||||
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
|
||||
4
.github/workflows/demo_deployment.yaml
vendored
4
.github/workflows/demo_deployment.yaml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
2
.github/workflows/design_deployment.yaml
vendored
2
.github/workflows/design_deployment.yaml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
2
.github/workflows/design_preview.yaml
vendored
2
.github/workflows/design_preview.yaml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
6
.github/workflows/nightly.yaml
vendored
6
.github/workflows/nightly.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -57,14 +57,14 @@ jobs:
|
||||
run: tar -czvf translations.tar.gz translations
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: wheels
|
||||
path: dist/home_assistant_frontend*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
|
||||
2
.github/workflows/release-drafter.yaml
vendored
2
.github/workflows/release-drafter.yaml
vendored
@@ -18,6 +18,6 @@ jobs:
|
||||
pull-requests: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0
|
||||
- uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
uses: home-assistant/actions/helpers/verify-version@master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
skip-existing: true
|
||||
|
||||
- name: Upload release assets
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
files: |
|
||||
dist/*.whl
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -115,6 +115,6 @@ jobs:
|
||||
- name: Tar folder
|
||||
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
||||
- name: Upload release asset
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 90 days stale policy
|
||||
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 90
|
||||
|
||||
@@ -31,7 +31,7 @@ index 8795ddcaa77aea7b0356417e4bc4b19e2b3f860c..fcdc68342d9ac53936c9ed40a9ccfc2f
|
||||
@@ -129,7 +129,10 @@ export async function injectManifest(
|
||||
searchString: options.injectionPoint!,
|
||||
});
|
||||
|
||||
|
||||
- filesToWrite[options.swDest] = source;
|
||||
+ filesToWrite[options.swDest] = source.replace(
|
||||
+ url!,
|
||||
942
.yarn/releases/yarn-4.12.0.cjs
vendored
942
.yarn/releases/yarn-4.12.0.cjs
vendored
File diff suppressed because one or more lines are too long
940
.yarn/releases/yarn-4.13.0.cjs
vendored
Executable file
940
.yarn/releases/yarn-4.13.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
@@ -8,4 +8,4 @@ enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.13.0.cjs
|
||||
|
||||
@@ -99,6 +99,41 @@ const lokaliseProjects = {
|
||||
frontend: "3420425759f6d6d241f598.13594006",
|
||||
};
|
||||
|
||||
const POLL_INTERVAL_MS = 1000;
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
async function pollProcess(lokaliseApi, projectId, processId) {
|
||||
while (true) {
|
||||
const process = await lokaliseApi
|
||||
.queuedProcesses()
|
||||
.get(processId, { project_id: projectId });
|
||||
|
||||
const project =
|
||||
projectId === lokaliseProjects.backend ? "backend" : "frontend";
|
||||
|
||||
if (process.status === "finished") {
|
||||
console.log(`Lokalise export process for ${project} finished`);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (process.status === "failed" || process.status === "cancelled") {
|
||||
throw new Error(
|
||||
`Lokalise export process for ${project} ${process.status}: ${process.message}`
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Lokalise export process for ${project} in progress...`,
|
||||
process.status
|
||||
);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, POLL_INTERVAL_MS);
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
|
||||
gulp.task("fetch-lokalise", async function () {
|
||||
let apiKey;
|
||||
try {
|
||||
@@ -118,55 +153,60 @@ gulp.task("fetch-lokalise", async function () {
|
||||
]);
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(lokaliseProjects).map(([project, projectId]) =>
|
||||
lokaliseApi
|
||||
.files()
|
||||
.download(projectId, {
|
||||
format: "json",
|
||||
original_filenames: false,
|
||||
replace_breaks: false,
|
||||
json_unescaped_slashes: true,
|
||||
export_empty_as: "skip",
|
||||
filter_data: ["verified"],
|
||||
})
|
||||
.then((download) => fetch(download.bundle_url))
|
||||
.then((response) => {
|
||||
if (response.status === 200 || response.status === 0) {
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
Object.entries(lokaliseProjects).map(async ([project, projectId]) => {
|
||||
try {
|
||||
const exportProcess = await lokaliseApi
|
||||
.files()
|
||||
.async_download(projectId, {
|
||||
format: "json",
|
||||
original_filenames: false,
|
||||
replace_breaks: false,
|
||||
json_unescaped_slashes: true,
|
||||
export_empty_as: "skip",
|
||||
filter_data: ["verified"],
|
||||
});
|
||||
|
||||
const finishedProcess = await pollProcess(
|
||||
lokaliseApi,
|
||||
projectId,
|
||||
exportProcess.process_id
|
||||
);
|
||||
|
||||
const bundleUrl = finishedProcess.details.download_url;
|
||||
|
||||
console.log(`Downloading translations from: ${bundleUrl}`);
|
||||
|
||||
const response = await fetch(bundleUrl);
|
||||
|
||||
if (response.status !== 200 && response.status !== 0) {
|
||||
throw new Error(response.statusText);
|
||||
})
|
||||
.then(JSZip.loadAsync)
|
||||
.then(async (contents) => {
|
||||
await mkdirPromise;
|
||||
return Promise.all(
|
||||
Object.keys(contents.files).map(async (filename) => {
|
||||
const file = contents.file(filename);
|
||||
if (!file) {
|
||||
// no file, probably a directory
|
||||
return Promise.resolve();
|
||||
}
|
||||
return file
|
||||
.async("nodebuffer")
|
||||
.then((content) =>
|
||||
fs.writeFile(
|
||||
path.join(
|
||||
inDir,
|
||||
project,
|
||||
filename.split("/").splice(-1)[0]
|
||||
),
|
||||
content,
|
||||
{ flag: "w", encoding }
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
console.log(`Extracting translations...`);
|
||||
|
||||
const contents = await JSZip.loadAsync(await response.arrayBuffer());
|
||||
|
||||
await mkdirPromise;
|
||||
await Promise.all(
|
||||
Object.keys(contents.files).map(async (filename) => {
|
||||
const file = contents.file(filename);
|
||||
if (!file) {
|
||||
// no file, probably a directory
|
||||
return;
|
||||
}
|
||||
const content = await file.async("nodebuffer");
|
||||
await fs.writeFile(
|
||||
path.join(inDir, project, filename.split("/").splice(-1)[0]),
|
||||
content,
|
||||
{ flag: "w", encoding }
|
||||
);
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -40,18 +40,24 @@ const convertToJSON = async (
|
||||
throw e;
|
||||
}
|
||||
// Convert to JSON
|
||||
const parts = localeData.split("} else {");
|
||||
const firstBlock = parts[0];
|
||||
const obj = INTL_POLYFILLS[pkg];
|
||||
const dataRegex = new RegExp(
|
||||
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
|
||||
"s"
|
||||
);
|
||||
localeData = localeData.match(dataRegex)?.groups?.data;
|
||||
localeData = firstBlock.match(dataRegex)?.groups?.data;
|
||||
if (!localeData) {
|
||||
throw Error(`Failed to extract data for language ${lang} from ${pkg}`);
|
||||
}
|
||||
// Parse to validate JSON, then stringify to minify
|
||||
localeData = JSON.stringify(JSON.parse(localeData));
|
||||
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||
try {
|
||||
localeData = JSON.stringify(JSON.parse(localeData));
|
||||
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||
} catch (e) {
|
||||
throw Error(`Failed to parse JSON for language ${lang} from ${pkg}: ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
||||
|
||||
@@ -57,6 +57,12 @@ const runDevServer = async ({
|
||||
directory: contentBase,
|
||||
watch: true,
|
||||
},
|
||||
client: {
|
||||
overlay: {
|
||||
runtimeErrors: (error) =>
|
||||
!error?.message?.includes("ResizeObserver loop"),
|
||||
},
|
||||
},
|
||||
proxy,
|
||||
},
|
||||
compiler
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { Entity } from "../../../../src/fake_data/entity";
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { EntityInput } from "../../../../src/fake_data/entities/types";
|
||||
|
||||
export const castDemoEntities: () => Entity[] = () =>
|
||||
convertEntities({
|
||||
export const castDemoEntities: () => EntityInput[] = () =>
|
||||
Object.values({
|
||||
"light.reading_light": {
|
||||
entity_id: "light.reading_light",
|
||||
state: "on",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"cover.living_room_garden_shutter": {
|
||||
entity_id: "cover.living_room_garden_shutter",
|
||||
state: "open",
|
||||
@@ -142,7 +141,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"device_tracker.car": {
|
||||
entity_id: "sensor.outdoor_humidity",
|
||||
entity_id: "device_tracker.car",
|
||||
state: "not_home",
|
||||
attributes: {
|
||||
friendly_name: "Car",
|
||||
@@ -200,7 +199,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"binary_sensor.kitchen_motion": {
|
||||
entity_id: "light.kitchen_motion",
|
||||
entity_id: "binary_sensor.kitchen_motion",
|
||||
state: "on",
|
||||
attributes: {
|
||||
device_class: "motion",
|
||||
@@ -336,7 +335,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"sensor.rain": {
|
||||
entity_id: "sensor.moon_phase",
|
||||
entity_id: "sensor.rain",
|
||||
state: "7.2",
|
||||
attributes: {
|
||||
state_class: "total_increasing",
|
||||
@@ -566,7 +565,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"update.home_assistant_core_update": {
|
||||
entity_id: "update.home_assistant_supervisor_update",
|
||||
entity_id: "update.home_assistant_core_update",
|
||||
state: "off",
|
||||
attributes: {
|
||||
auto_update: false,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import type { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import type { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||
import type { Entity } from "../../../src/fake_data/entity";
|
||||
import type { EntityInput } from "../../../src/fake_data/entities/types";
|
||||
|
||||
export interface DemoConfig {
|
||||
index?: number;
|
||||
@@ -12,6 +12,6 @@ export interface DemoConfig {
|
||||
| string
|
||||
| ((localize: LocalizeFunc) => string | TemplateResult<1>);
|
||||
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
|
||||
entities: (localize: LocalizeFunc) => Entity[];
|
||||
entities: (localize: LocalizeFunc) => EntityInput[];
|
||||
theme: () => Record<string, string> | null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const mapEntities = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"zone.home": {
|
||||
entity_id: "zone.home",
|
||||
state: "zoning",
|
||||
@@ -51,7 +49,7 @@ export const mapEntities = () =>
|
||||
});
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
|
||||
@@ -1,175 +1,253 @@
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
|
||||
export const createMediaPlayerEntities = () => [
|
||||
getEntity("media_player", "music_paused", "paused", {
|
||||
friendly_name: "Pausing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover_2.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 50,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
source: "AirPlay",
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
}),
|
||||
getEntity("media_player", "music_playing", "playing", {
|
||||
friendly_name: "Playing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
|
||||
supported_features: 784959,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
group_members: ["media_player.playing", "media_player.stream_playing"],
|
||||
}),
|
||||
getEntity("media_player", "stream_playing", "playing", {
|
||||
friendly_name: "Playing the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play + Browse Media
|
||||
supported_features: 147489,
|
||||
}),
|
||||
getEntity("media_player", "stream_paused", "paused", {
|
||||
friendly_name: "Paused the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play
|
||||
supported_features: 16417,
|
||||
}),
|
||||
getEntity("media_player", "stream_playing_previous", "playing", {
|
||||
friendly_name: 'Playing the Stream (with "previous" support)',
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Previous Track + Play
|
||||
supported_features: 16401,
|
||||
}),
|
||||
getEntity("media_player", "tv_playing", "playing", {
|
||||
friendly_name: "Playing non-skip TV Show",
|
||||
media_content_type: "tvshow",
|
||||
media_title: "Chapter 1",
|
||||
media_series_title: "House of Cards",
|
||||
app_name: "Netflix",
|
||||
entity_picture: "/images/netflix.jpg",
|
||||
// Pause
|
||||
supported_features: 1,
|
||||
}),
|
||||
getEntity("media_player", "sonos_idle", "idle", {
|
||||
friendly_name: "Sonos Idle",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
volume_level: 0.33,
|
||||
is_volume_muted: true,
|
||||
}),
|
||||
getEntity("media_player", "idle_browse_media", "idle", {
|
||||
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
|
||||
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Play + Shuffle Set + Browse Media
|
||||
supported_features: 182839,
|
||||
volume_level: 0.79,
|
||||
}),
|
||||
getEntity("media_player", "theater_off", "off", {
|
||||
friendly_name: "TV Off",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
}),
|
||||
getEntity("media_player", "theater_on", "on", {
|
||||
friendly_name: "TV On",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
}),
|
||||
getEntity("media_player", "theater_off_static", "off", {
|
||||
friendly_name: "TV Off (cannot be switched on)",
|
||||
// Off + Next + Pause
|
||||
supported_features: 289,
|
||||
}),
|
||||
getEntity("media_player", "theater_on_static", "on", {
|
||||
friendly_name: "TV On (cannot be switched off)",
|
||||
// On + Next + Pause
|
||||
supported_features: 161,
|
||||
}),
|
||||
getEntity("media_player", "android_cast", "playing", {
|
||||
friendly_name: "Casting App (no supported features)",
|
||||
media_title: "Android Screen Casting",
|
||||
app_name: "Screen Mirroring",
|
||||
}),
|
||||
getEntity("media_player", "image_display", "playing", {
|
||||
friendly_name: "Digital Picture Frame",
|
||||
media_content_type: "image",
|
||||
media_title: "Famous Painting",
|
||||
media_artist: "Famous Artist",
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
// On + Off + Browse Media
|
||||
supported_features: 131456,
|
||||
}),
|
||||
getEntity("media_player", "unavailable", "unavailable", {
|
||||
friendly_name: "Player Unavailable",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "unknown", "unknown", {
|
||||
friendly_name: "Player Unknown",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "playing", "playing", {
|
||||
friendly_name: "Player Playing (no Pause support)",
|
||||
// Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21436,
|
||||
volume_level: 1,
|
||||
}),
|
||||
getEntity("media_player", "idle", "idle", {
|
||||
friendly_name: "Player Idle",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
volume_level: 0,
|
||||
}),
|
||||
getEntity("media_player", "receiver_on", "on", {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
volume_level: 0.63,
|
||||
is_volume_muted: false,
|
||||
source: "TV",
|
||||
sound_mode: "Movie",
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
}),
|
||||
getEntity("media_player", "receiver_off", "off", {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
}),
|
||||
{
|
||||
entity_id: "media_player.music_paused",
|
||||
state: "paused",
|
||||
attributes: {
|
||||
friendly_name: "Pausing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover_2.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 50,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
source: "AirPlay",
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.music_playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Playing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
|
||||
supported_features: 784959,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
group_members: ["media_player.playing", "media_player.stream_playing"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.stream_playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Playing the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play + Browse Media
|
||||
supported_features: 147489,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.stream_paused",
|
||||
state: "paused",
|
||||
attributes: {
|
||||
friendly_name: "Paused the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play
|
||||
supported_features: 16417,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.stream_playing_previous",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: 'Playing the Stream (with "previous" support)',
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Previous Track + Play
|
||||
supported_features: 16401,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.tv_playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Playing non-skip TV Show",
|
||||
media_content_type: "tvshow",
|
||||
media_title: "Chapter 1",
|
||||
media_series_title: "House of Cards",
|
||||
app_name: "Netflix",
|
||||
entity_picture: "/images/netflix.jpg",
|
||||
// Pause
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.sonos_idle",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Sonos Idle",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
volume_level: 0.33,
|
||||
is_volume_muted: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.idle_browse_media",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
|
||||
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Play + Shuffle Set + Browse Media
|
||||
supported_features: 182839,
|
||||
volume_level: 0.79,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_off",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "TV Off",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_on",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "TV On",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_off_static",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "TV Off (cannot be switched on)",
|
||||
// Off + Next + Pause
|
||||
supported_features: 289,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_on_static",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "TV On (cannot be switched off)",
|
||||
// On + Next + Pause
|
||||
supported_features: 161,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.android_cast",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Casting App (no supported features)",
|
||||
media_title: "Android Screen Casting",
|
||||
app_name: "Screen Mirroring",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.image_display",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Digital Picture Frame",
|
||||
media_content_type: "image",
|
||||
media_title: "Famous Painting",
|
||||
media_artist: "Famous Artist",
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
// On + Off + Browse Media
|
||||
supported_features: 131456,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Player Unavailable",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.unknown",
|
||||
state: "unknown",
|
||||
attributes: {
|
||||
friendly_name: "Player Unknown",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Player Playing (no Pause support)",
|
||||
// Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21436,
|
||||
volume_level: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.idle",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Player Idle",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
volume_level: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.receiver_on",
|
||||
state: "on",
|
||||
attributes: {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
volume_level: 0.63,
|
||||
is_volume_muted: false,
|
||||
source: "TV",
|
||||
sound_mode: "Movie",
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.receiver_off",
|
||||
state: "off",
|
||||
attributes: {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,72 +1,82 @@
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
|
||||
export const createPlantEntities = () => [
|
||||
getEntity("plant", "lemon_tree", "ok", {
|
||||
problem: "none",
|
||||
sensors: {
|
||||
moisture: "sensor.lemon_tree_moisture",
|
||||
battery: "sensor.lemon_tree_battery",
|
||||
temperature: "sensor.lemon_tree_temperature",
|
||||
conductivity: "sensor.lemon_tree_conductivity",
|
||||
brightness: "sensor.lemon_tree_brightness",
|
||||
{
|
||||
entity_id: "plant.lemon_tree",
|
||||
state: "ok",
|
||||
attributes: {
|
||||
problem: "none",
|
||||
sensors: {
|
||||
moisture: "sensor.lemon_tree_moisture",
|
||||
battery: "sensor.lemon_tree_battery",
|
||||
temperature: "sensor.lemon_tree_temperature",
|
||||
conductivity: "sensor.lemon_tree_conductivity",
|
||||
brightness: "sensor.lemon_tree_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 95,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 12,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Lemon Tree",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
{
|
||||
entity_id: "plant.apple_tree",
|
||||
state: "ok",
|
||||
attributes: {
|
||||
problem: "brightness",
|
||||
sensors: {
|
||||
moisture: "sensor.apple_tree_moisture",
|
||||
battery: "sensor.apple_tree_battery",
|
||||
temperature: "sensor.apple_tree_temperature",
|
||||
conductivity: "sensor.apple_tree_conductivity",
|
||||
brightness: "sensor.apple_tree_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 2,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Apple Tree",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 95,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 12,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Lemon Tree",
|
||||
}),
|
||||
getEntity("plant", "apple_tree", "ok", {
|
||||
problem: "brightness",
|
||||
sensors: {
|
||||
moisture: "sensor.apple_tree_moisture",
|
||||
battery: "sensor.apple_tree_battery",
|
||||
temperature: "sensor.apple_tree_temperature",
|
||||
conductivity: "sensor.apple_tree_conductivity",
|
||||
brightness: "sensor.apple_tree_brightness",
|
||||
},
|
||||
{
|
||||
entity_id: "plant.sunflowers",
|
||||
state: "ok",
|
||||
attributes: {
|
||||
problem: "moisture, temperature, conductivity",
|
||||
sensors: {
|
||||
moisture: "sensor.sunflowers_moisture",
|
||||
temperature: "sensor.sunflowers_temperature",
|
||||
conductivity: "sensor.sunflowers_conductivity",
|
||||
brightness: "sensor.sunflowers_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 2,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Apple Tree",
|
||||
}),
|
||||
getEntity("plant", "sunflowers", "ok", {
|
||||
problem: "moisture, temperature, conductivity",
|
||||
sensors: {
|
||||
moisture: "sensor.sunflowers_moisture",
|
||||
temperature: "sensor.sunflowers_temperature",
|
||||
conductivity: "sensor.sunflowers_conductivity",
|
||||
brightness: "sensor.sunflowers_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -5,17 +5,24 @@ import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { Action } from "../../../../src/data/script";
|
||||
import { describeAction } from "../../../../src/data/script_i18n";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("scene", "kitchen_morning", "scening", {
|
||||
friendly_name: "Kitchen Morning",
|
||||
}),
|
||||
getEntity("media_player", "kitchen", "playing", {
|
||||
friendly_name: "Sonos Kitchen",
|
||||
}),
|
||||
{
|
||||
entity_id: "scene.kitchen_morning",
|
||||
state: "scening",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Morning",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.kitchen",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Sonos Kitchen",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const ACTIONS = [
|
||||
|
||||
@@ -5,20 +5,31 @@ import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { Condition } from "../../../../src/data/automation";
|
||||
import { describeCondition } from "../../../../src/data/automation_i18n";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen", "on", {
|
||||
friendly_name: "Kitchen Light",
|
||||
}),
|
||||
getEntity("device_tracker", "person", "home", {
|
||||
friendly_name: "Person",
|
||||
}),
|
||||
getEntity("zone", "home", "", {
|
||||
friendly_name: "Home",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.person",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Person",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "",
|
||||
attributes: {
|
||||
friendly_name: "Home",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const conditions: Condition[] = [
|
||||
|
||||
@@ -5,20 +5,31 @@ import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { LegacyTrigger } from "../../../../src/data/automation";
|
||||
import { describeTrigger } from "../../../../src/data/automation_i18n";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen", "on", {
|
||||
friendly_name: "Kitchen Light",
|
||||
}),
|
||||
getEntity("person", "person", "", {
|
||||
friendly_name: "Person",
|
||||
}),
|
||||
getEntity("zone", "home", "", {
|
||||
friendly_name: "Home",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.person",
|
||||
state: "",
|
||||
attributes: {
|
||||
friendly_name: "Person",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "",
|
||||
attributes: {
|
||||
friendly_name: "Home",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const triggers = [
|
||||
|
||||
@@ -21,8 +21,8 @@ type DialogType =
|
||||
| "basic"
|
||||
| "basic-subtitle-below"
|
||||
| "basic-subtitle-above"
|
||||
| "allow-mode-change"
|
||||
| "form"
|
||||
| "form-block-mode"
|
||||
| "actions"
|
||||
| "large"
|
||||
| "small";
|
||||
@@ -69,8 +69,8 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
<ha-button @click=${this._handleOpenDialog("form")}
|
||||
>Adaptive dialog with form</ha-button
|
||||
>
|
||||
<ha-button @click=${this._handleOpenDialog("form-block-mode")}
|
||||
>Adaptive dialog with form (block mode change)</ha-button
|
||||
<ha-button @click=${this._handleOpenDialog("allow-mode-change")}
|
||||
>Adaptive dialog with allow mode change</ha-button
|
||||
>
|
||||
<ha-button @click=${this._handleOpenDialog("actions")}
|
||||
>Adaptive dialog with actions</ha-button
|
||||
@@ -164,27 +164,15 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
|
||||
<ha-adaptive-dialog
|
||||
.hass=${this._hass}
|
||||
.open=${this._openDialog === "form-block-mode"}
|
||||
header-title="Adaptive dialog with form (block mode change)"
|
||||
header-subtitle="This form will not reset when the viewport size changes"
|
||||
block-mode-change
|
||||
.allowModeChange=${this._openDialog === "allow-mode-change"}
|
||||
header-title="Adaptive dialog with allow mode change"
|
||||
header-subtitle="Resize the window while this dialog is open"
|
||||
@closed=${this._handleClosed}
|
||||
>
|
||||
<ha-form autofocus .schema=${SCHEMA}></ha-form>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
@click=${this._handleClosed}
|
||||
slot="secondaryAction"
|
||||
variant="plain"
|
||||
>Cancel</ha-button
|
||||
>
|
||||
<ha-button
|
||||
@click=${this._handleClosed}
|
||||
slot="primaryAction"
|
||||
variant="accent"
|
||||
>Submit</ha-button
|
||||
>
|
||||
</ha-dialog-footer>
|
||||
<div>
|
||||
This dialog can switch between dialog mode and bottom sheet mode
|
||||
while open.
|
||||
</div>
|
||||
</ha-adaptive-dialog>
|
||||
|
||||
<ha-adaptive-dialog
|
||||
@@ -225,10 +213,9 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The mode is determined automatically and updates when the window is
|
||||
resized. To prevent mode changes after the initial mount (useful for
|
||||
preventing form resets), use the <code>block-mode-change</code>
|
||||
attribute.
|
||||
By default, the mode is determined at mount time and then stays fixed
|
||||
while the dialog is open. To allow switching modes while the viewport
|
||||
changes, use the <code>allow-mode-change</code> attribute.
|
||||
</p>
|
||||
|
||||
<h3>Width</h3>
|
||||
@@ -399,10 +386,10 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Use the <code>block-mode-change</code> attribute when you want to
|
||||
prevent the dialog from switching modes after it's opened. This is
|
||||
especially useful for forms, as it prevents form data from being lost
|
||||
when users resize their browser window.
|
||||
Use the <code>allow-mode-change</code> attribute when you want the
|
||||
dialog to switch between modes as the viewport changes after opening.
|
||||
For forms, you can keep the default behavior to avoid resetting fields
|
||||
on resize.
|
||||
</p>
|
||||
|
||||
<h3>Example usage</h3>
|
||||
@@ -426,23 +413,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
</ha-dialog-footer>
|
||||
</ha-adaptive-dialog></code></pre>
|
||||
|
||||
<p>Example with <code>block-mode-change</code> for forms:</p>
|
||||
|
||||
<pre><code><ha-adaptive-dialog
|
||||
.hass=\${this.hass}
|
||||
open
|
||||
header-title="Edit configuration"
|
||||
block-mode-change
|
||||
>
|
||||
<ha-form .schema=\${schema} .data=\${data}></ha-form>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button slot="secondaryAction" variant="plain"
|
||||
>Cancel</ha-button
|
||||
>
|
||||
<ha-button slot="primaryAction" variant="accent">Save</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-adaptive-dialog></code></pre>
|
||||
|
||||
<h3>API</h3>
|
||||
|
||||
<p>
|
||||
@@ -520,12 +490,10 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>block-mode-change</code></td>
|
||||
<td><code>allow-mode-change</code></td>
|
||||
<td>
|
||||
When set, the mode is determined at mount time based on the
|
||||
current screen size, but subsequent mode changes are blocked.
|
||||
Useful for preventing forms from resetting when the viewport
|
||||
size changes.
|
||||
When set, the dialog can switch between modes as the viewport
|
||||
size changes while it is open.
|
||||
</td>
|
||||
<td><code>false</code></td>
|
||||
<td><code>false</code>, <code>true</code></td>
|
||||
@@ -547,6 +515,14 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
<td><code>--ha-dialog-surface-background</code></td>
|
||||
<td>Dialog/sheet background color.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
|
||||
<td>Dialog/sheet surface backdrop filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-box-shadow</code></td>
|
||||
<td>Dialog surface box shadow (dialog mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-border-radius</code></td>
|
||||
<td>Border radius of the dialog surface (dialog mode only).</td>
|
||||
@@ -559,6 +535,34 @@ export class DemoHaAdaptiveDialog extends LitElement {
|
||||
<td><code>--ha-dialog-hide-duration</code></td>
|
||||
<td>Hide animation duration (dialog mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
|
||||
<td>Dialog/sheet scrim backdrop filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-backdrop-filter</code></td>
|
||||
<td>Dialog/sheet scrim backdrop filter (legacy fallback).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--mdc-dialog-scrim-color</code></td>
|
||||
<td>Dialog/sheet scrim color (legacy compatibility).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-surface-background</code></td>
|
||||
<td>Bottom sheet background color (sheet mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-surface-backdrop-filter</code></td>
|
||||
<td>Bottom sheet surface backdrop filter (sheet mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-scrim-backdrop-filter</code></td>
|
||||
<td>Bottom sheet scrim backdrop filter (sheet mode only).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-bottom-sheet-scrim-color</code></td>
|
||||
<td>Bottom sheet scrim color (sheet mode only).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -380,13 +380,29 @@ export class DemoHaDialog extends LitElement {
|
||||
<td><code>--ha-dialog-surface-background</code></td>
|
||||
<td>Dialog background color.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
|
||||
<td>Backdrop filter applied to the dialog surface.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-box-shadow</code></td>
|
||||
<td>Dialog surface box shadow.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--ha-dialog-border-radius</code></td>
|
||||
<td>Border radius of the dialog surface.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-z-index</code></td>
|
||||
<td>Z-index for the dialog.</td>
|
||||
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
|
||||
<td>Backdrop filter applied to the dialog scrim.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-backdrop-filter</code></td>
|
||||
<td>Legacy fallback for the dialog scrim backdrop filter.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--mdc-dialog-scrim-color</code></td>
|
||||
<td>Dialog scrim color (legacy compatibility).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>--dialog-surface-margin-top</code></td>
|
||||
|
||||
@@ -12,34 +12,53 @@ import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("media_player", "livingroom", "playing", {
|
||||
friendly_name: "Livingroom",
|
||||
media_content_type: "music",
|
||||
device_class: "tv",
|
||||
}),
|
||||
getEntity("media_player", "lounge", "idle", {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
device_class: "speaker",
|
||||
}),
|
||||
getEntity("light", "bedroom", "on", {
|
||||
friendly_name: "Bedroom",
|
||||
effect: "colorloop",
|
||||
effect_list: ["colorloop", "random"],
|
||||
}),
|
||||
getEntity("switch", "coffee", "off", {
|
||||
friendly_name: "Coffee",
|
||||
device_class: "switch",
|
||||
}),
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.livingroom",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Livingroom",
|
||||
media_content_type: "music",
|
||||
device_class: "tv",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.lounge",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
device_class: "speaker",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bedroom",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bedroom",
|
||||
effect: "colorloop",
|
||||
effect_list: ["colorloop", "random"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.coffee",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Coffee",
|
||||
device_class: "switch",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const DEVICES: DeviceRegistryEntry[] = [
|
||||
@@ -461,6 +480,12 @@ const SCHEMAS: {
|
||||
},
|
||||
{ type: "string", name: "path", default: "/" },
|
||||
{ type: "boolean", name: "ssl", default: false },
|
||||
{
|
||||
type: "string",
|
||||
name: "comments",
|
||||
default: "disabled field",
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
82
gallery/src/pages/components/ha-input.markdown
Normal file
82
gallery/src/pages/components/ha-input.markdown
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: Input
|
||||
---
|
||||
|
||||
# Input `<ha-input>`
|
||||
|
||||
A text input component supporting Home Assistant theming and validation, based on webawesome input.
|
||||
Supports multiple input types including text, number, password, email, search, and more.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Example usage
|
||||
|
||||
```html
|
||||
<ha-input label="Name" value="Hello"></ha-input>
|
||||
|
||||
<ha-input label="Email" type="email" placeholder="you@example.com"></ha-input>
|
||||
|
||||
<ha-input label="Password" type="password" password-toggle></ha-input>
|
||||
|
||||
<ha-input label="Required" required></ha-input>
|
||||
|
||||
<ha-input label="Disabled" disabled value="Can't touch this"></ha-input>
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
This component is based on the webawesome input component.
|
||||
|
||||
**Slots**
|
||||
|
||||
- `start`: Content placed before the input (usually for icons or prefixes).
|
||||
- `end`: Content placed after the input (usually for icons or suffixes).
|
||||
- `label`: Custom label content. Overrides the `label` property.
|
||||
- `hint`: Custom hint content. Overrides the `hint` property.
|
||||
- `clear-icon`: Custom clear icon.
|
||||
- `show-password-icon`: Custom show password icon.
|
||||
- `hide-password-icon`: Custom hide password icon.
|
||||
|
||||
**Properties/Attributes**
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| appearance | "material"/"outlined" | "material" | Sets the input appearance style. "material" is the default filled style, "outlined" uses a bordered style. |
|
||||
| type | "text"/"number"/"password"/"email"/"search"/"tel"/"url"/"date"/"datetime-local"/"time"/"color" | "text" | Sets the input type. |
|
||||
| value | String | - | The current value of the input. |
|
||||
| label | String | "" | The input's label text. |
|
||||
| hint | String | "" | The input's hint/helper text. |
|
||||
| placeholder | String | "" | Placeholder text shown when the input is empty. |
|
||||
| with-clear | Boolean | false | Adds a clear button when the input is not empty. |
|
||||
| readonly | Boolean | false | Makes the input readonly. |
|
||||
| disabled | Boolean | false | Disables the input and prevents user interaction. |
|
||||
| required | Boolean | false | Makes the input a required field. |
|
||||
| password-toggle | Boolean | false | Adds a button to toggle the password visibility. |
|
||||
| without-spin-buttons | Boolean | false | Hides the browser's built-in spin buttons for number inputs. |
|
||||
| auto-validate | Boolean | false | Validates the input on blur instead of on form submit. |
|
||||
| invalid | Boolean | false | Marks the input as invalid. |
|
||||
| inset-label | Boolean | false | Uses an inset label style where the label stays inside the input. |
|
||||
| validation-message | String | "" | Custom validation message shown when the input is invalid. |
|
||||
| pattern | String | - | A regular expression pattern to validate input against. |
|
||||
| minlength | Number | - | The minimum length of input that will be considered valid. |
|
||||
| maxlength | Number | - | The maximum length of input that will be considered valid. |
|
||||
| min | Number/String | - | The input's minimum value. Only applies to date and number input types. |
|
||||
| max | Number/String | - | The input's maximum value. Only applies to date and number input types. |
|
||||
| step | Number/"any" | - | Specifies the granularity that the value must adhere to. |
|
||||
|
||||
**CSS Custom Properties**
|
||||
|
||||
- `--ha-input-padding-top` - Padding above the input.
|
||||
- `--ha-input-padding-bottom` - Padding below the input. Defaults to `var(--ha-space-2)`.
|
||||
- `--ha-input-text-align` - Text alignment of the input. Defaults to `start`.
|
||||
- `--ha-input-required-marker` - The marker shown after the label for required fields. Defaults to `"*"`.
|
||||
|
||||
---
|
||||
|
||||
## Derivatives
|
||||
|
||||
The following components extend or wrap `ha-input` for specific use cases:
|
||||
|
||||
- **`<ha-input-search>`** — A pre-configured search input with a magnify icon, clear button, and localized "Search" placeholder. Extends `ha-input`.
|
||||
- **`<ha-input-copy>`** — A read-only input with a copy-to-clipboard button. Supports optional value masking with a reveal toggle.
|
||||
- **`<ha-input-multi>`** — A dynamic list of text inputs for managing arrays of strings. Supports adding, removing, and drag-and-drop reordering.
|
||||
232
gallery/src/pages/components/ha-input.ts
Normal file
232
gallery/src/pages/components/ha-input.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import { mdiMagnify } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/input/ha-input";
|
||||
import "../../../../src/components/input/ha-input-copy";
|
||||
import "../../../../src/components/input/ha-input-multi";
|
||||
import "../../../../src/components/input/ha-input-search";
|
||||
import { localizeContext } from "../../../../src/data/context";
|
||||
|
||||
const LOCALIZE_KEYS: Record<string, string> = {
|
||||
"ui.common.copy": "Copy",
|
||||
"ui.common.show": "Show",
|
||||
"ui.common.hide": "Hide",
|
||||
"ui.common.add": "Add",
|
||||
"ui.common.remove": "Remove",
|
||||
"ui.common.search": "Search",
|
||||
"ui.common.copied_clipboard": "Copied to clipboard",
|
||||
};
|
||||
|
||||
@customElement("demo-components-ha-input")
|
||||
export class DemoHaInput extends LitElement {
|
||||
constructor() {
|
||||
super();
|
||||
// Provides localizeContext for ha-input-copy, ha-input-multi and ha-input-search
|
||||
// eslint-disable-next-line no-new
|
||||
new ContextProvider(this, {
|
||||
context: localizeContext,
|
||||
initialValue: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${["light", "dark"].map(
|
||||
(mode) => html`
|
||||
<div class=${mode}>
|
||||
<ha-card header="ha-input in ${mode}">
|
||||
<div class="card-content">
|
||||
<h3>Basic</h3>
|
||||
<div class="row">
|
||||
<ha-input label="Default"></ha-input>
|
||||
<ha-input label="With value" value="Hello"></ha-input>
|
||||
<ha-input
|
||||
label="With placeholder"
|
||||
placeholder="Type here..."
|
||||
></ha-input>
|
||||
</div>
|
||||
|
||||
<h3>Input types</h3>
|
||||
<div class="row">
|
||||
<ha-input label="Text" type="text" value="Text"></ha-input>
|
||||
<ha-input label="Number" type="number" value="42"></ha-input>
|
||||
<ha-input
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
></ha-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
label="Password"
|
||||
type="password"
|
||||
value="secret"
|
||||
password-toggle
|
||||
></ha-input>
|
||||
<ha-input label="URL" type="url" placeholder="https://...">
|
||||
</ha-input>
|
||||
<ha-input label="Date" type="date"></ha-input>
|
||||
</div>
|
||||
|
||||
<h3>States</h3>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
label="Disabled"
|
||||
disabled
|
||||
value="Disabled"
|
||||
></ha-input>
|
||||
<ha-input
|
||||
label="Readonly"
|
||||
readonly
|
||||
value="Readonly"
|
||||
></ha-input>
|
||||
<ha-input label="Required" required></ha-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
label="Invalid"
|
||||
invalid
|
||||
validation-message="This field is required"
|
||||
value=""
|
||||
></ha-input>
|
||||
<ha-input label="With hint" hint="This is a hint"></ha-input>
|
||||
<ha-input
|
||||
label="With clear"
|
||||
with-clear
|
||||
value="Clear me"
|
||||
></ha-input>
|
||||
</div>
|
||||
|
||||
<h3>With slots</h3>
|
||||
<div class="row">
|
||||
<ha-input label="With prefix">
|
||||
<span slot="start">$</span>
|
||||
</ha-input>
|
||||
<ha-input label="With suffix">
|
||||
<span slot="end">kg</span>
|
||||
</ha-input>
|
||||
<ha-input label="With icon">
|
||||
<ha-svg-icon .path=${mdiMagnify} slot="start"></ha-svg-icon>
|
||||
</ha-input>
|
||||
</div>
|
||||
|
||||
<h3>Appearance: outlined</h3>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
label="Outlined"
|
||||
value="Hello"
|
||||
></ha-input>
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
label="Outlined disabled"
|
||||
disabled
|
||||
value="Disabled"
|
||||
></ha-input>
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
label="Outlined invalid"
|
||||
invalid
|
||||
validation-message="Required"
|
||||
></ha-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
placeholder="Placeholder only"
|
||||
></ha-input>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Derivatives in ${mode}">
|
||||
<div class="card-content">
|
||||
<h3>ha-input-search</h3>
|
||||
<ha-input-search label="Search label"></ha-input-search>
|
||||
<ha-input-search appearance="outlined"></ha-input-search>
|
||||
|
||||
<h3>ha-input-copy</h3>
|
||||
<ha-input-copy
|
||||
value="my-api-token-123"
|
||||
masked-value="••••••••••••••••••"
|
||||
masked-toggle
|
||||
></ha-input-copy>
|
||||
|
||||
<h3>ha-input-multi</h3>
|
||||
<ha-input-multi
|
||||
label="URL"
|
||||
add-label="Add URL"
|
||||
.value=${["https://example.com"]}
|
||||
></ha-input-multi>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
applyThemesOnElement(
|
||||
this.shadowRoot!.querySelector(".dark"),
|
||||
{
|
||||
default_theme: "default",
|
||||
default_dark_theme: "default",
|
||||
themes: {},
|
||||
darkMode: true,
|
||||
theme: "default",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.dark,
|
||||
.light {
|
||||
display: block;
|
||||
background-color: var(--primary-background-color);
|
||||
padding: 0 50px;
|
||||
}
|
||||
ha-card {
|
||||
margin: 24px auto;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-2);
|
||||
}
|
||||
h3 {
|
||||
margin: var(--ha-space-4) 0 var(--ha-space-1) 0;
|
||||
font-size: var(--ha-font-size-l);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
gap: var(--ha-space-4);
|
||||
}
|
||||
.row > * {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-components-ha-input": DemoHaInput;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
|
||||
@@ -8,6 +8,7 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
|
||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
|
||||
import type { HASSDomEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-selector/ha-selector";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
@@ -16,33 +17,59 @@ import type { BlueprintInput } from "../../../../src/data/blueprint";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
|
||||
import type { LabelRegistryEntry } from "../../../../src/data/label/label_registry";
|
||||
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
showDialog,
|
||||
type ShowDialogParams,
|
||||
} from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("media_player", "livingroom", "playing", {
|
||||
friendly_name: "Livingroom",
|
||||
}),
|
||||
getEntity("media_player", "lounge", "idle", {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
}),
|
||||
getEntity("light", "bedroom", "on", {
|
||||
friendly_name: "Bedroom",
|
||||
}),
|
||||
getEntity("switch", "coffee", "off", {
|
||||
friendly_name: "Coffee",
|
||||
}),
|
||||
getEntity("number", "number", 5, {
|
||||
friendly_name: "Number",
|
||||
}),
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.livingroom",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Livingroom",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.lounge",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bedroom",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bedroom",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.coffee",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Coffee",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.number",
|
||||
state: "5",
|
||||
attributes: {
|
||||
friendly_name: "Number",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const DEVICES: DeviceRegistryEntry[] = [
|
||||
@@ -611,14 +638,15 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||
};
|
||||
};
|
||||
|
||||
private _dialogManager = (e) => {
|
||||
const { dialogTag, dialogImport, dialogParams, addHistory } = e.detail;
|
||||
private _dialogManager = (e: HASSDomEvent<ShowDialogParams<unknown>>) => {
|
||||
const { dialogTag, dialogImport, dialogParams, addHistory, parentElement } =
|
||||
e.detail;
|
||||
showDialog(
|
||||
this,
|
||||
this.shadowRoot!,
|
||||
dialogTag,
|
||||
dialogParams,
|
||||
dialogImport,
|
||||
parentElement,
|
||||
addHistory
|
||||
);
|
||||
};
|
||||
@@ -664,7 +692,11 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||
([key, value]) => html`
|
||||
<ha-settings-row narrow slot=${slot}>
|
||||
<span slot="heading">${value?.name || key}</span>
|
||||
<span slot="description">${value?.description}</span>
|
||||
${value?.description
|
||||
? html`<span slot="description"
|
||||
>${value?.description}</span
|
||||
>`
|
||||
: nothing}
|
||||
<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${value!.selector}
|
||||
|
||||
@@ -28,9 +28,12 @@ This element is based on webawesome `wa-tooltip` it only sets some css tokens an
|
||||
|
||||
In your theme settings use this without the prefixed `--`.
|
||||
|
||||
- `--ha-tooltip-border-radius` (Default: 4px)
|
||||
- `--ha-tooltip-arrow-size` (Default: 8px)
|
||||
- `--wa-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
|
||||
- `--ha-tooltip-background-color` (Default: `var(--secondary-background-color)`)
|
||||
- `--ha-tooltip-text-color` (Default: `var(--primary-text-color)`)
|
||||
- `--ha-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
|
||||
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
|
||||
- `--wa-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
|
||||
- `--wa-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
|
||||
- `--ha-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
|
||||
- `--ha-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
|
||||
- `--ha-tooltip-padding` (Default: 8px)
|
||||
- `--ha-tooltip-border-radius` (Default: `var(--ha-border-radius-sm)`)
|
||||
- `--ha-tooltip-arrow-size` (Default: 8px)
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("alarm_control_panel", "alarm_armed", "armed_home", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("alarm_control_panel", "unavailable", "unavailable", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("alarm_control_panel", "alarm_code", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
code_format: "number",
|
||||
}),
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm_armed",
|
||||
state: "armed_home",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm_code",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
code_format: "number",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,44 +1,79 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("switch", "bed_ac", "on", {
|
||||
friendly_name: "Ecobee",
|
||||
}),
|
||||
getEntity("sensor", "bed_temp", "72", {
|
||||
friendly_name: "Bedroom Temp",
|
||||
device_class: "temperature",
|
||||
unit_of_measurement: "°F",
|
||||
}),
|
||||
getEntity("light", "living_room_light", "off", {
|
||||
friendly_name: "Living Room Light",
|
||||
}),
|
||||
getEntity("fan", "living_room", "on", {
|
||||
friendly_name: "Living Room Fan",
|
||||
}),
|
||||
getEntity("sensor", "office_humidity", "73", {
|
||||
friendly_name: "Office Humidity",
|
||||
device_class: "humidity",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("light", "office", "on", {
|
||||
friendly_name: "Office Light",
|
||||
}),
|
||||
getEntity("fan", "kitchen", "on", {
|
||||
friendly_name: "Kitchen Fan",
|
||||
}),
|
||||
getEntity("binary_sensor", "kitchen_door", "on", {
|
||||
friendly_name: "Office Door",
|
||||
device_class: "door",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.bed_ac",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Ecobee",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.bed_temp",
|
||||
state: "72",
|
||||
attributes: {
|
||||
friendly_name: "Bedroom Temp",
|
||||
device_class: "temperature",
|
||||
unit_of_measurement: "°F",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.living_room_light",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Living Room Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.living_room",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Living Room Fan",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.office_humidity",
|
||||
state: "73",
|
||||
attributes: {
|
||||
friendly_name: "Office Humidity",
|
||||
device_class: "humidity",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.office",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Office Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Fan",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.kitchen_door",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Office Door",
|
||||
device_class: "door",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// TODO: Update image here
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "controller_1", "on", {
|
||||
friendly_name: "Controller 1",
|
||||
}),
|
||||
getEntity("light", "controller_2", "on", {
|
||||
friendly_name: "Controller 2",
|
||||
}),
|
||||
getEntity("light", "floor", "off", {
|
||||
friendly_name: "Floor light",
|
||||
}),
|
||||
getEntity("light", "kitchen", "on", {
|
||||
friendly_name: "Kitchen light",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.controller_1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Controller 1",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.controller_2",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Controller 2",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.floor",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Floor light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen light",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,150 +1,257 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("group", "kitchen", "on", {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "Kitchen Group",
|
||||
}),
|
||||
getEntity("lock", "kitchen_door", "locked", {
|
||||
friendly_name: "Kitchen Lock",
|
||||
}),
|
||||
getEntity("cover", "kitchen_window", "open", {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
}),
|
||||
getEntity("scene", "romantic_lights", "scening", {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_paulus", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("climate", "ecobee", "auto", {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
operation_mode: "auto",
|
||||
operation_list: ["heat", "cool", "auto", "off"],
|
||||
hold_mode: "home",
|
||||
swing_mode: "Auto",
|
||||
swing_list: ["Auto", "1", "2", "3", "Off"],
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
}),
|
||||
getEntity("input_number", "number", 5, {
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Number",
|
||||
icon: "mdi:bell-ring",
|
||||
}),
|
||||
getEntity("input_boolean", "toggle", "on", {
|
||||
friendly_name: "Toggle",
|
||||
}),
|
||||
getEntity("input_datetime", "date_and_time", "2022-01-10 00:00:00", {
|
||||
has_date: true,
|
||||
has_time: true,
|
||||
editable: true,
|
||||
year: 2022,
|
||||
month: 1,
|
||||
day: 10,
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
timestamp: 1641801600,
|
||||
friendly_name: "Date and Time",
|
||||
}),
|
||||
getEntity("sensor", "humidity", "23.2", {
|
||||
friendly_name: "Humidity",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("input_select", "dropdown", "Soda", {
|
||||
friendly_name: "Dropdown",
|
||||
options: ["Soda", "Beer", "Wine"],
|
||||
}),
|
||||
getEntity("input_text", "text", "Inspiration", {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
}),
|
||||
getEntity("timer", "timer", "idle", {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
}),
|
||||
getEntity("counter", "counter", "3", {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
step: 1,
|
||||
minimum: 0,
|
||||
maximum: 10,
|
||||
}),
|
||||
getEntity("text", "message", "Hello!", {
|
||||
friendly_name: "Message",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "group.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "Kitchen Group",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.kitchen_door",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lock",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.kitchen_window",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "scene.romantic_lights",
|
||||
state: "scening",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.ecobee",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
operation_mode: "auto",
|
||||
operation_list: ["heat", "cool", "auto", "off"],
|
||||
hold_mode: "home",
|
||||
swing_mode: "Auto",
|
||||
swing_list: ["Auto", "1", "2", "3", "Off"],
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.number",
|
||||
state: "5",
|
||||
attributes: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Number",
|
||||
icon: "mdi:bell-ring",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_boolean.toggle",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Toggle",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_datetime.date_and_time",
|
||||
state: "2022-01-10 00:00:00",
|
||||
attributes: {
|
||||
has_date: true,
|
||||
has_time: true,
|
||||
editable: true,
|
||||
year: 2022,
|
||||
month: 1,
|
||||
day: 10,
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
timestamp: 1641801600,
|
||||
friendly_name: "Date and Time",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.humidity",
|
||||
state: "23.2",
|
||||
attributes: {
|
||||
friendly_name: "Humidity",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_select.dropdown",
|
||||
state: "Soda",
|
||||
attributes: {
|
||||
friendly_name: "Dropdown",
|
||||
options: ["Soda", "Beer", "Wine"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_text.text",
|
||||
state: "Inspiration",
|
||||
attributes: {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "timer.timer",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "counter.counter",
|
||||
state: "3",
|
||||
attributes: {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
step: 1,
|
||||
minimum: 0,
|
||||
maximum: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "text.message",
|
||||
state: "Hello!",
|
||||
attributes: {
|
||||
friendly_name: "Message",
|
||||
},
|
||||
},
|
||||
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("lock", "unavailable", "unavailable", {
|
||||
friendly_name: "Kitchen Door",
|
||||
}),
|
||||
getEntity("cover", "unavailable", "unavailable", {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
}),
|
||||
getEntity("scene", "unavailable", "unavailable", {
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("device_tracker", "unavailable", "unavailable", {
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("climate", "unavailable", "unavailable", {
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
}),
|
||||
getEntity("input_number", "unavailable", "unavailable", {
|
||||
friendly_name: "Allowed Noise",
|
||||
icon: "mdi:bell-ring",
|
||||
}),
|
||||
getEntity("input_select", "unavailable", "unavailable", {
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Who cooks",
|
||||
icon: "mdi:cheff",
|
||||
}),
|
||||
getEntity("text", "unavailable", "unavailable", {
|
||||
friendly_name: "Message",
|
||||
}),
|
||||
getEntity("event", "unavailable", "unavailable", {
|
||||
friendly_name: "Empty remote",
|
||||
}),
|
||||
getEntity("event", "doorbell", "2023-07-17T21:26:11.615+00:00", {
|
||||
friendly_name: "Doorbell",
|
||||
device_class: "doorbell",
|
||||
event_type: "Ding-Dong",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Door",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "scene.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Allowed Noise",
|
||||
icon: "mdi:bell-ring",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_select.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Who cooks",
|
||||
icon: "mdi:cheff",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "text.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Message",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "event.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Empty remote",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "event.doorbell",
|
||||
state: "2023-07-17T21:26:11.615+00:00",
|
||||
attributes: {
|
||||
friendly_name: "Doorbell",
|
||||
device_class: "doorbell",
|
||||
event_type: "Ding-Dong",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,74 +1,117 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "work", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 25,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_anne_therese", "school", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 50,
|
||||
friendly_name: "Anne Therese",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_home_boy", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 75,
|
||||
friendly_name: "Home Boy",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "ceiling_lights", "off", {
|
||||
friendly_name: "Ceiling Lights",
|
||||
}),
|
||||
getEntity("sensor", "battery_1", 20, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 1",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "battery_2", 35, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 2",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "battery_3", 40, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 3",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "battery_4", 80, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 4",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("input_number", "min_battery_level", 30, {
|
||||
mode: "slider",
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
icon: "mdi:battery-alert-variant",
|
||||
friendly_name: "Minimum Battery Level",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "work",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 25,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_anne_therese",
|
||||
state: "school",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 50,
|
||||
friendly_name: "Anne Therese",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_home_boy",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 75,
|
||||
friendly_name: "Home Boy",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.ceiling_lights",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_1",
|
||||
state: "20",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 1",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_2",
|
||||
state: "35",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 2",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_3",
|
||||
state: "40",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 3",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_4",
|
||||
state: "80",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 4",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.min_battery_level",
|
||||
state: "30",
|
||||
attributes: {
|
||||
mode: "slider",
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
icon: "mdi:battery-alert-variant",
|
||||
friendly_name: "Minimum Battery Level",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("sensor", "brightness", "12", {}),
|
||||
getEntity("sensor", "brightness_medium", "53", {}),
|
||||
getEntity("sensor", "brightness_high", "87", {}),
|
||||
getEntity("plant", "bonsai", "ok", {}),
|
||||
getEntity("sensor", "not_working", "unavailable", {}),
|
||||
getEntity("sensor", "outside_humidity", "54", {
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "outside_temperature", "15.6", {
|
||||
unit_of_measurement: "°C",
|
||||
}),
|
||||
{ entity_id: "sensor.brightness", state: "12", attributes: {} },
|
||||
{ entity_id: "sensor.brightness_medium", state: "53", attributes: {} },
|
||||
{ entity_id: "sensor.brightness_high", state: "87", attributes: {} },
|
||||
{ entity_id: "plant.bonsai", state: "ok", attributes: {} },
|
||||
{ entity_id: "sensor.not_working", state: "unavailable", attributes: {} },
|
||||
{
|
||||
entity_id: "sensor.outside_humidity",
|
||||
state: "54",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.outside_temperature",
|
||||
state: "15.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "°C",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
@@ -47,6 +54,19 @@ const CONFIGS = [
|
||||
needle: true
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Rendering needle and severity levels",
|
||||
config: `
|
||||
- type: gauge
|
||||
entity: sensor.brightness_high
|
||||
name: Brightness High
|
||||
needle: true
|
||||
severity:
|
||||
red: 75
|
||||
green: 0
|
||||
yellow: 50
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Setting severity levels",
|
||||
config: `
|
||||
@@ -114,6 +134,21 @@ const CONFIGS = [
|
||||
entity: sensor.not_working
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Lower minimum",
|
||||
config: `
|
||||
- type: gauge
|
||||
entity: sensor.brightness_high
|
||||
needle: true
|
||||
severity:
|
||||
green: 0
|
||||
yellow: 0.45
|
||||
red: 0.9
|
||||
min: -0.05
|
||||
name: " "
|
||||
max: 1.9
|
||||
unit: GBP/h`,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-lovelace-gauge-card")
|
||||
|
||||
@@ -1,62 +1,89 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("media_player", "living_room", "playing", {
|
||||
volume_level: 1,
|
||||
is_volume_muted: false,
|
||||
media_content_id: "eyU3bRy2x44",
|
||||
media_content_type: "movie",
|
||||
media_duration: 300,
|
||||
media_position: 45.017773,
|
||||
media_position_updated_at: "2018-07-19T10:44:45.919514+00:00",
|
||||
media_title: "♥♥ The Best Fireplace Video (3 hours)",
|
||||
app_name: "YouTube",
|
||||
sound_mode: "Dummy Music",
|
||||
sound_mode_list: ["Dummy Music", "Dummy Movie"],
|
||||
shuffle: false,
|
||||
friendly_name: "Living Room",
|
||||
entity_picture:
|
||||
"/api/media_player_proxy/media_player.living_room?token=e925f8db7f7bd1f317e4524dcb8333d60f6019219a3799a22604b5787f243567&cache=bc2ffb49c4f67034",
|
||||
supported_features: 115597,
|
||||
}),
|
||||
getEntity("sun", "sun", "below_horizon", {
|
||||
next_dawn: "2018-07-19T20:48:47+00:00",
|
||||
next_dusk: "2018-07-20T11:46:06+00:00",
|
||||
next_midnight: "2018-07-19T16:17:28+00:00",
|
||||
next_noon: "2018-07-20T04:17:26+00:00",
|
||||
next_rising: "2018-07-19T21:16:31+00:00",
|
||||
next_setting: "2018-07-20T11:18:22+00:00",
|
||||
elevation: 67.69,
|
||||
azimuth: 338.55,
|
||||
friendly_name: "Sun",
|
||||
}),
|
||||
getEntity("cover", "kitchen_window", "open", {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
}),
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "ceiling_lights", "off", {
|
||||
friendly_name: "Ceiling Lights",
|
||||
}),
|
||||
getEntity("lock", "kitchen_door", "locked", {
|
||||
friendly_name: "Kitchen Door",
|
||||
}),
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.living_room",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
volume_level: 1,
|
||||
is_volume_muted: false,
|
||||
media_content_id: "eyU3bRy2x44",
|
||||
media_content_type: "movie",
|
||||
media_duration: 300,
|
||||
media_position: 45.017773,
|
||||
media_position_updated_at: "2018-07-19T10:44:45.919514+00:00",
|
||||
media_title: "♥♥ The Best Fireplace Video (3 hours)",
|
||||
app_name: "YouTube",
|
||||
sound_mode: "Dummy Music",
|
||||
sound_mode_list: ["Dummy Music", "Dummy Movie"],
|
||||
shuffle: false,
|
||||
friendly_name: "Living Room",
|
||||
entity_picture:
|
||||
"/api/media_player_proxy/media_player.living_room?token=e925f8db7f7bd1f317e4524dcb8333d60f6019219a3799a22604b5787f243567&cache=bc2ffb49c4f67034",
|
||||
supported_features: 115597,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sun.sun",
|
||||
state: "below_horizon",
|
||||
attributes: {
|
||||
next_dawn: "2018-07-19T20:48:47+00:00",
|
||||
next_dusk: "2018-07-20T11:46:06+00:00",
|
||||
next_midnight: "2018-07-19T16:17:28+00:00",
|
||||
next_noon: "2018-07-20T04:17:26+00:00",
|
||||
next_rising: "2018-07-19T21:16:31+00:00",
|
||||
next_setting: "2018-07-20T11:18:22+00:00",
|
||||
elevation: 67.69,
|
||||
azimuth: 338.55,
|
||||
friendly_name: "Sun",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.kitchen_window",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.ceiling_lights",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.kitchen_door",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Door",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -2,46 +2,69 @@ import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Lights",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_paulus", "work", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_anne_therese", "school", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Anne Therese",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_home_boy", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Home Boy",
|
||||
}),
|
||||
getEntity("sensor", "illumination", "23", {
|
||||
friendly_name: "Illumination",
|
||||
unit_of_measurement: "lx",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "work",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_anne_therese",
|
||||
state: "school",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Anne Therese",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_home_boy",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Home Boy",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.illumination",
|
||||
state: "23",
|
||||
attributes: {
|
||||
friendly_name: "Illumination",
|
||||
unit_of_measurement: "lx",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,29 +1,44 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
brightness: 255,
|
||||
}),
|
||||
getEntity("light", "dim_on", "on", {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
brightness: 100,
|
||||
}),
|
||||
getEntity("light", "dim_off", "off", {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
}),
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Lost Light",
|
||||
supported_features: 1,
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
brightness: 255,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.dim_on",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
brightness: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.dim_off",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Lost Light",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,59 +1,86 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "not_home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_home_boy", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.87334,
|
||||
longitude: 117.22745,
|
||||
gps_accuracy: 20,
|
||||
battery: 53,
|
||||
friendly_name: "Home Boy",
|
||||
}),
|
||||
getEntity("zone", "home", "zoning", {
|
||||
latitude: 32.87354,
|
||||
longitude: 117.22765,
|
||||
radius: 100,
|
||||
friendly_name: "Home",
|
||||
icon: "mdi:home",
|
||||
}),
|
||||
getEntity("zone", "bushfire", "zoning", {
|
||||
latitude: -33.8611,
|
||||
longitude: 151.203,
|
||||
radius: 35000,
|
||||
friendly_name: "Bushfire Zone",
|
||||
icon: "mdi:home",
|
||||
}),
|
||||
getEntity("geo_location", "nelsons_creek", "15", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -34.07792,
|
||||
longitude: 151.03219,
|
||||
friendly_name: "Nelsons Creek",
|
||||
}),
|
||||
getEntity("geo_location", "forest_rd_nowra_hill", "8", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.69452,
|
||||
longitude: 151.19577,
|
||||
friendly_name: "Forest Rd, Nowra Hill",
|
||||
}),
|
||||
getEntity("geo_location", "stoney_ridge_rd_kremnos", "20", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.66584,
|
||||
longitude: 150.97209,
|
||||
friendly_name: "Stoney Ridge Rd, Kremnos",
|
||||
}),
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "not_home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_home_boy",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.87334,
|
||||
longitude: 117.22745,
|
||||
gps_accuracy: 20,
|
||||
battery: 53,
|
||||
friendly_name: "Home Boy",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "zoning",
|
||||
attributes: {
|
||||
latitude: 32.87354,
|
||||
longitude: 117.22765,
|
||||
radius: 100,
|
||||
friendly_name: "Home",
|
||||
icon: "mdi:home",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.bushfire",
|
||||
state: "zoning",
|
||||
attributes: {
|
||||
latitude: -33.8611,
|
||||
longitude: 151.203,
|
||||
radius: 35000,
|
||||
friendly_name: "Bushfire Zone",
|
||||
icon: "mdi:home",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "geo_location.nelsons_creek",
|
||||
state: "15",
|
||||
attributes: {
|
||||
source: "bushfire_demo",
|
||||
latitude: -34.07792,
|
||||
longitude: 151.03219,
|
||||
friendly_name: "Nelsons Creek",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "geo_location.forest_rd_nowra_hill",
|
||||
state: "8",
|
||||
attributes: {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.69452,
|
||||
longitude: 151.19577,
|
||||
friendly_name: "Forest Rd, Nowra Hill",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "geo_location.stoney_ridge_rd_kremnos",
|
||||
state: "20",
|
||||
attributes: {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.66584,
|
||||
longitude: 150.97209,
|
||||
friendly_name: "Stoney Ridge Rd, Kremnos",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,40 +1,63 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("group", "all_lights", "on", {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "All Lights",
|
||||
}),
|
||||
getEntity("camera", "demo_camera", "idle", {
|
||||
access_token:
|
||||
"2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
friendly_name: "Demo camera",
|
||||
entity_picture:
|
||||
"/api/camera_proxy/camera.demo_camera?token=2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
}),
|
||||
getEntity("binary_sensor", "movement_backyard", "on", {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "motion",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
getEntity("sensor", "battery", 35, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "group.all_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "All Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "camera.demo_camera",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
access_token:
|
||||
"2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
friendly_name: "Demo camera",
|
||||
entity_picture:
|
||||
"/api/camera_proxy/camera.demo_camera?token=2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.movement_backyard",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "motion",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery",
|
||||
state: "35",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "bed_light", "off", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,35 +1,58 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("switch", "decorative_lights", "on", {
|
||||
friendly_name: "Decorative Lights",
|
||||
}),
|
||||
getEntity("light", "ceiling_lights", "on", {
|
||||
friendly_name: "Ceiling Lights",
|
||||
}),
|
||||
getEntity("binary_sensor", "movement_backyard", "on", {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "moving",
|
||||
}),
|
||||
getEntity("binary_sensor", "basement_floor_wet", "off", {
|
||||
friendly_name: "Basement Floor Wet",
|
||||
device_class: "moisture",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
getEntity("sensor", "battery", 35, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
{
|
||||
entity_id: "switch.decorative_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Decorative Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.ceiling_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.movement_backyard",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "moving",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.basement_floor_wet",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Basement Floor Wet",
|
||||
device_class: "moisture",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery",
|
||||
state: "35",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,100 +1,123 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("climate", "ecobee", "auto", {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 59,
|
||||
preset_mode: "eco",
|
||||
preset_modes: ["away", "eco"],
|
||||
}),
|
||||
getEntity("climate", "nest", "heat", {
|
||||
current_temperature: 17,
|
||||
min_temp: 15,
|
||||
max_temp: 25,
|
||||
temperature: 19,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Nest",
|
||||
supported_features: 43,
|
||||
}),
|
||||
getEntity("climate", "overkiz_radiator", "heat", {
|
||||
current_temperature: 18,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
temperature: 20,
|
||||
hvac_modes: ["heat", "auto", "off"],
|
||||
friendly_name: "Overkiz radiator",
|
||||
supported_features: 17,
|
||||
preset_mode: "comfort",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
"auto",
|
||||
"boost",
|
||||
"external",
|
||||
"prog",
|
||||
],
|
||||
}),
|
||||
getEntity("climate", "overkiz_towel_dryer", "heat", {
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
hvac_modes: ["heat", "off"],
|
||||
friendly_name: "Overkiz towel dryer",
|
||||
supported_features: 16,
|
||||
preset_mode: "eco",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
}),
|
||||
getEntity("climate", "sensibo", "fan_only", {
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
friendly_name: "Sensibo purifier",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
swing_modes: ["both", "rangefull", "off"],
|
||||
swing_mode: "rangefull",
|
||||
swing_horizontal_modes: ["both", "rangefull", "off"],
|
||||
swing_horizontal_mode: "both",
|
||||
supported_features: 553,
|
||||
}),
|
||||
getEntity("climate", "unavailable", "unavailable", {
|
||||
supported_features: 43,
|
||||
}),
|
||||
{
|
||||
entity_id: "climate.ecobee",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 59,
|
||||
preset_mode: "eco",
|
||||
preset_modes: ["away", "eco"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.nest",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: 17,
|
||||
min_temp: 15,
|
||||
max_temp: 25,
|
||||
temperature: 19,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Nest",
|
||||
supported_features: 43,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.overkiz_radiator",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: 18,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
temperature: 20,
|
||||
hvac_modes: ["heat", "auto", "off"],
|
||||
friendly_name: "Overkiz radiator",
|
||||
supported_features: 17,
|
||||
preset_mode: "comfort",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
"auto",
|
||||
"boost",
|
||||
"external",
|
||||
"prog",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.overkiz_towel_dryer",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
hvac_modes: ["heat", "off"],
|
||||
friendly_name: "Overkiz towel dryer",
|
||||
supported_features: 16,
|
||||
preset_mode: "eco",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.sensibo",
|
||||
state: "fan_only",
|
||||
attributes: {
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
friendly_name: "Sensibo purifier",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
swing_modes: ["both", "rangefull", "off"],
|
||||
swing_mode: "rangefull",
|
||||
swing_horizontal_modes: ["both", "rangefull", "off"],
|
||||
swing_horizontal_mode: "both",
|
||||
supported_features: 553,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
supported_features: 43,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -6,7 +6,6 @@ import { LightColorMode } from "../../../../src/data/light";
|
||||
import { LockEntityFeature } from "../../../../src/data/lock";
|
||||
import { MediaPlayerEntityFeature } from "../../../../src/data/media-player";
|
||||
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
@@ -14,102 +13,154 @@ import { ClimateEntityFeature } from "../../../../src/data/climate";
|
||||
import { FanEntityFeature } from "../../../../src/data/fan";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("switch", "tv_outlet", "on", {
|
||||
friendly_name: "TV outlet",
|
||||
device_class: "outlet",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
|
||||
}),
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable entity",
|
||||
}),
|
||||
getEntity("lock", "front_door", "locked", {
|
||||
friendly_name: "Front Door Lock",
|
||||
device_class: "lock",
|
||||
supported_features: LockEntityFeature.OPEN,
|
||||
}),
|
||||
getEntity("media_player", "living_room", "playing", {
|
||||
friendly_name: "Living room speaker",
|
||||
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
|
||||
}),
|
||||
getEntity("climate", "thermostat", "heat", {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: 80,
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
friendly_name: "Thermostat",
|
||||
hvac_action: "heating",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
}),
|
||||
getEntity("cover", "kitchen_shutter", "open", {
|
||||
friendly_name: "Kitchen shutter",
|
||||
device_class: "shutter",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP,
|
||||
}),
|
||||
getEntity("cover", "pergola_roof", "open", {
|
||||
friendly_name: "Pergola Roof",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT,
|
||||
}),
|
||||
getEntity("input_number", "counter", "1.0", {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
}),
|
||||
getEntity("climate", "dual_thermostat", "heat/cool", {
|
||||
friendly_name: "Dual thermostat",
|
||||
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
|
||||
preset_modes: ["home", "eco", "away"],
|
||||
swing_modes: ["auto", "1", "2", "3", "off"],
|
||||
switch_horizontal_modes: ["auto", "4", "5", "6", "off"],
|
||||
current_temperature: 23,
|
||||
target_temp_high: 24,
|
||||
target_temp_low: 21,
|
||||
fan_mode: "auto_low",
|
||||
preset_mode: "home",
|
||||
swing_mode: "auto",
|
||||
swing_horizontal_mode: "off",
|
||||
supported_features:
|
||||
ClimateEntityFeature.TURN_ON +
|
||||
ClimateEntityFeature.TURN_OFF +
|
||||
ClimateEntityFeature.SWING_MODE +
|
||||
ClimateEntityFeature.SWING_HORIZONTAL_MODE +
|
||||
ClimateEntityFeature.PRESET_MODE +
|
||||
ClimateEntityFeature.FAN_MODE +
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
}),
|
||||
getEntity("fan", "fan_demo", "on", {
|
||||
friendly_name: "Ceiling fan",
|
||||
device_class: "fan",
|
||||
direction: "reverse",
|
||||
supported_features:
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED +
|
||||
FanEntityFeature.OSCILLATE,
|
||||
}),
|
||||
{
|
||||
entity_id: "switch.tv_outlet",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "TV outlet",
|
||||
device_class: "outlet",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable entity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.front_door",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Front Door Lock",
|
||||
device_class: "lock",
|
||||
supported_features: LockEntityFeature.OPEN,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.living_room",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Living room speaker",
|
||||
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.thermostat",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: 80,
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
friendly_name: "Thermostat",
|
||||
hvac_action: "heating",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "vacuum.first_floor_vacuum",
|
||||
state: "docked",
|
||||
attributes: {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.kitchen_shutter",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen shutter",
|
||||
device_class: "shutter",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.pergola_roof",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Pergola Roof",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.counter",
|
||||
state: "1.0",
|
||||
attributes: {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.dual_thermostat",
|
||||
state: "heat/cool",
|
||||
attributes: {
|
||||
friendly_name: "Dual thermostat",
|
||||
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
|
||||
preset_modes: ["home", "eco", "away"],
|
||||
swing_modes: ["auto", "1", "2", "3", "off"],
|
||||
switch_horizontal_modes: ["auto", "4", "5", "6", "off"],
|
||||
current_temperature: 23,
|
||||
target_temp_high: 24,
|
||||
target_temp_low: 21,
|
||||
fan_mode: "auto_low",
|
||||
preset_mode: "home",
|
||||
swing_mode: "auto",
|
||||
swing_horizontal_mode: "off",
|
||||
supported_features:
|
||||
ClimateEntityFeature.TURN_ON +
|
||||
ClimateEntityFeature.TURN_OFF +
|
||||
ClimateEntityFeature.SWING_MODE +
|
||||
ClimateEntityFeature.SWING_HORIZONTAL_MODE +
|
||||
ClimateEntityFeature.PRESET_MODE +
|
||||
ClimateEntityFeature.FAN_MODE +
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.fan_demo",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling fan",
|
||||
device_class: "fan",
|
||||
direction: "reverse",
|
||||
supported_features:
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED +
|
||||
FanEntityFeature.OSCILLATE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -3,18 +3,25 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
import { mockTodo } from "../../../../demo/src/stubs/todo";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("todo", "shopping_list", "2", {
|
||||
friendly_name: "Shopping List",
|
||||
supported_features: 15,
|
||||
}),
|
||||
getEntity("todo", "read_only", "2", {
|
||||
friendly_name: "Read only",
|
||||
}),
|
||||
{
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
friendly_name: "Shopping List",
|
||||
supported_features: 15,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "todo.read_only",
|
||||
state: "2",
|
||||
attributes: {
|
||||
friendly_name: "Read only",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -3,108 +3,135 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { ClimateEntityFeature } from "../../../../src/data/climate";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("climate", "radiator", "heat", {
|
||||
friendly_name: "Basic heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
}),
|
||||
getEntity("climate", "ac", "cool", {
|
||||
friendly_name: "Basic air conditioning",
|
||||
hvac_modes: ["cool", "off"],
|
||||
hvac_mode: "cool",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
}),
|
||||
getEntity("climate", "fan", "fan_only", {
|
||||
friendly_name: "Basic fan",
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
hvac_mode: "fan_only",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
|
||||
}),
|
||||
getEntity("climate", "hvac", "auto", {
|
||||
friendly_name: "Basic hvac",
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
}),
|
||||
getEntity("climate", "advanced", "auto", {
|
||||
friendly_name: "Advanced hvac",
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE |
|
||||
ClimateEntityFeature.TARGET_HUMIDITY |
|
||||
ClimateEntityFeature.PRESET_MODE,
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
preset_modes: ["eco", "comfort", "boost"],
|
||||
preset_mode: "eco",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
current_humidity: 40,
|
||||
min_humidity: 0,
|
||||
max_humidity: 100,
|
||||
humidity: 50,
|
||||
}),
|
||||
getEntity("climate", "towel_dryer", "heat", {
|
||||
friendly_name: "Preset only heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
preset_mode: "eco",
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
supported_features: ClimateEntityFeature.PRESET_MODE,
|
||||
}),
|
||||
getEntity("climate", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
}),
|
||||
{
|
||||
entity_id: "climate.radiator",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
friendly_name: "Basic heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.ac",
|
||||
state: "cool",
|
||||
attributes: {
|
||||
friendly_name: "Basic air conditioning",
|
||||
hvac_modes: ["cool", "off"],
|
||||
hvac_mode: "cool",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.fan",
|
||||
state: "fan_only",
|
||||
attributes: {
|
||||
friendly_name: "Basic fan",
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
hvac_mode: "fan_only",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.hvac",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
friendly_name: "Basic hvac",
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.advanced",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
friendly_name: "Advanced hvac",
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE |
|
||||
ClimateEntityFeature.TARGET_HUMIDITY |
|
||||
ClimateEntityFeature.PRESET_MODE,
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
preset_modes: ["eco", "comfort", "boost"],
|
||||
preset_mode: "eco",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
current_humidity: 40,
|
||||
min_humidity: 0,
|
||||
max_humidity: 100,
|
||||
humidity: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.towel_dryer",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
friendly_name: "Preset only heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
preset_mode: "eco",
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
supported_features: ClimateEntityFeature.PRESET_MODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-climate")
|
||||
@@ -117,7 +144,7 @@ class DemoMoreInfoClimate extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,138 +4,189 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { CoverEntityFeature } from "../../../../src/data/cover";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("cover", "position_buttons", "on", {
|
||||
friendly_name: "Position Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE,
|
||||
}),
|
||||
getEntity("cover", "position_slider_half", "on", {
|
||||
friendly_name: "Position Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 50,
|
||||
}),
|
||||
getEntity("cover", "position_slider_open", "on", {
|
||||
friendly_name: "Position Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 100,
|
||||
}),
|
||||
getEntity("cover", "position_slider_closed", "on", {
|
||||
friendly_name: "Position Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 0,
|
||||
}),
|
||||
getEntity("cover", "tilt_buttons", "on", {
|
||||
friendly_name: "Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
}),
|
||||
getEntity("cover", "tilt_slider_half", "on", {
|
||||
friendly_name: "Tilt Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 50,
|
||||
}),
|
||||
getEntity("cover", "tilt_slider_open", "on", {
|
||||
friendly_name: "Tilt Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 100,
|
||||
}),
|
||||
getEntity("cover", "tilt_slider_closed", "on", {
|
||||
friendly_name: "Tilt Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 0,
|
||||
}),
|
||||
getEntity("cover", "position_slider_tilt_slider", "on", {
|
||||
friendly_name: "Both Sliders",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
}),
|
||||
getEntity("cover", "position_tilt_slider", "on", {
|
||||
friendly_name: "Position & Tilt Slider",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 70,
|
||||
}),
|
||||
getEntity("cover", "position_slider_tilt", "on", {
|
||||
friendly_name: "Position Slider & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
}),
|
||||
getEntity("cover", "position_slider_only_tilt_slider", "on", {
|
||||
friendly_name: "Position Slider Only & Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
}),
|
||||
getEntity("cover", "position_slider_only_tilt", "on", {
|
||||
friendly_name: "Position Slider Only & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
}),
|
||||
{
|
||||
entity_id: "cover.position_buttons",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_half",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_open",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_closed",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_buttons",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_slider_half",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_slider_open",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_slider_closed",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_tilt_slider",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Both Sliders",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_tilt_slider",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position & Tilt Slider",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 70,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_tilt",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Slider & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_only_tilt_slider",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Slider Only & Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_only_tilt",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Slider Only & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-cover")
|
||||
@@ -148,7 +199,7 @@ class DemoMoreInfoCover extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,21 +3,24 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { FanEntityFeature } from "../../../../src/data/fan";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("fan", "fan", "on", {
|
||||
friendly_name: "Fan",
|
||||
device_class: "fan",
|
||||
supported_features:
|
||||
FanEntityFeature.OSCILLATE +
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED,
|
||||
}),
|
||||
{
|
||||
entity_id: "fan.fan",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Fan",
|
||||
device_class: "fan",
|
||||
supported_features:
|
||||
FanEntityFeature.OSCILLATE +
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-fan")
|
||||
@@ -30,7 +33,7 @@ class DemoMoreInfoFan extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,27 +3,38 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("humidifier", "humidifier", "on", {
|
||||
friendly_name: "Humidifier",
|
||||
device_class: "humidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
}),
|
||||
getEntity("humidifier", "dehumidifier", "on", {
|
||||
friendly_name: "Dehumidifier",
|
||||
device_class: "dehumidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
}),
|
||||
getEntity("humidifier", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable humidifier",
|
||||
}),
|
||||
{
|
||||
entity_id: "humidifier.humidifier",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Humidifier",
|
||||
device_class: "humidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "humidifier.dehumidifier",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Dehumidifier",
|
||||
device_class: "dehumidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "humidifier.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable humidifier",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-humidifier")
|
||||
@@ -36,7 +47,7 @@ class DemoMoreInfoHumidifier extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,30 +3,37 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("input_number", "box1", 0, {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("input_number", "slider1", 0, {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
{
|
||||
entity_id: "input_number.box1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.slider1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-input-number")
|
||||
@@ -39,7 +46,7 @@ class DemoMoreInfoInputNumber extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("input_text", "text", "Inspiration", {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
}),
|
||||
{
|
||||
entity_id: "input_text.text",
|
||||
state: "Inspiration",
|
||||
attributes: {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-input-text")
|
||||
@@ -25,7 +28,7 @@ class DemoMoreInfoInputText extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,137 +4,172 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { LightColorMode, LightEntityFeature } from "../../../../src/data/light";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Basic Light",
|
||||
}),
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS],
|
||||
color_mode: LightColorMode.BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
brightness: 128,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_hs_light", "on", {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.HS,
|
||||
],
|
||||
color_mode: LightColorMode.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgb_ct_light", "on", {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGB,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_RGB_light", "on", {
|
||||
friendly_name: "Color Effects Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
|
||||
color_mode: LightColorMode.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbw_light", "on", {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbww_light", "on", {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBWW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_xy_light", "on", {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.XY,
|
||||
],
|
||||
color_mode: LightColorMode.XY,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Basic Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS],
|
||||
color_mode: LightColorMode.BRIGHTNESS,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_temperature_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
brightness: 128,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_hs_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.HS,
|
||||
],
|
||||
color_mode: LightColorMode.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_rgb_ct_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGB,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_RGB_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color Effects Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
|
||||
color_mode: LightColorMode.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_rgbw_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_rgbww_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBWW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_xy_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.XY,
|
||||
],
|
||||
color_mode: LightColorMode.XY,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-light")
|
||||
@@ -147,7 +182,7 @@ class DemoMoreInfoLight extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,26 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("lock", "lock", "locked", {
|
||||
friendly_name: "Lock",
|
||||
device_class: "lock",
|
||||
}),
|
||||
getEntity("lock", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable lock",
|
||||
}),
|
||||
{
|
||||
entity_id: "lock.lock",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Lock",
|
||||
device_class: "lock",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable lock",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-lock")
|
||||
@@ -28,7 +35,7 @@ class DemoMoreInfoLock extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class DemoMoreInfoMediaPlayer extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,48 +3,63 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("number", "box1", 0, {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "slider1", 0, {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "auto1", 0, {
|
||||
friendly_name: "Auto1",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "auto2", 0, {
|
||||
friendly_name: "Auto2",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
{
|
||||
entity_id: "number.box1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.slider1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.auto1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Auto1",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.auto2",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Auto2",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-number")
|
||||
@@ -57,7 +72,7 @@ class DemoMoreInfoNumber extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,26 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("scene", "romantic_lights", "scening", {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("scene", "unavailable", "unavailable", {
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
{
|
||||
entity_id: "scene.romantic_lights",
|
||||
state: "scening",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "scene.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-scene")
|
||||
@@ -28,7 +35,7 @@ class DemoMoreInfoScene extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("timer", "timer", "idle", {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
}),
|
||||
{
|
||||
entity_id: "timer.timer",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-timer")
|
||||
@@ -25,7 +28,7 @@ class DemoMoreInfoTimer extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
@@ -23,124 +22,208 @@ const base_attributes = {
|
||||
};
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("update", "update1", "on", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update",
|
||||
}),
|
||||
getEntity("update", "update2", "on", {
|
||||
...base_attributes,
|
||||
title: null,
|
||||
friendly_name: "Update without title",
|
||||
}),
|
||||
getEntity("update", "update3", "on", {
|
||||
...base_attributes,
|
||||
release_url: null,
|
||||
friendly_name: "Update without release_url",
|
||||
}),
|
||||
getEntity("update", "update4", "on", {
|
||||
...base_attributes,
|
||||
release_summary: null,
|
||||
friendly_name: "Update without release_summary",
|
||||
}),
|
||||
getEntity("update", "update5", "off", {
|
||||
...base_attributes,
|
||||
installed_version: "1.2.3",
|
||||
friendly_name: "No update",
|
||||
}),
|
||||
getEntity("update", "update6", "off", {
|
||||
...base_attributes,
|
||||
skipped_version: "1.2.3",
|
||||
friendly_name: "Skipped version",
|
||||
}),
|
||||
getEntity("update", "update7", "on", {
|
||||
...base_attributes,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
|
||||
friendly_name: "With backup support",
|
||||
}),
|
||||
getEntity("update", "update8", "on", {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name: "With true in_progress",
|
||||
}),
|
||||
getEntity("update", "update9", "on", {
|
||||
...base_attributes,
|
||||
in_progress: 25,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 25 in_progress",
|
||||
}),
|
||||
getEntity("update", "update10", "on", {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 50 in_progress",
|
||||
}),
|
||||
getEntity("update", "update11", "on", {
|
||||
...base_attributes,
|
||||
in_progress: 75,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 75 in_progress",
|
||||
}),
|
||||
getEntity("update", "update12", "unavailable", {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
friendly_name: "Unavailable",
|
||||
}),
|
||||
getEntity("update", "update13", "on", {
|
||||
...base_attributes,
|
||||
supported_features: 0,
|
||||
friendly_name: "No install support",
|
||||
}),
|
||||
getEntity("update", "update14", "off", {
|
||||
...base_attributes,
|
||||
installed_version: null,
|
||||
friendly_name: "Update without installed_version",
|
||||
}),
|
||||
getEntity("update", "update15", "off", {
|
||||
...base_attributes,
|
||||
latest_version: null,
|
||||
friendly_name: "Update without latest_version",
|
||||
}),
|
||||
getEntity("update", "update16", "off", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
}),
|
||||
getEntity("update", "update17", "off", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes error",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
}),
|
||||
getEntity("update", "update18", "off", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes loading",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
}),
|
||||
getEntity("update", "update19", "on", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with auto update",
|
||||
auto_update: true,
|
||||
}),
|
||||
getEntity("update", "update20", "on", {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
title: undefined,
|
||||
friendly_name: "Installing without title",
|
||||
}),
|
||||
getEntity("update", "update21", "on", {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name:
|
||||
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
}),
|
||||
{
|
||||
entity_id: "update.update1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update2",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
title: null,
|
||||
friendly_name: "Update without title",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update3",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
release_url: null,
|
||||
friendly_name: "Update without release_url",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update4",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
release_summary: null,
|
||||
friendly_name: "Update without release_summary",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update5",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
installed_version: "1.2.3",
|
||||
friendly_name: "No update",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update6",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
skipped_version: "1.2.3",
|
||||
friendly_name: "Skipped version",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update7",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
|
||||
friendly_name: "With backup support",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update8",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name: "With true in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update9",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 25,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 25 in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update10",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 50 in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update11",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 75,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 75 in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update12",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
friendly_name: "Unavailable",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update13",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
supported_features: 0,
|
||||
friendly_name: "No install support",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update14",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
installed_version: null,
|
||||
friendly_name: "Update without installed_version",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update15",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
latest_version: null,
|
||||
friendly_name: "Update without latest_version",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update16",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update17",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes error",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update18",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes loading",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update19",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with auto update",
|
||||
auto_update: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update20",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
title: undefined,
|
||||
friendly_name: "Installing without title",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update21",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name:
|
||||
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-update")
|
||||
@@ -153,7 +236,7 @@ class DemoMoreInfoUpdate extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,20 +3,23 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
}),
|
||||
{
|
||||
entity_id: "vacuum.first_floor_vacuum",
|
||||
state: "docked",
|
||||
attributes: {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-vacuum")
|
||||
@@ -29,7 +32,7 @@ class DemoMoreInfoVacuum extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,39 +4,46 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { WaterHeaterEntityFeature } from "../../../../src/data/water_heater";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("water_heater", "basic", "eco", {
|
||||
friendly_name: "Basic heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "eco",
|
||||
away_mode: "off",
|
||||
target_temp_step: 1,
|
||||
current_temperature: 55,
|
||||
temperature: 60,
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE |
|
||||
WaterHeaterEntityFeature.AWAY_MODE,
|
||||
}),
|
||||
getEntity("water_heater", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "off",
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE,
|
||||
}),
|
||||
{
|
||||
entity_id: "water_heater.basic",
|
||||
state: "eco",
|
||||
attributes: {
|
||||
friendly_name: "Basic heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "eco",
|
||||
away_mode: "off",
|
||||
target_temp_step: 1,
|
||||
current_temperature: 55,
|
||||
temperature: 60,
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE |
|
||||
WaterHeaterEntityFeature.AWAY_MODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "water_heater.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "off",
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-water-heater")
|
||||
@@ -49,7 +56,7 @@ class DemoMoreInfoWaterHeater extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class HaLandingPage extends LandingPageBaseElement {
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
makeDialogManager(this, this.shadowRoot!);
|
||||
makeDialogManager(this);
|
||||
|
||||
if (window.innerWidth > 450) {
|
||||
import("../../src/resources/particles");
|
||||
|
||||
111
package.json
111
package.json
@@ -26,33 +26,33 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.6",
|
||||
"@babel/runtime": "7.29.2",
|
||||
"@braintree/sanitize-url": "7.1.2",
|
||||
"@codemirror/autocomplete": "6.20.0",
|
||||
"@codemirror/commands": "6.10.2",
|
||||
"@codemirror/language": "6.12.1",
|
||||
"@codemirror/autocomplete": "6.20.1",
|
||||
"@codemirror/commands": "6.10.3",
|
||||
"@codemirror/language": "6.12.2",
|
||||
"@codemirror/legacy-modes": "6.5.2",
|
||||
"@codemirror/search": "6.6.0",
|
||||
"@codemirror/state": "6.5.4",
|
||||
"@codemirror/view": "6.39.12",
|
||||
"@codemirror/state": "6.6.0",
|
||||
"@codemirror/view": "6.40.0",
|
||||
"@date-fns/tz": "1.4.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "7.2.1",
|
||||
"@formatjs/intl-displaynames": "7.2.1",
|
||||
"@formatjs/intl-durationformat": "0.10.1",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.1",
|
||||
"@formatjs/intl-listformat": "8.2.1",
|
||||
"@formatjs/intl-locale": "5.2.1",
|
||||
"@formatjs/intl-numberformat": "9.2.2",
|
||||
"@formatjs/intl-pluralrules": "6.2.2",
|
||||
"@formatjs/intl-relativetimeformat": "12.2.2",
|
||||
"@formatjs/intl-datetimeformat": "7.3.1",
|
||||
"@formatjs/intl-displaynames": "7.3.1",
|
||||
"@formatjs/intl-durationformat": "0.10.3",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.2",
|
||||
"@formatjs/intl-listformat": "8.3.1",
|
||||
"@formatjs/intl-locale": "5.3.1",
|
||||
"@formatjs/intl-numberformat": "9.3.1",
|
||||
"@formatjs/intl-pluralrules": "6.3.1",
|
||||
"@formatjs/intl-relativetimeformat": "12.3.1",
|
||||
"@fullcalendar/core": "6.1.20",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"@fullcalendar/interaction": "6.1.20",
|
||||
"@fullcalendar/list": "6.1.20",
|
||||
"@fullcalendar/luxon3": "6.1.20",
|
||||
"@fullcalendar/timegrid": "6.1.20",
|
||||
"@home-assistant/webawesome": "3.2.1-ha.2",
|
||||
"@home-assistant/webawesome": "3.3.1-ha.0",
|
||||
"@lezer/highlight": "1.2.3",
|
||||
"@lit-labs/motion": "1.1.0",
|
||||
"@lit-labs/observers": "2.1.0",
|
||||
@@ -72,7 +72,6 @@
|
||||
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"@material/mwc-radio": "0.27.0",
|
||||
"@material/mwc-select": "0.27.0",
|
||||
"@material/mwc-snackbar": "0.27.0",
|
||||
"@material/mwc-switch": "0.27.0",
|
||||
"@material/mwc-textarea": "0.27.0",
|
||||
"@material/mwc-textfield": "0.27.0",
|
||||
@@ -83,19 +82,18 @@
|
||||
"@mdi/js": "7.4.47",
|
||||
"@mdi/svg": "7.4.47",
|
||||
"@replit/codemirror-indentation-markers": "6.5.3",
|
||||
"@swc/helpers": "0.5.18",
|
||||
"@swc/helpers": "0.5.19",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@tsparticles/engine": "3.9.1",
|
||||
"@tsparticles/preset-links": "3.2.0",
|
||||
"@vibrant/color": "4.0.4",
|
||||
"@vue/web-component-wrapper": "1.3.0",
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"app-datepicker": "5.1.1",
|
||||
"barcode-detector": "3.0.8",
|
||||
"barcode-detector": "3.1.1",
|
||||
"cally": "0.9.2",
|
||||
"color-name": "2.1.0",
|
||||
"comlink": "4.4.2",
|
||||
"core-js": "3.48.0",
|
||||
"core-js": "3.49.0",
|
||||
"cropperjs": "1.6.2",
|
||||
"culori": "4.0.2",
|
||||
"date-fns": "4.1.0",
|
||||
@@ -106,11 +104,11 @@
|
||||
"element-internals-polyfill": "3.0.2",
|
||||
"fuse.js": "7.1.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"gulp-zopfli-green": "6.0.2",
|
||||
"gulp-zopfli-green": "7.0.0",
|
||||
"hls.js": "1.6.15",
|
||||
"home-assistant-js-websocket": "9.6.0",
|
||||
"idb-keyval": "6.2.2",
|
||||
"intl-messageformat": "11.1.2",
|
||||
"intl-messageformat": "11.2.0",
|
||||
"js-yaml": "4.1.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
|
||||
@@ -118,7 +116,7 @@
|
||||
"lit": "3.3.2",
|
||||
"lit-html": "3.3.2",
|
||||
"luxon": "3.7.2",
|
||||
"marked": "17.0.1",
|
||||
"marked": "17.0.5",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "4.0.4",
|
||||
"object-hash": "3.0.0",
|
||||
@@ -131,9 +129,6 @@
|
||||
"stacktrace-js": "2.0.2",
|
||||
"superstruct": "2.0.2",
|
||||
"tinykeys": "3.0.0",
|
||||
"ua-parser-js": "2.0.9",
|
||||
"vue": "2.7.16",
|
||||
"vue2-daterange-picker": "0.6.8",
|
||||
"weekstart": "2.0.0",
|
||||
"workbox-cacheable-response": "7.4.0",
|
||||
"workbox-core": "7.4.0",
|
||||
@@ -145,17 +140,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.29.0",
|
||||
"@babel/helper-define-polyfill-provider": "0.6.6",
|
||||
"@babel/helper-define-polyfill-provider": "0.6.8",
|
||||
"@babel/plugin-transform-runtime": "7.29.0",
|
||||
"@babel/preset-env": "7.29.0",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.21.9",
|
||||
"@html-eslint/eslint-plugin": "0.55.0",
|
||||
"@babel/preset-env": "7.29.2",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.22.0",
|
||||
"@html-eslint/eslint-plugin": "0.58.1",
|
||||
"@lokalise/node-api": "15.6.1",
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
"@octokit/rest": "22.0.1",
|
||||
"@rsdoctor/rspack-plugin": "1.5.2",
|
||||
"@rspack/core": "1.7.5",
|
||||
"@rsdoctor/rspack-plugin": "1.5.5",
|
||||
"@rspack/core": "1.7.9",
|
||||
"@rspack/dev-server": "1.2.1",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.25",
|
||||
@@ -172,35 +167,34 @@
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/qrcode": "1.5.6",
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@types/tar": "6.1.13",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/tar": "7.0.87",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@vitest/coverage-v8": "4.0.18",
|
||||
"babel-loader": "10.0.0",
|
||||
"@vitest/coverage-v8": "4.1.0",
|
||||
"babel-loader": "10.1.1",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.3",
|
||||
"del": "8.0.1",
|
||||
"eslint": "9.39.2",
|
||||
"eslint": "9.39.4",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-import-resolver-webpack": "0.13.10",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-lit": "2.1.1",
|
||||
"eslint-plugin-lit": "2.2.1",
|
||||
"eslint-plugin-lit-a11y": "5.1.1",
|
||||
"eslint-plugin-unused-imports": "4.3.0",
|
||||
"eslint-plugin-wc": "3.0.2",
|
||||
"eslint-plugin-unused-imports": "4.4.1",
|
||||
"eslint-plugin-wc": "3.1.0",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.3.3",
|
||||
"glob": "13.0.1",
|
||||
"fs-extra": "11.3.4",
|
||||
"glob": "13.0.6",
|
||||
"gulp": "5.0.1",
|
||||
"gulp-brotli": "3.0.0",
|
||||
"gulp-json-transform": "0.5.0",
|
||||
"gulp-rename": "2.1.0",
|
||||
"html-minifier-terser": "7.2.0",
|
||||
"husky": "9.1.7",
|
||||
"jsdom": "28.0.0",
|
||||
"jsdom": "29.0.1",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "16.2.7",
|
||||
"lint-staged": "16.4.0",
|
||||
"lit-analyzer": "2.0.3",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.template": "4.5.0",
|
||||
@@ -208,33 +202,32 @@
|
||||
"pinst": "3.0.0",
|
||||
"prettier": "3.8.1",
|
||||
"rspack-manifest-plugin": "5.2.1",
|
||||
"serve": "14.2.5",
|
||||
"sinon": "21.0.1",
|
||||
"tar": "7.5.7",
|
||||
"terser-webpack-plugin": "5.3.16",
|
||||
"serve": "14.2.6",
|
||||
"sinon": "21.0.3",
|
||||
"tar": "7.5.12",
|
||||
"terser-webpack-plugin": "5.4.0",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.54.0",
|
||||
"vite-tsconfig-paths": "6.0.5",
|
||||
"vitest": "4.0.18",
|
||||
"typescript-eslint": "8.57.1",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.0",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.4.0#~/.yarn/patches/workbox-build-npm-7.4.0-c84561662c.patch"
|
||||
},
|
||||
"resolutions": {
|
||||
"@material/mwc-button@^0.25.3": "^0.27.0",
|
||||
"lit": "3.3.2",
|
||||
"lit-html": "3.3.2",
|
||||
"clean-css": "5.3.3",
|
||||
"@lit/reactive-element": "2.1.2",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"globals": "17.3.0",
|
||||
"globals": "17.4.0",
|
||||
"tslib": "2.8.1",
|
||||
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"glob@^10.2.2": "^10.5.0"
|
||||
},
|
||||
"packageManager": "yarn@4.12.0",
|
||||
"packageManager": "yarn@4.13.0",
|
||||
"volta": {
|
||||
"node": "24.13.1"
|
||||
"node": "24.14.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20260128.0"
|
||||
version = "20260325.6"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
"extends": ["monorepo:material-components-web"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"description": "Vue is only used by date range which is only v2",
|
||||
"matchPackageNames": ["vue"],
|
||||
"allowedVersions": "< 3"
|
||||
},
|
||||
{
|
||||
"description": "Group MDI packages",
|
||||
"groupName": "Material Design Icons",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { genClientId } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { keyed } from "lit/directives/keyed";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import "../components/ha-alert";
|
||||
@@ -23,6 +23,7 @@ import type {
|
||||
DataEntryFlowStepForm,
|
||||
} from "../data/data_entry_flow";
|
||||
import "./ha-auth-form";
|
||||
import type { HaAuthForm } from "./ha-auth-form";
|
||||
|
||||
type State = "loading" | "error" | "step";
|
||||
|
||||
@@ -52,6 +53,8 @@ export class HaAuthFlow extends LitElement {
|
||||
|
||||
@state() private _submitting = false;
|
||||
|
||||
@query("ha-auth-form") private _form?: HaAuthForm;
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
@@ -179,7 +182,7 @@ export class HaAuthFlow extends LitElement {
|
||||
<div class="action">
|
||||
<ha-button
|
||||
@click=${this._handleSubmit}
|
||||
.disabled=${this._submitting}
|
||||
.loading=${this._submitting}
|
||||
>
|
||||
${this.step.type === "form"
|
||||
? this.localize("ui.panel.page-authorize.form.next")
|
||||
@@ -370,6 +373,11 @@ export class HaAuthFlow extends LitElement {
|
||||
this._providerChanged(this.authProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._form?.reportValidity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._submitting = true;
|
||||
|
||||
const postData = { ...this._stepData, client_id: this.clientId };
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { HaFormString } from "../components/ha-form/ha-form-string";
|
||||
import "../components/ha-icon-button";
|
||||
import "./ha-auth-textfield";
|
||||
import "../components/input/ha-input";
|
||||
|
||||
@customElement("ha-auth-form-string")
|
||||
export class HaAuthFormString extends HaFormString {
|
||||
@@ -12,59 +9,9 @@ export class HaAuthFormString extends HaFormString {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
ha-auth-form-string {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
ha-auth-form-string[own-margin] {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
ha-auth-form-string ha-auth-textfield {
|
||||
display: block !important;
|
||||
}
|
||||
ha-auth-form-string ha-icon-button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 8px;
|
||||
--ha-icon-button-size: 40px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
direction: var(--direction);
|
||||
}
|
||||
</style>
|
||||
<ha-auth-textfield
|
||||
.type=${!this.isPassword
|
||||
? this.stringType
|
||||
: this.unmaskedPassword
|
||||
? "text"
|
||||
: "password"}
|
||||
.label=${this.label}
|
||||
.value=${this.data || ""}
|
||||
.helper=${this.helper}
|
||||
helperPersistent
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.schema.required}
|
||||
.autoValidate=${this.schema.required}
|
||||
.name=${this.schema.name}
|
||||
.autocomplete=${this.schema.autocomplete}
|
||||
?autofocus=${this.schema.autofocus}
|
||||
.suffix=${this.isPassword
|
||||
? // reserve some space for the icon.
|
||||
html`<div style="width: 24px"></div>`
|
||||
: this.schema.description?.suffix}
|
||||
.validationMessage=${this.schema.required
|
||||
? this.localize?.("ui.panel.page-authorize.form.error_required")
|
||||
: undefined}
|
||||
@input=${this._valueChanged}
|
||||
@change=${this._valueChanged}
|
||||
></ha-auth-textfield>
|
||||
${this.renderIcon()}
|
||||
`;
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.style.position = "relative";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { HaForm } from "../components/ha-form/ha-form";
|
||||
import "./ha-auth-form-string";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
const localizeBaseKey = "ui.panel.page-authorize.form";
|
||||
|
||||
@@ -34,6 +34,9 @@ export class HaAuthForm extends HaForm {
|
||||
protected render() {
|
||||
return html`
|
||||
<style>
|
||||
ha-auth-form {
|
||||
--ha-input-required-marker: "";
|
||||
}
|
||||
ha-auth-form .root > * {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
/* eslint-disable lit/value-after-constraints */
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import { floatingLabel } from "@material/mwc-floating-label/mwc-floating-label-directive";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { live } from "lit/directives/live";
|
||||
import { HaTextField } from "../components/ha-textfield";
|
||||
|
||||
@customElement("ha-auth-textfield")
|
||||
export class HaAuthTextField extends HaTextField {
|
||||
protected renderLabel(): TemplateResult | string {
|
||||
return !this.label
|
||||
? ""
|
||||
: html`
|
||||
<span
|
||||
.floatingLabelFoundation=${floatingLabel(
|
||||
this.label
|
||||
) as unknown as any}
|
||||
.id=${this.name}
|
||||
>${this.label}</span
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
protected renderInput(shouldRenderHelperText: boolean): TemplateResult {
|
||||
const minOrUndef = this.minLength === -1 ? undefined : this.minLength;
|
||||
const maxOrUndef = this.maxLength === -1 ? undefined : this.maxLength;
|
||||
const autocapitalizeOrUndef = this.autocapitalize
|
||||
? (this.autocapitalize as
|
||||
| "off"
|
||||
| "none"
|
||||
| "on"
|
||||
| "sentences"
|
||||
| "words"
|
||||
| "characters")
|
||||
: undefined;
|
||||
const showValidationMessage = this.validationMessage && !this.isUiValid;
|
||||
const ariaLabelledbyOrUndef = this.label ? this.name : undefined;
|
||||
const ariaControlsOrUndef = shouldRenderHelperText
|
||||
? "helper-text"
|
||||
: undefined;
|
||||
const ariaDescribedbyOrUndef =
|
||||
this.focused || this.helperPersistent || showValidationMessage
|
||||
? "helper-text"
|
||||
: undefined;
|
||||
// TODO: live() directive needs casting for lit-analyzer
|
||||
// https://github.com/runem/lit-analyzer/pull/91/files
|
||||
// TODO: lit-analyzer labels min/max as (number|string) instead of string
|
||||
return html`<input
|
||||
aria-labelledby=${ifDefined(ariaLabelledbyOrUndef)}
|
||||
aria-controls=${ifDefined(ariaControlsOrUndef)}
|
||||
aria-describedby=${ifDefined(ariaDescribedbyOrUndef)}
|
||||
class="mdc-text-field__input"
|
||||
type=${this.type}
|
||||
.value=${live(this.value) as unknown as string}
|
||||
?disabled=${this.disabled}
|
||||
placeholder=${this.placeholder}
|
||||
?required=${this.required}
|
||||
?readonly=${this.readOnly}
|
||||
minlength=${ifDefined(minOrUndef)}
|
||||
maxlength=${ifDefined(maxOrUndef)}
|
||||
pattern=${ifDefined(this.pattern ? this.pattern : undefined)}
|
||||
min=${ifDefined(this.min === "" ? undefined : (this.min as number))}
|
||||
max=${ifDefined(this.max === "" ? undefined : (this.max as number))}
|
||||
step=${ifDefined(this.step === null ? undefined : (this.step as number))}
|
||||
size=${ifDefined(this.size === null ? undefined : this.size)}
|
||||
name=${ifDefined(this.name === "" ? undefined : this.name)}
|
||||
inputmode=${ifDefined(this.inputMode)}
|
||||
autocapitalize=${ifDefined(autocapitalizeOrUndef)}
|
||||
?autofocus=${this.autofocus}
|
||||
@input=${this.handleInputChange}
|
||||
@focus=${this.onInputFocus}
|
||||
@blur=${this.onInputBlur}
|
||||
/>`;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<style>
|
||||
ha-auth-textfield {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
outline: none;
|
||||
}
|
||||
ha-auth-textfield:not([disabled]):hover
|
||||
:not(.mdc-text-field--invalid):not(.mdc-text-field--focused)
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-outlined-hover-border-color,
|
||||
rgba(0, 0, 0, 0.87)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field:not(.mdc-text-field--outlined) {
|
||||
background-color: var(--mdc-text-field-fill-color, whitesmoke);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--invalid
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-error-color,
|
||||
var(--mdc-theme-error, #b00020)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--invalid
|
||||
+ .mdc-text-field-helper-line
|
||||
.mdc-text-field-character-counter,
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--invalid
|
||||
.mdc-text-field__icon {
|
||||
color: var(
|
||||
--mdc-text-field-error-color,
|
||||
var(--mdc-theme-error, #b00020)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label,
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label::after {
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-stroke-width: 2px;
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-focused-label-color,
|
||||
var(--mdc-theme-primary, rgba(98, 0, 238, 0.87))
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||
.mdc-floating-label {
|
||||
color: #6200ee;
|
||||
color: var(--mdc-theme-primary, #6200ee);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field
|
||||
.mdc-text-field__input {
|
||||
color: var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87));
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field
|
||||
.mdc-text-field__input::placeholder {
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field-helper-line
|
||||
.mdc-text-field-helper-text:not(
|
||||
.mdc-text-field-helper-text--validation-msg
|
||||
),
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field-helper-line:not(.mdc-text-field--invalid)
|
||||
.mdc-text-field-character-counter {
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field:not(.mdc-text-field--outlined) {
|
||||
background-color: var(--mdc-text-field-disabled-fill-color, #fafafa);
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field.mdc-text-field--outlined
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-outlined-disabled-border-color,
|
||||
rgba(0, 0, 0, 0.06)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label,
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label::after {
|
||||
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled] .mdc-text-field .mdc-text-field__input,
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field
|
||||
.mdc-text-field__input::placeholder {
|
||||
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field-helper-line
|
||||
.mdc-text-field-helper-text,
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field-helper-line
|
||||
.mdc-text-field-character-counter {
|
||||
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||
}
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||
.mdc-floating-label {
|
||||
color: var(--mdc-theme-primary, #6200ee);
|
||||
}
|
||||
ha-auth-textfield[no-spinner] input::-webkit-outer-spin-button,
|
||||
ha-auth-textfield[no-spinner] input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
ha-auth-textfield[no-spinner] input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
</style>
|
||||
${super.render()}
|
||||
`;
|
||||
}
|
||||
|
||||
protected createRenderRoot() {
|
||||
// add parent style to light dom
|
||||
const style = document.createElement("style");
|
||||
style.textContent = HaTextField.elementStyles as unknown as string;
|
||||
this.append(style);
|
||||
return this;
|
||||
}
|
||||
|
||||
public firstUpdated() {
|
||||
super.firstUpdated();
|
||||
|
||||
if (this.autofocus) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-auth-textfield": HaAuthTextField;
|
||||
}
|
||||
}
|
||||
@@ -58,9 +58,11 @@ export class CastManager {
|
||||
this._eventListeners[event].push(listener);
|
||||
|
||||
return () => {
|
||||
this._eventListeners[event].splice(
|
||||
this._eventListeners[event].indexOf(listener)
|
||||
);
|
||||
const listeners = this._eventListeners[event];
|
||||
const index = listeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,17 @@ const YAML_ONLY_THEMES_COLORS = new Set([
|
||||
"disabled",
|
||||
]);
|
||||
|
||||
export function computeCssColor(color: string): string {
|
||||
export function computeCssVariableName(color: string): string {
|
||||
if (THEME_COLORS.has(color) || YAML_ONLY_THEMES_COLORS.has(color)) {
|
||||
return `var(--${color}-color)`;
|
||||
return `--${color}-color`;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
export function computeCssColor(color: string): string {
|
||||
const cssVarName = computeCssVariableName(color);
|
||||
if (cssVarName !== color) {
|
||||
return `var(${cssVarName})`;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {
|
||||
addDays,
|
||||
subHours,
|
||||
endOfDay,
|
||||
endOfMonth,
|
||||
endOfQuarter,
|
||||
endOfWeek,
|
||||
endOfYear,
|
||||
startOfDay,
|
||||
startOfMonth,
|
||||
startOfQuarter,
|
||||
startOfWeek,
|
||||
startOfYear,
|
||||
startOfQuarter,
|
||||
endOfQuarter,
|
||||
subDays,
|
||||
subHours,
|
||||
subMonths,
|
||||
} from "date-fns";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
@@ -33,83 +33,89 @@ export type DateRange =
|
||||
| "now-24h";
|
||||
|
||||
export const calcDateRange = (
|
||||
hass: HomeAssistant,
|
||||
locale: HomeAssistant["locale"],
|
||||
hassConfig: HomeAssistant["config"],
|
||||
range: DateRange
|
||||
): [Date, Date] => {
|
||||
const today = new Date();
|
||||
const weekStartsOn = firstWeekdayIndex(hass.locale);
|
||||
const weekStartsOn = firstWeekdayIndex(locale);
|
||||
switch (range) {
|
||||
case "today":
|
||||
return [
|
||||
calcDate(today, startOfDay, hass.locale, hass.config, {
|
||||
calcDate(today, startOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
calcDate(today, endOfDay, hass.locale, hass.config, {
|
||||
calcDate(today, endOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
];
|
||||
case "yesterday":
|
||||
return [
|
||||
calcDate(addDays(today, -1), startOfDay, hass.locale, hass.config, {
|
||||
calcDate(addDays(today, -1), startOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
calcDate(addDays(today, -1), endOfDay, hass.locale, hass.config, {
|
||||
calcDate(addDays(today, -1), endOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
];
|
||||
case "this_week":
|
||||
return [
|
||||
calcDate(today, startOfWeek, hass.locale, hass.config, {
|
||||
calcDate(today, startOfWeek, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
calcDate(today, endOfWeek, hass.locale, hass.config, {
|
||||
calcDate(today, endOfWeek, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
];
|
||||
case "this_month":
|
||||
return [
|
||||
calcDate(today, startOfMonth, hass.locale, hass.config),
|
||||
calcDate(today, endOfMonth, hass.locale, hass.config),
|
||||
calcDate(today, startOfMonth, locale, hassConfig),
|
||||
calcDate(today, endOfMonth, locale, hassConfig),
|
||||
];
|
||||
case "this_quarter":
|
||||
return [
|
||||
calcDate(today, startOfQuarter, hass.locale, hass.config),
|
||||
calcDate(today, endOfQuarter, hass.locale, hass.config),
|
||||
calcDate(today, startOfQuarter, locale, hassConfig),
|
||||
calcDate(today, endOfQuarter, locale, hassConfig),
|
||||
];
|
||||
case "this_year":
|
||||
return [
|
||||
calcDate(today, startOfYear, hass.locale, hass.config),
|
||||
calcDate(today, endOfYear, hass.locale, hass.config),
|
||||
calcDate(today, startOfYear, locale, hassConfig),
|
||||
calcDate(today, endOfYear, locale, hassConfig),
|
||||
];
|
||||
case "now-7d":
|
||||
return [
|
||||
calcDate(today, subDays, hass.locale, hass.config, 7),
|
||||
calcDate(today, subDays, hass.locale, hass.config, 0),
|
||||
calcDate(today, subDays, locale, hassConfig, 7),
|
||||
calcDate(today, subDays, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-30d":
|
||||
return [
|
||||
calcDate(today, subDays, hass.locale, hass.config, 30),
|
||||
calcDate(today, subDays, hass.locale, hass.config, 0),
|
||||
calcDate(today, subDays, locale, hassConfig, 30),
|
||||
calcDate(today, subDays, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-12m":
|
||||
return [
|
||||
calcDate(today, subMonths, hass.locale, hass.config, 12),
|
||||
calcDate(today, subMonths, hass.locale, hass.config, 0),
|
||||
calcDate(
|
||||
today,
|
||||
(date) => subMonths(startOfMonth(date), 11),
|
||||
locale,
|
||||
hassConfig
|
||||
),
|
||||
calcDate(today, endOfMonth, locale, hassConfig),
|
||||
];
|
||||
case "now-1h":
|
||||
return [
|
||||
calcDate(today, subHours, hass.locale, hass.config, 1),
|
||||
calcDate(today, subHours, hass.locale, hass.config, 0),
|
||||
calcDate(today, subHours, locale, hassConfig, 1),
|
||||
calcDate(today, subHours, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-12h":
|
||||
return [
|
||||
calcDate(today, subHours, hass.locale, hass.config, 12),
|
||||
calcDate(today, subHours, hass.locale, hass.config, 0),
|
||||
calcDate(today, subHours, locale, hassConfig, 12),
|
||||
calcDate(today, subHours, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-24h":
|
||||
return [
|
||||
calcDate(today, subHours, hass.locale, hass.config, 24),
|
||||
calcDate(today, subHours, hass.locale, hass.config, 0),
|
||||
calcDate(today, subHours, locale, hassConfig, 24),
|
||||
calcDate(today, subHours, locale, hassConfig, 0),
|
||||
];
|
||||
}
|
||||
return [today, today];
|
||||
|
||||
@@ -166,6 +166,21 @@ const formatDateMonthMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
// Aug
|
||||
export const formatDateMonthShort = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => formatDateMonthShortMem(locale, config.time_zone).format(dateObj);
|
||||
|
||||
const formatDateMonthShortMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
month: "short",
|
||||
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
|
||||
})
|
||||
);
|
||||
|
||||
// 2021
|
||||
export const formatDateYear = (
|
||||
dateObj: Date,
|
||||
@@ -210,3 +225,72 @@ const formatDateWeekdayShortMem = memoizeOne(
|
||||
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
|
||||
})
|
||||
);
|
||||
|
||||
// Mon, Aug 10
|
||||
export const formatDateWeekdayVeryShortDate = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) =>
|
||||
formatDateWeekdayVeryShortDateMem(locale, config.time_zone).format(dateObj);
|
||||
|
||||
const formatDateWeekdayVeryShortDateMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
weekday: "short",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
|
||||
})
|
||||
);
|
||||
|
||||
// Mon, Aug 10, 2021
|
||||
export const formatDateWeekdayShortDate = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => formatDateWeekdayShortDateMem(locale, config.time_zone).format(dateObj);
|
||||
|
||||
const formatDateWeekdayShortDateMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
weekday: "short",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Format a date as YYYY-MM-DD. Uses "en-CA" because it's the only
|
||||
* Intl locale that natively outputs ISO 8601 date format.
|
||||
* Locale/config are only used to resolve the time zone.
|
||||
*/
|
||||
export const formatISODateOnly = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => {
|
||||
const timeZone = resolveTimeZone(locale.time_zone, config.time_zone);
|
||||
const formatter = new Intl.DateTimeFormat("en-CA", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
timeZone,
|
||||
});
|
||||
return formatter.format(dateObj);
|
||||
};
|
||||
|
||||
// 2026-08-10/2026-08-15
|
||||
export const formatCallyDateRange = (
|
||||
start: Date,
|
||||
end: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => {
|
||||
const startDate = formatISODateOnly(start, locale, config);
|
||||
const endDate = formatISODateOnly(end, locale, config);
|
||||
|
||||
return `${startDate}/${endDate}`;
|
||||
};
|
||||
|
||||
@@ -11,11 +11,11 @@ export const canOverrideAlphanumericInput = (composedPath: EventTarget[]) => {
|
||||
|
||||
const el = composedPath[0] as Element;
|
||||
|
||||
if (el.tagName === "TEXTAREA") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (el.parentElement?.tagName === "HA-SELECT") {
|
||||
if (
|
||||
el.tagName === "TEXTAREA" ||
|
||||
el.parentElement?.tagName === "HA-SELECT" ||
|
||||
el.parentElement?.tagName === "HA-DROPDOWN"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { computeAreaName } from "./compute_area_name";
|
||||
import { computeDeviceName } from "./compute_device_name";
|
||||
import { computeEntityName, entityUseDeviceName } from "./compute_entity_name";
|
||||
import { computeFloorName } from "./compute_floor_name";
|
||||
import { computeStateName } from "./compute_state_name";
|
||||
import { getEntityContext } from "./context/get_entity_context";
|
||||
|
||||
const DEFAULT_SEPARATOR = " ";
|
||||
@@ -29,14 +30,23 @@ export interface EntityNameOptions {
|
||||
|
||||
export const computeEntityNameDisplay = (
|
||||
stateObj: HassEntity,
|
||||
name: EntityNameItem | EntityNameItem[] | undefined,
|
||||
name: string | EntityNameItem | EntityNameItem[] | undefined,
|
||||
entities: HomeAssistant["entities"],
|
||||
devices: HomeAssistant["devices"],
|
||||
areas: HomeAssistant["areas"],
|
||||
floors: HomeAssistant["floors"],
|
||||
options?: EntityNameOptions
|
||||
) => {
|
||||
let items = ensureArray(name || DEFAULT_ENTITY_NAME);
|
||||
if (typeof name === "string") {
|
||||
return name;
|
||||
}
|
||||
|
||||
// If no name config is provided, fall back to the friendly name
|
||||
if (!name) {
|
||||
return computeStateName(stateObj);
|
||||
}
|
||||
|
||||
let items = ensureArray(name);
|
||||
|
||||
const separator = options?.separator ?? DEFAULT_SEPARATOR;
|
||||
|
||||
|
||||
@@ -133,33 +133,36 @@ const computeStateToPartsFromEntityAttributes = (
|
||||
),
|
||||
});
|
||||
} catch (_err) {
|
||||
// fallback to default
|
||||
// fallback to default numeric formatting below
|
||||
}
|
||||
|
||||
const TYPE_MAP: Record<string, ValuePart["type"]> = {
|
||||
integer: "value",
|
||||
group: "value",
|
||||
decimal: "value",
|
||||
fraction: "value",
|
||||
literal: "literal",
|
||||
currency: "unit",
|
||||
};
|
||||
if (parts.length) {
|
||||
const TYPE_MAP: Record<string, ValuePart["type"]> = {
|
||||
integer: "value",
|
||||
group: "value",
|
||||
decimal: "value",
|
||||
fraction: "value",
|
||||
minusSign: "value",
|
||||
plusSign: "value",
|
||||
literal: "literal",
|
||||
currency: "unit",
|
||||
};
|
||||
|
||||
const valueParts: ValuePart[] = [];
|
||||
const valueParts: ValuePart[] = [];
|
||||
|
||||
for (const part of parts) {
|
||||
const type = TYPE_MAP[part.type];
|
||||
if (!type) continue;
|
||||
const last = valueParts[valueParts.length - 1];
|
||||
// Merge consecutive numeric parts (e.g. "1" + "," + "234" + "." + "56" → "1,234.56")
|
||||
if (type === "value" && last?.type === "value") {
|
||||
last.value += part.value;
|
||||
} else {
|
||||
valueParts.push({ type, value: part.value });
|
||||
for (const part of parts) {
|
||||
const type = TYPE_MAP[part.type];
|
||||
if (!type) continue;
|
||||
const last = valueParts[valueParts.length - 1];
|
||||
// Merge consecutive value parts (e.g. "-" + "12" + "." + "00" → "-12.00")
|
||||
if (type === "value" && last?.type === "value") {
|
||||
last.value += part.value;
|
||||
} else {
|
||||
valueParts.push({ type, value: part.value });
|
||||
}
|
||||
}
|
||||
return valueParts;
|
||||
}
|
||||
|
||||
return valueParts;
|
||||
}
|
||||
|
||||
// default processing of numeric values
|
||||
@@ -252,6 +255,7 @@ const computeStateToPartsFromEntityAttributes = (
|
||||
"conversation",
|
||||
"event",
|
||||
"image",
|
||||
"infrared",
|
||||
"input_button",
|
||||
"notify",
|
||||
"scene",
|
||||
|
||||
@@ -29,6 +29,7 @@ export const FIXED_DOMAIN_STATES = {
|
||||
device_tracker: ["home", "not_home"],
|
||||
fan: ["on", "off"],
|
||||
humidifier: ["on", "off"],
|
||||
infrared: [],
|
||||
input_boolean: ["on", "off"],
|
||||
input_button: [],
|
||||
lawn_mower: ["error", "paused", "mowing", "returning", "docked"],
|
||||
@@ -270,6 +271,8 @@ export const getStates = (
|
||||
result.push(...state.attributes.preset_modes);
|
||||
} else if (attribute === "swing_mode") {
|
||||
result.push(...state.attributes.swing_modes);
|
||||
} else if (attribute === "swing_horizontal_mode") {
|
||||
result.push(...state.attributes.swing_horizontal_modes);
|
||||
}
|
||||
break;
|
||||
case "device_tracker":
|
||||
|
||||
@@ -6,7 +6,9 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
const compareState = state !== undefined ? state : stateObj?.state;
|
||||
|
||||
if (["button", "event", "input_button", "scene"].includes(domain)) {
|
||||
if (
|
||||
["button", "event", "infrared", "input_button", "scene"].includes(domain)
|
||||
) {
|
||||
return compareState !== UNAVAILABLE;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Indicates whether the current browser has native ElementInternals support.
|
||||
*/
|
||||
export const nativeElementInternalsSupported =
|
||||
Boolean(globalThis.ElementInternals) &&
|
||||
globalThis.HTMLElement?.prototype.attachInternals
|
||||
?.toString()
|
||||
.includes("[native code]");
|
||||
11
src/common/feature-detect/support-popover.ts
Normal file
11
src/common/feature-detect/support-popover.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Indicates whether the current browser supports the Popover API.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
|
||||
*/
|
||||
export const popoverSupported = globalThis?.HTMLElement?.prototype
|
||||
? Object.prototype.hasOwnProperty.call(
|
||||
globalThis.HTMLElement.prototype,
|
||||
"popover"
|
||||
)
|
||||
: false;
|
||||
@@ -1,3 +1,5 @@
|
||||
import { deepActiveElement } from "../dom/deep-active-element";
|
||||
|
||||
export const copyToClipboard = async (str, rootEl?: HTMLElement) => {
|
||||
if (navigator.clipboard) {
|
||||
try {
|
||||
@@ -8,10 +10,15 @@ export const copyToClipboard = async (str, rootEl?: HTMLElement) => {
|
||||
}
|
||||
}
|
||||
|
||||
const root = rootEl ?? document.body;
|
||||
const root = rootEl || deepActiveElement()?.getRootNode() || document.body;
|
||||
|
||||
const el = document.createElement("textarea");
|
||||
el.value = str;
|
||||
el.setAttribute("readonly", "");
|
||||
el.style.position = "fixed";
|
||||
el.style.top = "0";
|
||||
el.style.left = "0";
|
||||
el.style.opacity = "0";
|
||||
root.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand("copy");
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// From: https://davidwalsh.name/javascript-debounce-function
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
// leading edge and on the trailing.
|
||||
// leading edge. The trailing edge only fires if there were additional calls
|
||||
// during the wait period.
|
||||
|
||||
export const debounce = <T extends any[]>(
|
||||
func: (...args: T) => void,
|
||||
@@ -11,20 +10,35 @@ export const debounce = <T extends any[]>(
|
||||
immediate = false
|
||||
) => {
|
||||
let timeout: number | undefined;
|
||||
let trailingArgs: T | undefined;
|
||||
|
||||
const debouncedFunc = (...args: T): void => {
|
||||
const later = () => {
|
||||
timeout = undefined;
|
||||
func(...args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
const isLeading = immediate && !timeout;
|
||||
|
||||
if (timeout) {
|
||||
trailingArgs = args;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
timeout = window.setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
|
||||
timeout = window.setTimeout(() => {
|
||||
timeout = undefined;
|
||||
if (trailingArgs) {
|
||||
func(...trailingArgs);
|
||||
trailingArgs = undefined;
|
||||
} else if (!immediate) {
|
||||
func(...args);
|
||||
}
|
||||
}, wait);
|
||||
|
||||
if (isLeading) {
|
||||
func(...args);
|
||||
}
|
||||
};
|
||||
|
||||
debouncedFunc.cancel = () => {
|
||||
clearTimeout(timeout);
|
||||
trailingArgs = undefined;
|
||||
};
|
||||
|
||||
return debouncedFunc;
|
||||
};
|
||||
|
||||
@@ -5,12 +5,41 @@ import {
|
||||
formatDateMonthYear,
|
||||
formatDateVeryShort,
|
||||
formatDateWeekdayShort,
|
||||
formatDateYear,
|
||||
} from "../../common/datetime/format_date";
|
||||
import {
|
||||
formatTime,
|
||||
formatTimeWithSeconds,
|
||||
} from "../../common/datetime/format_time";
|
||||
|
||||
export function getPeriodicAxisLabelConfig(
|
||||
period: string,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
):
|
||||
| {
|
||||
formatter: (value: number) => string;
|
||||
}
|
||||
| undefined {
|
||||
if (period === "month") {
|
||||
return {
|
||||
formatter: (value: number) => {
|
||||
const date = new Date(value);
|
||||
return date.getMonth() === 0
|
||||
? `{bold|${formatDateMonthYear(date, locale, config)}}`
|
||||
: formatDateMonth(date, locale, config);
|
||||
},
|
||||
};
|
||||
}
|
||||
if (period === "year") {
|
||||
return {
|
||||
formatter: (value: number) =>
|
||||
formatDateYear(new Date(value), locale, config),
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function formatTimeLabel(
|
||||
value: number | Date,
|
||||
locale: FrontendLocaleData,
|
||||
|
||||
@@ -44,6 +44,7 @@ export type CustomLegendOption = ECOption["legend"] & {
|
||||
id?: string;
|
||||
secondaryIds?: string[]; // Other dataset IDs that should be controlled by this legend item.
|
||||
name: string;
|
||||
value?: string; // Current value to display next to the name in the legend.
|
||||
itemStyle?: Record<string, any>;
|
||||
}[];
|
||||
};
|
||||
@@ -279,18 +280,23 @@ export class HaChartBase extends LitElement {
|
||||
<div class="chart"></div>
|
||||
</div>
|
||||
${this._renderLegend()}
|
||||
<div class="chart-controls ${classMap({ small: this.smallControls })}">
|
||||
${this._isZoomed && !this.hideResetButton
|
||||
? html`<ha-icon-button
|
||||
class="zoom-reset"
|
||||
.path=${mdiRestart}
|
||||
@click=${this._handleZoomReset}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.history_charts.zoom_reset"
|
||||
)}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
<slot name="button"></slot>
|
||||
<div class="top-controls ${classMap({ small: this.smallControls })}">
|
||||
<slot name="search"></slot>
|
||||
<div
|
||||
class="chart-controls ${classMap({ small: this.smallControls })}"
|
||||
>
|
||||
${this._isZoomed && !this.hideResetButton
|
||||
? html`<ha-icon-button
|
||||
class="zoom-reset"
|
||||
.path=${mdiRestart}
|
||||
@click=${this._handleZoomReset}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.history_charts.zoom_reset"
|
||||
)}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
<slot name="button"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -333,12 +339,14 @@ export class HaChartBase extends LitElement {
|
||||
let itemStyle: Record<string, any> = {};
|
||||
let name = "";
|
||||
let id = "";
|
||||
let value = "";
|
||||
if (typeof item === "string") {
|
||||
name = item;
|
||||
id = item;
|
||||
} else {
|
||||
name = item.name ?? "";
|
||||
id = item.id ?? name;
|
||||
value = item.value ?? "";
|
||||
itemStyle = item.itemStyle ?? {};
|
||||
}
|
||||
const dataset =
|
||||
@@ -365,6 +373,7 @@ export class HaChartBase extends LitElement {
|
||||
})}
|
||||
></div>
|
||||
<div class="label">${name}</div>
|
||||
${value ? html`<div class="value">${value}</div>` : nothing}
|
||||
</li>`;
|
||||
})}
|
||||
${items.length > overflowLimit
|
||||
@@ -578,7 +587,7 @@ export class HaChartBase extends LitElement {
|
||||
id: "dataZoom",
|
||||
type: "inside",
|
||||
orient: "horizontal",
|
||||
filterMode: "none",
|
||||
filterMode: this._getDataZoomFilterMode() as any,
|
||||
xAxisIndex: 0,
|
||||
moveOnMouseMove: !this._isTouchDevice || this._isZoomed,
|
||||
preventDefaultMouseMove: !this._isTouchDevice || this._isZoomed,
|
||||
@@ -586,6 +595,23 @@ export class HaChartBase extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
// "boundaryFilter" is a custom mode added via axis-proxy-patch.ts.
|
||||
// It rescales the Y-axis to the visible data while keeping one point
|
||||
// just outside each boundary to avoid line gaps at the zoom edges.
|
||||
// Use "filter" for bar charts since boundaryFilter causes rendering issues.
|
||||
// Use "weakFilter" for other types (e.g. custom/timeline) so bars
|
||||
// spanning the visible range boundary are kept.
|
||||
private _getDataZoomFilterMode(): string {
|
||||
const series = ensureArray(this.data);
|
||||
if (series.every((s) => s.type === "line")) {
|
||||
return "boundaryFilter";
|
||||
}
|
||||
if (series.some((s) => s.type === "bar")) {
|
||||
return "filter";
|
||||
}
|
||||
return "weakFilter";
|
||||
}
|
||||
|
||||
private _createOptions(): ECOption {
|
||||
let xAxis = this.options?.xAxis;
|
||||
if (xAxis) {
|
||||
@@ -620,7 +646,7 @@ export class HaChartBase extends LitElement {
|
||||
hideOverlap: true,
|
||||
...axis.axisLabel,
|
||||
},
|
||||
minInterval,
|
||||
minInterval: axis.minInterval ?? minInterval,
|
||||
} as XAXisOption;
|
||||
});
|
||||
}
|
||||
@@ -877,10 +903,20 @@ export class HaChartBase extends LitElement {
|
||||
};
|
||||
}
|
||||
if (s.sampling === "minmax") {
|
||||
const minX =
|
||||
xAxis?.min && typeof xAxis.min === "number" ? xAxis.min : undefined;
|
||||
const maxX =
|
||||
xAxis?.max && typeof xAxis.max === "number" ? xAxis.max : undefined;
|
||||
const minX = xAxis?.min
|
||||
? xAxis.min instanceof Date
|
||||
? xAxis.min.getTime()
|
||||
: typeof xAxis.min === "number"
|
||||
? xAxis.min
|
||||
: undefined
|
||||
: undefined;
|
||||
const maxX = xAxis?.max
|
||||
? xAxis.max instanceof Date
|
||||
? xAxis.max.getTime()
|
||||
: typeof xAxis.max === "number"
|
||||
? xAxis.max
|
||||
: undefined
|
||||
: undefined;
|
||||
return {
|
||||
...s,
|
||||
sampling: undefined,
|
||||
@@ -1099,16 +1135,35 @@ export class HaChartBase extends LitElement {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.chart-controls {
|
||||
.top-controls {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 4px;
|
||||
top: var(--ha-space-4);
|
||||
inset-inline-start: var(--ha-space-4);
|
||||
inset-inline-end: var(--ha-space-1);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--ha-space-2);
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
::slotted([slot="search"]) {
|
||||
flex: 1 1 250px;
|
||||
min-width: 0;
|
||||
max-width: 250px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.chart-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-1);
|
||||
margin-inline-start: auto;
|
||||
flex-shrink: 0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.top-controls.small {
|
||||
top: 0;
|
||||
}
|
||||
.chart-controls.small {
|
||||
top: 0;
|
||||
flex-direction: row;
|
||||
}
|
||||
.chart-controls ha-icon-button,
|
||||
@@ -1156,6 +1211,9 @@ export class HaChartBase extends LitElement {
|
||||
.chart-legend.multiple-items li {
|
||||
max-width: 220px;
|
||||
}
|
||||
.chart-legend.multiple-items li:has(.value) {
|
||||
max-width: 300px;
|
||||
}
|
||||
.chart-legend .hidden {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
@@ -1164,6 +1222,12 @@ export class HaChartBase extends LitElement {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.chart-legend .value {
|
||||
color: var(--secondary-text-color);
|
||||
margin-inline-start: var(--ha-space-1);
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chart-legend .bullet {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { EChartsType } from "echarts/core";
|
||||
import type { GraphSeriesOption } from "echarts/charts";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
|
||||
import type {
|
||||
CallbackDataParams,
|
||||
TopLevelFormatterParams,
|
||||
@@ -63,6 +65,8 @@ export interface NetworkData {
|
||||
categories?: { name: string; symbol: string }[];
|
||||
}
|
||||
|
||||
const PHYSICS_DISABLE_THRESHOLD = 512;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/consistent-type-imports
|
||||
let GraphChart: typeof import("echarts/lib/chart/graph/install");
|
||||
|
||||
@@ -76,11 +80,23 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
params: TopLevelFormatterParams
|
||||
) => string;
|
||||
|
||||
/**
|
||||
* Optional callback that returns additional searchable strings for a node.
|
||||
* These are matched against the search filter in addition to the node's name and context.
|
||||
*/
|
||||
@property({ attribute: false }) public searchableAttributes?: (
|
||||
nodeId: string
|
||||
) => string[];
|
||||
|
||||
@property({ attribute: false }) public searchFilter = "";
|
||||
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@state() private _highlightedNodes?: Set<string>;
|
||||
|
||||
@state() private _reducedMotion = false;
|
||||
|
||||
@state() private _physicsEnabled = true;
|
||||
@state() private _physicsEnabled?: boolean;
|
||||
|
||||
@state() private _showLabels = true;
|
||||
|
||||
@@ -108,6 +124,14 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
];
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues): void {
|
||||
super.willUpdate(changedProperties);
|
||||
if (this._physicsEnabled === undefined && this.data?.nodes?.length > 1) {
|
||||
this._physicsEnabled =
|
||||
this.data.nodes.length <= PHYSICS_DISABLE_THRESHOLD;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!GraphChart || !this.data.nodes?.length) {
|
||||
return nothing;
|
||||
@@ -117,19 +141,24 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||
).matches;
|
||||
|
||||
const hasHighlightedNodes =
|
||||
this._highlightedNodes && this._highlightedNodes.size > 0;
|
||||
|
||||
return html`<ha-chart-base
|
||||
.hass=${this.hass}
|
||||
.data=${this._getSeries(
|
||||
this.data,
|
||||
this._physicsEnabled,
|
||||
this._physicsEnabled ?? false,
|
||||
this._reducedMotion,
|
||||
this._showLabels,
|
||||
isMobile
|
||||
isMobile,
|
||||
hasHighlightedNodes
|
||||
)}
|
||||
.options=${this._createOptions(this.data?.categories)}
|
||||
height="100%"
|
||||
.extraComponents=${[GraphChart]}
|
||||
>
|
||||
<slot name="search" slot="search"></slot>
|
||||
<slot name="button" slot="button"></slot>
|
||||
<ha-icon-button
|
||||
slot="button"
|
||||
@@ -165,7 +194,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
...category,
|
||||
icon: category.symbol,
|
||||
})),
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
},
|
||||
dataZoom: {
|
||||
type: "inside",
|
||||
@@ -175,13 +204,56 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
deepEqual
|
||||
);
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("searchFilter")) {
|
||||
const filter = this.searchFilter;
|
||||
if (!filter) {
|
||||
this._highlightedNodes = undefined;
|
||||
} else {
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
const matchingIds = new Set<string>();
|
||||
for (const node of this.data.nodes) {
|
||||
if (this._nodeMatchesFilter(node, lowerFilter)) {
|
||||
matchingIds.add(node.id);
|
||||
}
|
||||
}
|
||||
this._highlightedNodes = matchingIds;
|
||||
}
|
||||
this._applyHighlighting();
|
||||
this._updateMouseoverHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private _nodeMatchesFilter(node: NetworkNode, lowerFilter: string): boolean {
|
||||
if (node.name?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
if (node.context?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
if (node.id?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
if (this.searchableAttributes) {
|
||||
const extraValues = this.searchableAttributes(node.id);
|
||||
for (const value of extraValues) {
|
||||
if (value?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _getSeries = memoizeOne(
|
||||
(
|
||||
data: NetworkData,
|
||||
physicsEnabled: boolean,
|
||||
reducedMotion: boolean,
|
||||
showLabels: boolean,
|
||||
isMobile: boolean
|
||||
isMobile: boolean,
|
||||
hasHighlightedNodes?: boolean
|
||||
) => ({
|
||||
id: "network",
|
||||
type: "graph",
|
||||
@@ -214,7 +286,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
focus: isMobile ? "none" : "adjacency",
|
||||
focus: hasHighlightedNodes ? "self" : isMobile ? "none" : "adjacency",
|
||||
},
|
||||
force: {
|
||||
repulsion: [400, 600],
|
||||
@@ -362,6 +434,68 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
});
|
||||
}
|
||||
|
||||
private _applyHighlighting() {
|
||||
const chart = this._baseChart?.chart;
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
// Reset all nodes to normal opacity first
|
||||
chart.dispatchAction({ type: "downplay" });
|
||||
|
||||
const highlighted = this._highlightedNodes;
|
||||
if (!highlighted || highlighted.size === 0) {
|
||||
return;
|
||||
}
|
||||
const dataIndices: number[] = [];
|
||||
this.data.nodes.forEach((node, index) => {
|
||||
if (highlighted.has(node.id)) {
|
||||
dataIndices.push(index);
|
||||
}
|
||||
});
|
||||
if (dataIndices.length > 0) {
|
||||
chart.dispatchAction({ type: "highlight", dataIndex: dataIndices });
|
||||
}
|
||||
}
|
||||
|
||||
private _emphasisGuardHandler?: () => void;
|
||||
|
||||
private _updateMouseoverHandler() {
|
||||
const chart = this._baseChart?.chart;
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When there are highlighted nodes, re-apply highlighting on hover
|
||||
// and mouseout to prevent hover from overriding the search state
|
||||
if (this._highlightedNodes && this._highlightedNodes.size > 0) {
|
||||
if (this._emphasisGuardHandler) {
|
||||
// Guard already set
|
||||
return;
|
||||
}
|
||||
this._emphasisGuardHandler = () => {
|
||||
this._applyHighlighting();
|
||||
};
|
||||
chart.on("mouseover", this._emphasisGuardHandler);
|
||||
chart.on("mouseout", this._emphasisGuardHandler);
|
||||
} else {
|
||||
if (!this._emphasisGuardHandler) {
|
||||
return;
|
||||
}
|
||||
chart.off("mouseover", this._emphasisGuardHandler);
|
||||
chart.off("mouseout", this._emphasisGuardHandler);
|
||||
this._emphasisGuardHandler = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
if (this._emphasisGuardHandler) {
|
||||
this._baseChart?.chart?.off("mouseover", this._emphasisGuardHandler);
|
||||
this._baseChart?.chart?.off("mouseout", this._emphasisGuardHandler);
|
||||
this._emphasisGuardHandler = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _togglePhysics() {
|
||||
this._saveNodePositions();
|
||||
this._physicsEnabled = !this._physicsEnabled;
|
||||
|
||||
@@ -239,7 +239,9 @@ export class StateHistoryChartLine extends LitElement {
|
||||
changedProps.has("fitYData") ||
|
||||
changedProps.has("paddingYAxis") ||
|
||||
changedProps.has("_visualMap") ||
|
||||
changedProps.has("_yWidth")
|
||||
changedProps.has("_yWidth") ||
|
||||
(changedProps.has("hass") &&
|
||||
this._hasEntityStatesChanged(changedProps.get("hass")))
|
||||
) {
|
||||
const rtl = computeRTL(this.hass);
|
||||
let minYAxis: number | ((values: { min: number }) => number) | undefined =
|
||||
@@ -296,6 +298,19 @@ export class StateHistoryChartLine extends LitElement {
|
||||
legend: {
|
||||
type: "custom",
|
||||
show: this.showNames,
|
||||
data: this._chartData
|
||||
.map((d, i) => ({ dataset: d, entityId: this._entityIds[i] }))
|
||||
.filter((item) => !(item.dataset as LineSeriesOption).areaStyle)
|
||||
.map((item) => {
|
||||
const stateObj = this.hass.states[item.entityId];
|
||||
return {
|
||||
id: item.dataset.id as string,
|
||||
name: item.dataset.name as string,
|
||||
value: stateObj
|
||||
? this.hass.formatEntityState(stateObj)
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
},
|
||||
grid: {
|
||||
top: 15,
|
||||
@@ -316,6 +331,13 @@ export class StateHistoryChartLine extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _hasEntityStatesChanged(oldHass: HomeAssistant): boolean {
|
||||
return this._entityIds.some(
|
||||
(entityId) =>
|
||||
this.hass.states[entityId]?.state !== oldHass.states[entityId]?.state
|
||||
);
|
||||
}
|
||||
|
||||
private _generateData() {
|
||||
let colorIndex = 0;
|
||||
const computedStyles = getComputedStyle(this);
|
||||
|
||||
@@ -27,10 +27,12 @@ import {
|
||||
getDisplayUnit,
|
||||
getStatisticLabel,
|
||||
getStatisticMetadata,
|
||||
isExternalStatistic,
|
||||
statisticsHaveType,
|
||||
} from "../../data/recorder";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { getPeriodicAxisLabelConfig } from "./axis-label";
|
||||
import type { CustomLegendOption } from "./ha-chart-base";
|
||||
import "./ha-chart-base";
|
||||
|
||||
@@ -292,6 +294,22 @@ export class StatisticsChart extends LitElement {
|
||||
type: "time",
|
||||
min: startTime,
|
||||
max: this.endTime,
|
||||
...(this.period === "month" && {
|
||||
minInterval: 28 * 24 * 3600 * 1000,
|
||||
axisLabel: getPeriodicAxisLabelConfig(
|
||||
"month",
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
),
|
||||
}),
|
||||
...(this.period === "year" && {
|
||||
minInterval: 365 * 24 * 3600 * 1000,
|
||||
axisLabel: getPeriodicAxisLabelConfig(
|
||||
"year",
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
),
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: "hiddenAxis",
|
||||
@@ -398,7 +416,31 @@ export class StatisticsChart extends LitElement {
|
||||
endTime = new Date();
|
||||
}
|
||||
|
||||
let unit: string | undefined | null;
|
||||
// Check if we need to display most recent data. Allow 10m of leeway for "now",
|
||||
// because stats are 5 minute aggregated.
|
||||
// Use same now point for all statistics even if processing time means the
|
||||
// state value is actually from a slightly later time. Otherwise the points
|
||||
// end up separated slightly and disappear from the tooltips.
|
||||
const now = new Date();
|
||||
const displayCurrentState = now.getTime() - endTime.getTime() <= 600000;
|
||||
|
||||
// Try to determine chart unit if it has not already been set explicitly
|
||||
if (!this.unit) {
|
||||
let unit: string | undefined | null;
|
||||
statisticsData.forEach(([statistic_id, _stats]) => {
|
||||
const meta = statisticsMetaData?.[statistic_id];
|
||||
const statisticUnit = getDisplayUnit(this.hass, statistic_id, meta);
|
||||
if (unit === undefined) {
|
||||
unit = statisticUnit;
|
||||
} else if (unit !== null && unit !== statisticUnit) {
|
||||
// Clear unit if not all statistics have same unit
|
||||
unit = null;
|
||||
}
|
||||
});
|
||||
if (unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
const names = this.names || {};
|
||||
statisticsData.forEach(([statistic_id, stats]) => {
|
||||
@@ -408,18 +450,6 @@ export class StatisticsChart extends LitElement {
|
||||
name = getStatisticLabel(this.hass, statistic_id, meta);
|
||||
}
|
||||
|
||||
if (!this.unit) {
|
||||
if (unit === undefined) {
|
||||
unit = getDisplayUnit(this.hass, statistic_id, meta);
|
||||
} else if (
|
||||
unit !== null &&
|
||||
unit !== getDisplayUnit(this.hass, statistic_id, meta)
|
||||
) {
|
||||
// Clear unit if not all statistics have same unit
|
||||
unit = null;
|
||||
}
|
||||
}
|
||||
|
||||
// array containing [value1, value2, etc]
|
||||
let prevValues: (number | null)[][] | null = null;
|
||||
let prevEndTime: Date | undefined;
|
||||
@@ -507,7 +537,6 @@ export class StatisticsChart extends LitElement {
|
||||
id: `${statistic_id}-${type}`,
|
||||
type: this.chartType,
|
||||
smooth: this.chartType === "line" ? 0.4 : false,
|
||||
smoothMonotone: "x",
|
||||
cursor: "default",
|
||||
data: [],
|
||||
name: name
|
||||
@@ -544,7 +573,7 @@ export class StatisticsChart extends LitElement {
|
||||
(series as LineSeriesOption).areaStyle = undefined;
|
||||
} else {
|
||||
series.stackOrder = "seriesAsc";
|
||||
if (drawBands && type === bandTop) {
|
||||
if (type === bandTop) {
|
||||
(series as LineSeriesOption).areaStyle = {
|
||||
color: color + "3F",
|
||||
};
|
||||
@@ -613,10 +642,10 @@ export class StatisticsChart extends LitElement {
|
||||
}
|
||||
});
|
||||
|
||||
// Close out the last stat segment at prevEndTime
|
||||
// For line charts, close out the last stat segment at prevEndTime
|
||||
const lastEndTime = prevEndTime;
|
||||
const lastValues = prevValues;
|
||||
if (lastEndTime && lastValues) {
|
||||
if (this.chartType === "line" && lastEndTime && lastValues) {
|
||||
statDataSets.forEach((d, i) => {
|
||||
d.data!.push(
|
||||
this._transformDataValue([lastEndTime, ...lastValues[i]!])
|
||||
@@ -624,13 +653,14 @@ export class StatisticsChart extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
// Append current state if viewing recent data
|
||||
const now = new Date();
|
||||
// allow 10m of leeway for "now", because stats are 5 minute aggregated
|
||||
const isUpToNow = now.getTime() - endTime.getTime() <= 600000;
|
||||
if (isUpToNow) {
|
||||
// Skip external statistics (they have ":" in the ID)
|
||||
if (!statistic_id.includes(":")) {
|
||||
// Show current state if required, and units match (or are unknown)
|
||||
const statisticUnit = getDisplayUnit(this.hass, statistic_id, meta);
|
||||
if (
|
||||
displayCurrentState &&
|
||||
(!this.unit || !statisticUnit || this.unit === statisticUnit)
|
||||
) {
|
||||
// Skip external statistics
|
||||
if (!isExternalStatistic(statistic_id)) {
|
||||
const stateObj = this.hass.states[statistic_id];
|
||||
if (stateObj) {
|
||||
const currentValue = parseFloat(stateObj.state);
|
||||
@@ -640,11 +670,12 @@ export class StatisticsChart extends LitElement {
|
||||
) {
|
||||
// Then push the current state at now
|
||||
statTypes.forEach((type, i) => {
|
||||
const val: (number | null)[] = [];
|
||||
if (type === "sum" || type === "change") {
|
||||
// Skip cumulative types - need special calculation
|
||||
val.push(null);
|
||||
} else if (
|
||||
// Skip cumulative types - need special calculation.
|
||||
return;
|
||||
}
|
||||
const val: (number | null)[] = [];
|
||||
if (
|
||||
type === bandTop &&
|
||||
this.chartType === "line" &&
|
||||
drawBands &&
|
||||
@@ -670,10 +701,6 @@ export class StatisticsChart extends LitElement {
|
||||
Array.prototype.push.apply(legendData, statLegendData);
|
||||
});
|
||||
|
||||
if (unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
legendData.forEach(({ id, name, color, borderColor }) => {
|
||||
// Add an empty series for the legend
|
||||
totalDataSets.push({
|
||||
|
||||
@@ -32,11 +32,13 @@ export class DialogDataTableSettings extends LitElement {
|
||||
|
||||
@state() private _hiddenColumns?: string[];
|
||||
|
||||
private _lastFixedKeys: string[] = [];
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
public showDialog(params: DataTableSettingsDialogParams) {
|
||||
this._params = params;
|
||||
this._columnOrder = params.columnOrder;
|
||||
this._columnOrder = this._preserveLastFixed(params.columnOrder);
|
||||
this._hiddenColumns = params.hiddenColumns;
|
||||
this._open = true;
|
||||
}
|
||||
@@ -50,6 +52,29 @@ export class DialogDataTableSettings extends LitElement {
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
private _lastFixedCount(): number {
|
||||
const lastFixedKeys = Object.keys(this._params!.columns).filter(
|
||||
(col) => this._params!.columns[col].lastFixed
|
||||
);
|
||||
if (lastFixedKeys.length) {
|
||||
this._lastFixedKeys = lastFixedKeys;
|
||||
}
|
||||
return lastFixedKeys.length;
|
||||
}
|
||||
|
||||
private _preserveLastFixed(columnOrder) {
|
||||
let strippedColumnOrder;
|
||||
const lastFixedCount = this._lastFixedCount();
|
||||
if (lastFixedCount && columnOrder) {
|
||||
strippedColumnOrder = [...columnOrder];
|
||||
strippedColumnOrder.splice(
|
||||
columnOrder.length - lastFixedCount,
|
||||
lastFixedCount
|
||||
);
|
||||
}
|
||||
return strippedColumnOrder;
|
||||
}
|
||||
|
||||
private _sortedColumns = memoizeOne(
|
||||
(
|
||||
columns: DataTableColumnContainer,
|
||||
@@ -57,7 +82,7 @@ export class DialogDataTableSettings extends LitElement {
|
||||
hiddenColumns: string[] | undefined
|
||||
) =>
|
||||
Object.keys(columns)
|
||||
.filter((col) => !columns[col].hidden)
|
||||
.filter((col) => !columns[col].hidden && !columns[col].lastFixed)
|
||||
.sort((a, b) => {
|
||||
const orderA = columnOrder?.indexOf(a) ?? -1;
|
||||
const orderB = columnOrder?.indexOf(b) ?? -1;
|
||||
@@ -195,7 +220,8 @@ export class DialogDataTableSettings extends LitElement {
|
||||
|
||||
this._columnOrder = columnOrder;
|
||||
|
||||
this._params!.onUpdate(this._columnOrder, this._hiddenColumns);
|
||||
const reportedOrder = columnOrder.concat(this._lastFixedKeys);
|
||||
this._params!.onUpdate(reportedOrder, this._hiddenColumns);
|
||||
}
|
||||
|
||||
private _toggle(ev) {
|
||||
@@ -276,7 +302,8 @@ export class DialogDataTableSettings extends LitElement {
|
||||
|
||||
this._hiddenColumns = hidden;
|
||||
|
||||
this._params!.onUpdate(this._columnOrder, this._hiddenColumns);
|
||||
const reportedOrder = this._columnOrder.concat(this._lastFixedKeys);
|
||||
this._params!.onUpdate(reportedOrder, this._hiddenColumns);
|
||||
}
|
||||
|
||||
private _reset() {
|
||||
|
||||
@@ -27,7 +27,7 @@ import type { HomeAssistant } from "../../types";
|
||||
import "../ha-checkbox";
|
||||
import type { HaCheckbox } from "../ha-checkbox";
|
||||
import "../ha-svg-icon";
|
||||
import "../search-input";
|
||||
import "../input/ha-input-search";
|
||||
import { filterData, sortData } from "./sort-filter";
|
||||
|
||||
export interface RowClickedEvent {
|
||||
@@ -86,6 +86,7 @@ export interface DataTableColumnData<T = any> extends DataTableSortColumnData {
|
||||
flex?: number;
|
||||
forceLTR?: boolean;
|
||||
hidden?: boolean;
|
||||
lastFixed?: boolean;
|
||||
}
|
||||
|
||||
export type ClonedDataTableColumnData = Omit<DataTableColumnData, "title"> & {
|
||||
@@ -117,8 +118,6 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public clickable = false;
|
||||
|
||||
@property({ attribute: "has-fab", type: Boolean }) public hasFab = false;
|
||||
|
||||
/**
|
||||
* Add an extra row at the bottom of the data table
|
||||
* @type {TemplateResult}
|
||||
@@ -135,9 +134,6 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public searchLabel?: string;
|
||||
|
||||
@property({ type: Boolean, attribute: "no-label-float" })
|
||||
public noLabelFloat? = false;
|
||||
|
||||
@property({ type: String }) public filter = "";
|
||||
|
||||
@property({ attribute: false }) public groupColumn?: string;
|
||||
@@ -359,6 +355,11 @@ export class HaDataTable extends LitElement {
|
||||
.sort((a, b) => {
|
||||
const orderA = columnOrder!.indexOf(a);
|
||||
const orderB = columnOrder!.indexOf(b);
|
||||
const fixedA = Boolean(columns[a].lastFixed);
|
||||
const fixedB = Boolean(columns[b].lastFixed);
|
||||
if (fixedA !== fixedB) {
|
||||
return fixedA ? 1 : -1;
|
||||
}
|
||||
if (orderA !== orderB) {
|
||||
if (orderA === -1) {
|
||||
return 1;
|
||||
@@ -390,12 +391,11 @@ export class HaDataTable extends LitElement {
|
||||
${this._filterable
|
||||
? html`
|
||||
<div class="table-header">
|
||||
<search-input
|
||||
.hass=${this.hass}
|
||||
@value-changed=${this._handleSearchChange}
|
||||
.label=${this.searchLabel}
|
||||
.noLabelFloat=${this.noLabelFloat}
|
||||
></search-input>
|
||||
<ha-input-search
|
||||
appearance="outlined"
|
||||
@input=${this._handleSearchChange}
|
||||
.placeholder=${this.searchLabel}
|
||||
></ha-input-search>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
@@ -428,9 +428,9 @@ export class HaDataTable extends LitElement {
|
||||
<ha-checkbox
|
||||
class="mdc-data-table__row-checkbox"
|
||||
@change=${this._handleHeaderRowCheckboxClick}
|
||||
.indeterminate=${this._checkedRows.length &&
|
||||
.indeterminate=${!!this._checkedRows.length &&
|
||||
this._checkedRows.length !== this._checkableRowsCount}
|
||||
.checked=${this._checkedRows.length &&
|
||||
.checked=${!!this._checkedRows.length &&
|
||||
this._checkedRows.length === this._checkableRowsCount}
|
||||
>
|
||||
</ha-checkbox>
|
||||
@@ -517,7 +517,6 @@ export class HaDataTable extends LitElement {
|
||||
this._filteredData,
|
||||
localize,
|
||||
this.appendRow,
|
||||
this.hasFab,
|
||||
this.groupColumn,
|
||||
this.groupOrder,
|
||||
this._collapsedGroups,
|
||||
@@ -714,14 +713,13 @@ export class HaDataTable extends LitElement {
|
||||
data: DataTableRowData[],
|
||||
localize: LocalizeFunc,
|
||||
appendRow,
|
||||
hasFab: boolean,
|
||||
groupColumn: string | undefined,
|
||||
groupOrder: string[] | undefined,
|
||||
collapsedGroups: string[],
|
||||
sortColumn: string | undefined,
|
||||
sortDirection: SortingDirection
|
||||
) => {
|
||||
if (appendRow || hasFab || groupColumn) {
|
||||
if (appendRow || groupColumn) {
|
||||
let items = [...data];
|
||||
|
||||
if (groupColumn) {
|
||||
@@ -811,13 +809,11 @@ export class HaDataTable extends LitElement {
|
||||
items.push({ append: true, selectable: false, content: appendRow });
|
||||
}
|
||||
|
||||
if (hasFab) {
|
||||
items.push({ empty: true });
|
||||
}
|
||||
items.push({ empty: true });
|
||||
|
||||
return items;
|
||||
}
|
||||
return data;
|
||||
return [...data, { empty: true }];
|
||||
}
|
||||
);
|
||||
|
||||
@@ -869,7 +865,6 @@ export class HaDataTable extends LitElement {
|
||||
this._filteredData,
|
||||
this.localizeFunc || this.hass.localize,
|
||||
this.appendRow,
|
||||
this.hasFab,
|
||||
this.groupColumn,
|
||||
this.groupOrder,
|
||||
this._collapsedGroups,
|
||||
@@ -975,12 +970,12 @@ export class HaDataTable extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleSearchChange(ev: CustomEvent): void {
|
||||
private _handleSearchChange(ev: InputEvent): void {
|
||||
if (this.filter) {
|
||||
return;
|
||||
}
|
||||
this._lastSelectedRowId = null;
|
||||
this._debounceSearch(ev.detail.value);
|
||||
this._debounceSearch((ev.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
private async _calcTableHeight() {
|
||||
@@ -1087,11 +1082,8 @@ export class HaDataTable extends LitElement {
|
||||
}
|
||||
|
||||
.mdc-data-table__row.empty-row {
|
||||
height: max(
|
||||
var(
|
||||
--data-table-empty-row-height,
|
||||
var(--data-table-row-height, 52px)
|
||||
),
|
||||
height: var(
|
||||
--data-table-empty-row-height,
|
||||
var(--safe-area-inset-bottom, 0px)
|
||||
);
|
||||
}
|
||||
@@ -1396,11 +1388,9 @@ export class HaDataTable extends LitElement {
|
||||
.table-header {
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
search-input {
|
||||
display: block;
|
||||
ha-input-search {
|
||||
flex: 1;
|
||||
--mdc-text-field-fill-color: var(--sidebar-background-color);
|
||||
--mdc-text-field-idle-line-color: transparent;
|
||||
padding: var(--ha-space-3);
|
||||
}
|
||||
slot[name="header"] {
|
||||
display: block;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user