mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-21 07:09:28 +00:00
Compare commits
648 Commits
allow-to-s
...
dashboard_
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1b44c7ce64 | ||
![]() |
de370d6384 | ||
![]() |
1cf928b425 | ||
![]() |
b9c62c34a0 | ||
![]() |
37554213dc | ||
![]() |
3845fd2ef9 | ||
![]() |
837b36a430 | ||
![]() |
bff09a7a36 | ||
![]() |
a5e5efa526 | ||
![]() |
1cd5b9ff37 | ||
![]() |
6a11155dd7 | ||
![]() |
1acb76d2f0 | ||
![]() |
2e8a40efd5 | ||
![]() |
845a0e4e39 | ||
![]() |
9417f13cc9 | ||
![]() |
d3f52f3bc0 | ||
![]() |
7d0ec00fde | ||
![]() |
6dbb5dd4be | ||
![]() |
f30e7948cb | ||
![]() |
41472d8160 | ||
![]() |
e596d32426 | ||
![]() |
89de62812f | ||
![]() |
3e391fcbe6 | ||
![]() |
708ab2ed69 | ||
![]() |
1d45cb78fe | ||
![]() |
d3f6ebd1d0 | ||
![]() |
21644c70b3 | ||
![]() |
eb75389cac | ||
![]() |
cd4f3a091b | ||
![]() |
19c1973ec1 | ||
![]() |
a8efbe5b06 | ||
![]() |
44b5c66c19 | ||
![]() |
3fa3bba4d8 | ||
![]() |
ab09c821a2 | ||
![]() |
37b7395986 | ||
![]() |
4933413140 | ||
![]() |
1c16bd5ab2 | ||
![]() |
3a926c6f83 | ||
![]() |
7a619ad55a | ||
![]() |
bc21425981 | ||
![]() |
f2505c0798 | ||
![]() |
cee8c756fc | ||
![]() |
396b3aace9 | ||
![]() |
28dc1e4da9 | ||
![]() |
aa0419e783 | ||
![]() |
fd9c24d05e | ||
![]() |
8bdbe8c6a6 | ||
![]() |
39550cefa0 | ||
![]() |
589e3b63c7 | ||
![]() |
0cc38278b9 | ||
![]() |
aafdf7bed7 | ||
![]() |
a7f6ce3079 | ||
![]() |
3d03f74d66 | ||
![]() |
e09a6a23fc | ||
![]() |
9e4bb6ed0c | ||
![]() |
c45c8ab5c0 | ||
![]() |
5ef6973933 | ||
![]() |
1df344580a | ||
![]() |
25acc479b9 | ||
![]() |
c9f051374b | ||
![]() |
bf5116fc0b | ||
![]() |
edc8cb1d2e | ||
![]() |
a60bb3ae0a | ||
![]() |
784f753f07 | ||
![]() |
e63c7e3763 | ||
![]() |
cfa522068c | ||
![]() |
5f2375fe84 | ||
![]() |
a9e34d7590 | ||
![]() |
463cfb869f | ||
![]() |
acb5a2b283 | ||
![]() |
08597d6e91 | ||
![]() |
9478485268 | ||
![]() |
0af9ec2fff | ||
![]() |
f6e74de05e | ||
![]() |
8b0373d5c0 | ||
![]() |
d4218250af | ||
![]() |
78783942be | ||
![]() |
17ad8013ca | ||
![]() |
f70767f084 | ||
![]() |
3972394d82 | ||
![]() |
5331fb4f5f | ||
![]() |
16df352ba8 | ||
![]() |
8e1e75dee1 | ||
![]() |
d27b4e04a9 | ||
![]() |
36f7b34ac5 | ||
![]() |
c0ff24bf1b | ||
![]() |
abb5aa348f | ||
![]() |
37ab5cbdc3 | ||
![]() |
42be3b331c | ||
![]() |
71e05f79c2 | ||
![]() |
c5a0a5bbbf | ||
![]() |
c3d809fcf3 | ||
![]() |
cbd0c39091 | ||
![]() |
113eb5be24 | ||
![]() |
c497669fd3 | ||
![]() |
c32ca5885b | ||
![]() |
3c792c4019 | ||
![]() |
9762e61ee2 | ||
![]() |
c09c39998b | ||
![]() |
d37e29c247 | ||
![]() |
51f22cd74a | ||
![]() |
b57dc968bd | ||
![]() |
e2e8cb785a | ||
![]() |
9961a4ae3f | ||
![]() |
c25755bfcd | ||
![]() |
40983619d6 | ||
![]() |
54758b5962 | ||
![]() |
4f09485b20 | ||
![]() |
9207f6c407 | ||
![]() |
1a2312460a | ||
![]() |
951b88ab4c | ||
![]() |
d3cc57d8b4 | ||
![]() |
4e9b118728 | ||
![]() |
f1748e4dd5 | ||
![]() |
d491d8f5ac | ||
![]() |
cf0fde0f3c | ||
![]() |
a7dc2cfaa6 | ||
![]() |
d8c7db6ebf | ||
![]() |
c3743b57ea | ||
![]() |
e16a101de8 | ||
![]() |
94ad47c60e | ||
![]() |
184ef7b7ff | ||
![]() |
81053f2e07 | ||
![]() |
e8b4eeec67 | ||
![]() |
b0b7e77e28 | ||
![]() |
763f80b46a | ||
![]() |
0c2531a7ee | ||
![]() |
8d2ec8098c | ||
![]() |
d2caed2b68 | ||
![]() |
402d443843 | ||
![]() |
399f12194a | ||
![]() |
a745539c33 | ||
![]() |
f6fddbc6ec | ||
![]() |
01f51f3247 | ||
![]() |
80112bb662 | ||
![]() |
7ce7cbb755 | ||
![]() |
464ecffda7 | ||
![]() |
33e0c691c7 | ||
![]() |
d94f7c90c0 | ||
![]() |
d8d16c4d5f | ||
![]() |
1cb238ec2a | ||
![]() |
3e6ab8b179 | ||
![]() |
10bcaadcdb | ||
![]() |
67517643ef | ||
![]() |
eb35eb3de5 | ||
![]() |
8350d71f6e | ||
![]() |
c840f1cbb1 | ||
![]() |
8efc0816bb | ||
![]() |
b12e4989db | ||
![]() |
2b67731906 | ||
![]() |
ccba7a7623 | ||
![]() |
c0dfc9f73e | ||
![]() |
be1624f66f | ||
![]() |
32edbd7b33 | ||
![]() |
18827db9ba | ||
![]() |
191250a66a | ||
![]() |
1fdf609606 | ||
![]() |
6ffc0625d3 | ||
![]() |
c9f5d16745 | ||
![]() |
eb4afedf2e | ||
![]() |
2b9540fe03 | ||
![]() |
53b8d1bb0a | ||
![]() |
0ff5bffd0c | ||
![]() |
82a464f50f | ||
![]() |
fdddc18291 | ||
![]() |
463a3244cf | ||
![]() |
6cae11f0a6 | ||
![]() |
65112b36ce | ||
![]() |
58625d2a9d | ||
![]() |
9bafbdd989 | ||
![]() |
eedb42b2f3 | ||
![]() |
4354ad3807 | ||
![]() |
aeaf091b50 | ||
![]() |
c6be4d6f4d | ||
![]() |
c48b620e03 | ||
![]() |
77e05decdf | ||
![]() |
b24e99c56c | ||
![]() |
768344c3f7 | ||
![]() |
03a21d5519 | ||
![]() |
1247a5c8d3 | ||
![]() |
db8287df89 | ||
![]() |
71edbd6352 | ||
![]() |
9d87a66908 | ||
![]() |
11d62cece2 | ||
![]() |
f15a65f5a6 | ||
![]() |
a03d3f796b | ||
![]() |
d0f5b0e864 | ||
![]() |
e4a67dd555 | ||
![]() |
f72ab94742 | ||
![]() |
b521be6d3b | ||
![]() |
3fa7001be6 | ||
![]() |
b45226509b | ||
![]() |
2a5f8097bc | ||
![]() |
0ffe0f38e1 | ||
![]() |
c48491088e | ||
![]() |
f115e4025d | ||
![]() |
7be8a799aa | ||
![]() |
d992b2d40b | ||
![]() |
96fbd8aefb | ||
![]() |
17df761a1b | ||
![]() |
79b2fa96ed | ||
![]() |
c14e3333cf | ||
![]() |
4af0ecbaa8 | ||
![]() |
ce11301516 | ||
![]() |
16766f8878 | ||
![]() |
5e933e8e15 | ||
![]() |
7eb92be84a | ||
![]() |
60ec4d31db | ||
![]() |
3526ba308f | ||
![]() |
02a212a47d | ||
![]() |
49f88a98a5 | ||
![]() |
feb371839c | ||
![]() |
24c37f5293 | ||
![]() |
8f3fea5a33 | ||
![]() |
b2bc529d7b | ||
![]() |
ad68782b79 | ||
![]() |
f432528388 | ||
![]() |
f98c0769b0 | ||
![]() |
b2cb0d8e0f | ||
![]() |
4c7c04bdc0 | ||
![]() |
ffb7469a7e | ||
![]() |
d88831b719 | ||
![]() |
3b2f6d71f5 | ||
![]() |
a08185a1a5 | ||
![]() |
7ee91ca8fc | ||
![]() |
b6fe0cfa1b | ||
![]() |
c3a9682861 | ||
![]() |
62d21bea4f | ||
![]() |
434b9595c0 | ||
![]() |
a0f1b7f365 | ||
![]() |
628c2c39cf | ||
![]() |
4b885cbd93 | ||
![]() |
02d9786f8c | ||
![]() |
88d14cd7b5 | ||
![]() |
4ea8f599cf | ||
![]() |
37ef444180 | ||
![]() |
4253feb8a2 | ||
![]() |
ce33cf7ff3 | ||
![]() |
faa4455951 | ||
![]() |
08d8b43f44 | ||
![]() |
15c67fe299 | ||
![]() |
a10ec1f53c | ||
![]() |
1b220abf70 | ||
![]() |
607175706b | ||
![]() |
f8966a2114 | ||
![]() |
4c94ac5dda | ||
![]() |
6d1e923b83 | ||
![]() |
2c743b7b56 | ||
![]() |
6686da1f24 | ||
![]() |
6bdeb45f6b | ||
![]() |
9f05a9679b | ||
![]() |
51a6376991 | ||
![]() |
c5056eb4d2 | ||
![]() |
79f3759756 | ||
![]() |
6c3b748279 | ||
![]() |
4293192e74 | ||
![]() |
ceaceaf47b | ||
![]() |
479a625662 | ||
![]() |
095d171a61 | ||
![]() |
8c3a7de6d9 | ||
![]() |
84e743c4c0 | ||
![]() |
51f8d91ddf | ||
![]() |
8f1a6ef1b1 | ||
![]() |
b3f1783269 | ||
![]() |
7e630d0fc5 | ||
![]() |
a4533251a1 | ||
![]() |
659db109aa | ||
![]() |
d3fd27910a | ||
![]() |
eae3c1309f | ||
![]() |
4a5b67e320 | ||
![]() |
86c014b677 | ||
![]() |
5a6d6dc7d3 | ||
![]() |
294df396f4 | ||
![]() |
cc01e8d6a8 | ||
![]() |
a3532a41da | ||
![]() |
ae35fd1eb8 | ||
![]() |
63095f1501 | ||
![]() |
bf9e2cd404 | ||
![]() |
5b7ef941e4 | ||
![]() |
352e721d0c | ||
![]() |
220b4794c5 | ||
![]() |
811ebde42a | ||
![]() |
bfeee618f4 | ||
![]() |
db9b16e9f5 | ||
![]() |
7861d813b1 | ||
![]() |
d7760c4b7a | ||
![]() |
a60a721ea5 | ||
![]() |
36219e1cb4 | ||
![]() |
7fdbc9dd32 | ||
![]() |
334be93254 | ||
![]() |
c14a6d59e2 | ||
![]() |
7a8139b650 | ||
![]() |
9d2a443217 | ||
![]() |
484b166233 | ||
![]() |
530208cb6a | ||
![]() |
b534ff8ca3 | ||
![]() |
02bd50c434 | ||
![]() |
9a84ce7b81 | ||
![]() |
6e00be6684 | ||
![]() |
91ec43b9bc | ||
![]() |
4a4d9a08d5 | ||
![]() |
0c32d1eb4e | ||
![]() |
f43171f91c | ||
![]() |
48593eee0d | ||
![]() |
c106a0ac85 | ||
![]() |
e1a71fbfaa | ||
![]() |
0489d8922e | ||
![]() |
d7f1e9d091 | ||
![]() |
32bc8bd01d | ||
![]() |
242b018ece | ||
![]() |
c25447d001 | ||
![]() |
d2d718475f | ||
![]() |
8e1e42cd50 | ||
![]() |
014f9b8b73 | ||
![]() |
774c7e275c | ||
![]() |
75c43d15e1 | ||
![]() |
e288b003d8 | ||
![]() |
4aa8518ed6 | ||
![]() |
6acbf6395c | ||
![]() |
2030feabf7 | ||
![]() |
46d1dbcb47 | ||
![]() |
2f6297ec17 | ||
![]() |
a3400a2f9c | ||
![]() |
c345f41416 | ||
![]() |
292cdc7621 | ||
![]() |
c5ba74e0b4 | ||
![]() |
41b24de559 | ||
![]() |
4fe7b18161 | ||
![]() |
399a979c33 | ||
![]() |
03c5482860 | ||
![]() |
a116a50604 | ||
![]() |
0dfa292c40 | ||
![]() |
5914a6c1a4 | ||
![]() |
8daff17d6a | ||
![]() |
59bd852e7a | ||
![]() |
246fe2861e | ||
![]() |
dbf623ada2 | ||
![]() |
c848356a6d | ||
![]() |
47022d3a04 | ||
![]() |
d732bd4776 | ||
![]() |
5a5265723c | ||
![]() |
30c6e4e35e | ||
![]() |
c5f909d89f | ||
![]() |
13a691606f | ||
![]() |
44748df3ac | ||
![]() |
4f3dc82fd9 | ||
![]() |
b2e260d6ba | ||
![]() |
7111a21173 | ||
![]() |
ad51d313a1 | ||
![]() |
60345f3fe8 | ||
![]() |
956723cf15 | ||
![]() |
dd6a69ea03 | ||
![]() |
98bd08c9dd | ||
![]() |
6b31c07459 | ||
![]() |
c3b41afb68 | ||
![]() |
46d8f2eefb | ||
![]() |
0ffc7b59d6 | ||
![]() |
74be4ae20a | ||
![]() |
5455ce2e0f | ||
![]() |
01405d96b6 | ||
![]() |
965f893a65 | ||
![]() |
0b6813d9dc | ||
![]() |
cbd424ff5a | ||
![]() |
b899e39a9e | ||
![]() |
49a66961e2 | ||
![]() |
c69fb77b62 | ||
![]() |
d794ec3408 | ||
![]() |
d8c98d8f96 | ||
![]() |
acb32ae5c8 | ||
![]() |
c567a61dd7 | ||
![]() |
2a8d98307e | ||
![]() |
5aaf0cd579 | ||
![]() |
f38e4dcf54 | ||
![]() |
1df1ce5423 | ||
![]() |
a68381a4d9 | ||
![]() |
f7604b136e | ||
![]() |
362d950515 | ||
![]() |
22f9dbd65d | ||
![]() |
579050bfc7 | ||
![]() |
6b33b4e656 | ||
![]() |
dac7c0f5fd | ||
![]() |
aaceff0d23 | ||
![]() |
48e5cc6b63 | ||
![]() |
799a0933ba | ||
![]() |
940618f72d | ||
![]() |
d8ff69b65d | ||
![]() |
391aca6388 | ||
![]() |
071d078e84 | ||
![]() |
61982bcb77 | ||
![]() |
1c79fcc244 | ||
![]() |
f3513e3e52 | ||
![]() |
b3b10fa2ef | ||
![]() |
0ffabcc055 | ||
![]() |
9da8499004 | ||
![]() |
04d1fccb87 | ||
![]() |
fdf1eb5170 | ||
![]() |
4e2877b035 | ||
![]() |
467059f515 | ||
![]() |
e6db63bb63 | ||
![]() |
78ddec2c8c | ||
![]() |
9217d5bf40 | ||
![]() |
90d01e4b63 | ||
![]() |
e7960bf8c0 | ||
![]() |
bf12eaa1b3 | ||
![]() |
6179c75182 | ||
![]() |
4b5c7021ff | ||
![]() |
3349031cbd | ||
![]() |
5e107d43d7 | ||
![]() |
e46f2cd9bf | ||
![]() |
713ebfcc22 | ||
![]() |
46e4eafe95 | ||
![]() |
e6fd18e23b | ||
![]() |
71cd71dfd5 | ||
![]() |
1019ccfd26 | ||
![]() |
577c1d8522 | ||
![]() |
63f0b469cc | ||
![]() |
e688417863 | ||
![]() |
a19633e2d4 | ||
![]() |
8797142cca | ||
![]() |
2a7403b6fd | ||
![]() |
22efe14149 | ||
![]() |
7cce24bcd1 | ||
![]() |
b8f0bb66cd | ||
![]() |
b950f990b4 | ||
![]() |
b511e7a37d | ||
![]() |
50f4b78f2e | ||
![]() |
7b0b4cdfe4 | ||
![]() |
c60e5c4c61 | ||
![]() |
709a63e6da | ||
![]() |
f689eed073 | ||
![]() |
cd55eee2fc | ||
![]() |
40bb6566b8 | ||
![]() |
cf27e68748 | ||
![]() |
472ed2fe82 | ||
![]() |
d0a60984ed | ||
![]() |
24d401061c | ||
![]() |
2352d05573 | ||
![]() |
87d53e38c4 | ||
![]() |
db3c535884 | ||
![]() |
158b24f902 | ||
![]() |
19c4ed4690 | ||
![]() |
eae4ca1271 | ||
![]() |
db272e3e18 | ||
![]() |
0276430ab5 | ||
![]() |
db7caf1c32 | ||
![]() |
7176a51fec | ||
![]() |
4a6539d75b | ||
![]() |
850699ea70 | ||
![]() |
c17cc22f88 | ||
![]() |
9e3f2d5cb7 | ||
![]() |
0677c9c7b0 | ||
![]() |
af7e385884 | ||
![]() |
35496ead23 | ||
![]() |
ba88fef09b | ||
![]() |
ad0e59c8f4 | ||
![]() |
14e6f5e8ca | ||
![]() |
41403a5d35 | ||
![]() |
3c48157793 | ||
![]() |
3a07af6ad2 | ||
![]() |
c1c05f8d22 | ||
![]() |
29aed5371c | ||
![]() |
76c878df57 | ||
![]() |
8acf557137 | ||
![]() |
d6e7ebe71d | ||
![]() |
085b26d5ea | ||
![]() |
32472ca627 | ||
![]() |
c3c4bb4421 | ||
![]() |
f7f1a0c32d | ||
![]() |
d4872b177f | ||
![]() |
5bb8c51d25 | ||
![]() |
4e62370d18 | ||
![]() |
77c08fd00f | ||
![]() |
d8894a0078 | ||
![]() |
4fd9c63633 | ||
![]() |
5e1583f925 | ||
![]() |
5d5894cae6 | ||
![]() |
5417513f49 | ||
![]() |
546ba8f12f | ||
![]() |
a398b37380 | ||
![]() |
321f35f30e | ||
![]() |
82dfb06a04 | ||
![]() |
e666aac1bd | ||
![]() |
9e9a0e377e | ||
![]() |
ba3f9a318b | ||
![]() |
f3b4eefb72 | ||
![]() |
6ac1db6953 | ||
![]() |
1b42189dd6 | ||
![]() |
0d893b3d2b | ||
![]() |
7b167a4d7e | ||
![]() |
8e2f1026e7 | ||
![]() |
fe3a63af80 | ||
![]() |
f90ab60354 | ||
![]() |
5da4e1860a | ||
![]() |
6dcb7f2273 | ||
![]() |
53ae7e5a0c | ||
![]() |
56381f9914 | ||
![]() |
be31aecf00 | ||
![]() |
cc5fffc174 | ||
![]() |
dd8a50af31 | ||
![]() |
c8feded4f2 | ||
![]() |
0d0fe75f4e | ||
![]() |
fb69deb617 | ||
![]() |
c291af5d97 | ||
![]() |
6d63028406 | ||
![]() |
3917739ad2 | ||
![]() |
e98e59a265 | ||
![]() |
16ed60902d | ||
![]() |
6c7efc17c2 | ||
![]() |
d187aa0ac6 | ||
![]() |
9c60a047c1 | ||
![]() |
1825749036 | ||
![]() |
93846a2867 | ||
![]() |
f3ed0160af | ||
![]() |
38b275f7f9 | ||
![]() |
c3a36efaa4 | ||
![]() |
68fa67e77a | ||
![]() |
806cebb024 | ||
![]() |
fa788a8223 | ||
![]() |
dfbaee1649 | ||
![]() |
cfb698d0a6 | ||
![]() |
63c3d6406d | ||
![]() |
d817e92a57 | ||
![]() |
96597b3963 | ||
![]() |
40c7bc08d9 | ||
![]() |
b8cd1760f7 | ||
![]() |
24dd45c8cd | ||
![]() |
e06bd41b5e | ||
![]() |
c0793fad83 | ||
![]() |
e002c5d96c | ||
![]() |
099e317d17 | ||
![]() |
ca1a183512 | ||
![]() |
c1cacf735e | ||
![]() |
515cfdb6d1 | ||
![]() |
3a6cffd6c1 | ||
![]() |
c84a826937 | ||
![]() |
c485e8d03e | ||
![]() |
2ab67328d4 | ||
![]() |
d350c35c4e | ||
![]() |
034ce56da5 | ||
![]() |
ae9fcebfd5 | ||
![]() |
6197b55da8 | ||
![]() |
4e5d57b5f3 | ||
![]() |
7040c6d469 | ||
![]() |
6f99a39b55 | ||
![]() |
7483833dcd | ||
![]() |
38fb48b231 | ||
![]() |
166acee1c6 | ||
![]() |
916a6df39b | ||
![]() |
70f37158fb | ||
![]() |
5011bba20e | ||
![]() |
8897bc703d | ||
![]() |
ea6e7d441a | ||
![]() |
f91396c986 | ||
![]() |
4598b530af | ||
![]() |
dfabb4bc36 | ||
![]() |
66e0100c95 | ||
![]() |
a08a989ef5 | ||
![]() |
000c28abf9 | ||
![]() |
6b67397c83 | ||
![]() |
f68823a09e | ||
![]() |
fc1782e676 | ||
![]() |
b4975344a1 | ||
![]() |
2dc08d782f | ||
![]() |
ed92958735 | ||
![]() |
5ce31f3177 | ||
![]() |
370ec9cd98 | ||
![]() |
52c12b5659 | ||
![]() |
3de4cfbc00 | ||
![]() |
4215854414 | ||
![]() |
88eba92f57 | ||
![]() |
f773c968f9 | ||
![]() |
bbb6fccaec | ||
![]() |
aa2b2b0d16 | ||
![]() |
5cc06ebf0b | ||
![]() |
85977e505b | ||
![]() |
3249a5225f | ||
![]() |
7e7205627a | ||
![]() |
d33430e53f | ||
![]() |
e3f53e90e2 | ||
![]() |
811edfcc0f | ||
![]() |
2483249b5f | ||
![]() |
3534617f81 | ||
![]() |
216a3c4c7e | ||
![]() |
567bd9831f | ||
![]() |
98d1a55d35 | ||
![]() |
92358b4859 | ||
![]() |
eca3ec7f98 | ||
![]() |
bfcdbbd70b | ||
![]() |
e764076b1a | ||
![]() |
693c77ce1c | ||
![]() |
a725b6c9de | ||
![]() |
014bbf12ce | ||
![]() |
07dceb8e6d | ||
![]() |
53f18bec53 | ||
![]() |
ac3e858738 | ||
![]() |
c76b2fb357 | ||
![]() |
5f015ac9af | ||
![]() |
ac7c354bfc | ||
![]() |
dddee87de3 | ||
![]() |
e8bd77a84e | ||
![]() |
46a036ddbe | ||
![]() |
bf912f7bd3 | ||
![]() |
196c15ff3e | ||
![]() |
d0a6e727f2 | ||
![]() |
09697148cf | ||
![]() |
76093d898d | ||
![]() |
00c69c0fc3 | ||
![]() |
93dd119ce5 | ||
![]() |
e4f3211e9f | ||
![]() |
c6ecdc9d5d | ||
![]() |
6bdd2d234d | ||
![]() |
9d169bcbeb | ||
![]() |
5c06ec1084 | ||
![]() |
38a317b7e7 | ||
![]() |
593b176ab8 | ||
![]() |
1a15c8da8c | ||
![]() |
060e67397a | ||
![]() |
d6de29ca8a | ||
![]() |
220767b347 | ||
![]() |
79e1fbe076 | ||
![]() |
cd19894ab0 | ||
![]() |
705b6aeb4b | ||
![]() |
6e27fbe10f | ||
![]() |
bbb99a6eee | ||
![]() |
8411efc1c3 | ||
![]() |
88721df637 | ||
![]() |
265faddfa9 | ||
![]() |
6584dc70b7 | ||
![]() |
d6b4dbe6a2 | ||
![]() |
d579f93aa7 | ||
![]() |
b91261a789 | ||
![]() |
872128d9a8 | ||
![]() |
4972db4648 | ||
![]() |
821cd7fe05 | ||
![]() |
8c24ffa710 | ||
![]() |
d50a130345 | ||
![]() |
ee8997fbd2 | ||
![]() |
613cf932b5 | ||
![]() |
12b61aea2f | ||
![]() |
51d9271c83 | ||
![]() |
bd5264308f | ||
![]() |
2c17d2fead | ||
![]() |
9f0b9782a0 | ||
![]() |
88ff4c2fa8 | ||
![]() |
cba246fc7f | ||
![]() |
7d80eb06b0 | ||
![]() |
a181189a49 | ||
![]() |
626b51112f |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,9 +2,7 @@
|
|||||||
You are amazing! Thanks for contributing to our project!
|
You are amazing! Thanks for contributing to our project!
|
||||||
Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
|
Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Breaking change
|
## Breaking change
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
If your PR contains a breaking change for existing users, it is important
|
If your PR contains a breaking change for existing users, it is important
|
||||||
to tell them what breaks, how to make it work again and why we did this.
|
to tell them what breaks, how to make it work again and why we did this.
|
||||||
@@ -13,8 +11,8 @@
|
|||||||
Note: Remove this section if this PR is NOT a breaking change.
|
Note: Remove this section if this PR is NOT a breaking change.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Proposed change
|
|
||||||
|
|
||||||
|
## Proposed change
|
||||||
<!--
|
<!--
|
||||||
Describe the big picture of your changes here to communicate to the
|
Describe the big picture of your changes here to communicate to the
|
||||||
maintainers why we should accept this pull request. If it fixes a bug
|
maintainers why we should accept this pull request. If it fixes a bug
|
||||||
@@ -22,8 +20,8 @@
|
|||||||
in the additional information section.
|
in the additional information section.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Type of change
|
|
||||||
|
|
||||||
|
## Type of change
|
||||||
<!--
|
<!--
|
||||||
What type of change does your PR introduce to the Home Assistant frontend?
|
What type of change does your PR introduce to the Home Assistant frontend?
|
||||||
NOTE: Please, check only 1! box!
|
NOTE: Please, check only 1! box!
|
||||||
@@ -38,7 +36,6 @@
|
|||||||
- [ ] Code quality improvements to existing code or addition of tests
|
- [ ] Code quality improvements to existing code or addition of tests
|
||||||
|
|
||||||
## Example configuration
|
## Example configuration
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Supplying a configuration snippet, makes it easier for a maintainer to test
|
Supplying a configuration snippet, makes it easier for a maintainer to test
|
||||||
your PR.
|
your PR.
|
||||||
@@ -49,7 +46,6 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Additional information
|
## Additional information
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Details are important, and help maintainers processing your PR.
|
Details are important, and help maintainers processing your PR.
|
||||||
Please be sure to fill out additional details, if applicable.
|
Please be sure to fill out additional details, if applicable.
|
||||||
@@ -60,7 +56,6 @@
|
|||||||
- Link to documentation pull request:
|
- Link to documentation pull request:
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Put an `x` in the boxes that apply. You can also fill these out after
|
Put an `x` in the boxes that apply. You can also fill these out after
|
||||||
creating the PR. If you're unsure about any of them, don't hesitate to ask.
|
creating the PR. If you're unsure about any of them, don't hesitate to ask.
|
||||||
|
8
.github/workflows/cast_deployment.yaml
vendored
8
.github/workflows/cast_deployment.yaml
vendored
@@ -21,12 +21,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -57,12 +57,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
30
.github/workflows/ci.yaml
vendored
30
.github/workflows/ci.yaml
vendored
@@ -24,9 +24,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
- name: Build resources
|
- name: Build resources
|
||||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
||||||
- name: Setup lint cache
|
- name: Setup lint cache
|
||||||
uses: actions/cache@v3.3.1
|
uses: actions/cache@v3.3.2
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
node_modules/.cache/prettier
|
node_modules/.cache/prettier
|
||||||
@@ -55,9 +55,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -73,9 +73,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -85,15 +85,21 @@ jobs:
|
|||||||
run: ./node_modules/.bin/gulp build-app
|
run: ./node_modules/.bin/gulp build-app
|
||||||
env:
|
env:
|
||||||
IS_TEST: "true"
|
IS_TEST: "true"
|
||||||
|
- name: Upload bundle stats
|
||||||
|
uses: actions/upload-artifact@v3.1.3
|
||||||
|
with:
|
||||||
|
name: frontend-bundle-stats
|
||||||
|
path: build/stats/*.json
|
||||||
|
if-no-files-found: error
|
||||||
supervisor:
|
supervisor:
|
||||||
name: Build supervisor
|
name: Build supervisor
|
||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -103,3 +109,9 @@ jobs:
|
|||||||
run: ./node_modules/.bin/gulp build-hassio
|
run: ./node_modules/.bin/gulp build-hassio
|
||||||
env:
|
env:
|
||||||
IS_TEST: "true"
|
IS_TEST: "true"
|
||||||
|
- name: Upload bundle stats
|
||||||
|
uses: actions/upload-artifact@v3.1.3
|
||||||
|
with:
|
||||||
|
name: supervisor-bundle-stats
|
||||||
|
path: build/stats/*.json
|
||||||
|
if-no-files-found: error
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
|
8
.github/workflows/demo_deployment.yaml
vendored
8
.github/workflows/demo_deployment.yaml
vendored
@@ -22,12 +22,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -58,12 +58,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/design_deployment.yaml
vendored
4
.github/workflows/design_deployment.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/design_preview.yaml
vendored
4
.github/workflows/design_preview.yaml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
8
.github/workflows/nightly.yaml
vendored
8
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -57,14 +57,14 @@ jobs:
|
|||||||
run: tar -czvf translations.tar.gz translations
|
run: tar -czvf translations.tar.gz translations
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3.1.3
|
||||||
with:
|
with:
|
||||||
name: wheels
|
name: wheels
|
||||||
path: dist/home_assistant_frontend*.whl
|
path: dist/home_assistant_frontend*.whl
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload translations
|
- name: Upload translations
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3.1.3
|
||||||
with:
|
with:
|
||||||
name: translations
|
name: translations
|
||||||
path: translations.tar.gz
|
path: translations.tar.gz
|
||||||
|
25
.github/workflows/relative-ci.yaml
vendored
Normal file
25
.github/workflows/relative-ci.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: RelativeCI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: [CI]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload:
|
||||||
|
name: Upload stats
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
bundle: [frontend, supervisor]
|
||||||
|
build: [modern, legacy]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Send bundle stats and build information to RelativeCI
|
||||||
|
uses: relative-ci/agent-action@v2.1.10
|
||||||
|
with:
|
||||||
|
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
||||||
|
token: ${{ github.token }}
|
||||||
|
artifactName: ${{ format('{0}-bundle-stats', matrix.bundle) }}
|
||||||
|
webpackStatsFile: ${{ format('{0}-{1}.json', matrix.bundle, matrix.build) }}
|
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Verify version
|
- name: Verify version
|
||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.7.0
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: home-assistant/wheels@2023.04.0
|
uses: home-assistant/wheels@2023.10.5
|
||||||
with:
|
with:
|
||||||
abi: cp311
|
abi: cp311
|
||||||
tag: musllinux_1_2
|
tag: musllinux_1_2
|
||||||
|
2
.github/workflows/translations.yaml
vendored
2
.github/workflows/translations.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.5.3
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -47,3 +47,6 @@ src/cast/dev_const.ts
|
|||||||
|
|
||||||
# Home Assistant config
|
# Home Assistant config
|
||||||
/config/
|
/config/
|
||||||
|
|
||||||
|
# Jetbrains
|
||||||
|
/.idea/
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
CLA.md
|
CLA.md
|
||||||
CODE_OF_CONDUCT.md
|
CODE_OF_CONDUCT.md
|
||||||
LICENSE.md
|
LICENSE.md
|
||||||
|
PULL_REQUEST_TEMPLATE.md
|
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
File diff suppressed because one or more lines are too long
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
File diff suppressed because one or more lines are too long
874
.yarn/releases/yarn-3.6.1.cjs
vendored
874
.yarn/releases/yarn-3.6.1.cjs
vendored
File diff suppressed because one or more lines are too long
893
.yarn/releases/yarn-4.0.1.cjs
vendored
Executable file
893
.yarn/releases/yarn-4.0.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
12
.yarnrc.yml
12
.yarnrc.yml
@@ -1,11 +1,9 @@
|
|||||||
|
compressionLevel: mixed
|
||||||
|
|
||||||
defaultSemverRangePrefix: ""
|
defaultSemverRangePrefix: ""
|
||||||
|
|
||||||
|
enableGlobalCache: false
|
||||||
|
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
plugins:
|
yarnPath: .yarn/releases/yarn-4.0.1.cjs
|
||||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
|
||||||
spec: "@yarnpkg/plugin-typescript"
|
|
||||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
|
||||||
spec: "@yarnpkg/plugin-interactive-tools"
|
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-3.6.1.cjs
|
|
||||||
|
@@ -8,15 +8,11 @@ module.exports.sourceMapURL = () => {
|
|||||||
const ref = env.version().endsWith("dev")
|
const ref = env.version().endsWith("dev")
|
||||||
? process.env.GITHUB_SHA || "dev"
|
? process.env.GITHUB_SHA || "dev"
|
||||||
: env.version();
|
: env.version();
|
||||||
return `https://raw.githubusercontent.com/home-assistant/frontend/${ref}`;
|
return `https://raw.githubusercontent.com/home-assistant/frontend/${ref}/`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Files from NPM Packages that should not be imported
|
// Files from NPM Packages that should not be imported
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
module.exports.ignorePackages = () => [];
|
||||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
|
||||||
// Part of yaml.js and only used for !!js functions that we don't use
|
|
||||||
require.resolve("esprima"),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Files from NPM packages that we should replace with empty file
|
// Files from NPM packages that we should replace with empty file
|
||||||
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||||
@@ -98,8 +94,9 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
|||||||
"@babel/preset-env",
|
"@babel/preset-env",
|
||||||
{
|
{
|
||||||
useBuiltIns: latestBuild ? false : "entry",
|
useBuiltIns: latestBuild ? false : "entry",
|
||||||
corejs: latestBuild ? false : { version: "3.32", proposals: true },
|
corejs: latestBuild ? false : { version: "3.33", proposals: true },
|
||||||
bugfixes: true,
|
bugfixes: true,
|
||||||
|
shippedProposals: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"@babel/preset-typescript",
|
"@babel/preset-typescript",
|
||||||
@@ -148,7 +145,7 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
|||||||
sourceMaps: !isTestBuild,
|
sourceMaps: !isTestBuild,
|
||||||
});
|
});
|
||||||
|
|
||||||
const nameSuffix = (latestBuild) => (latestBuild ? "-latest" : "-es5");
|
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");
|
||||||
|
|
||||||
const outputPath = (outputRoot, latestBuild) =>
|
const outputPath = (outputRoot, latestBuild) =>
|
||||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||||
@@ -182,7 +179,7 @@ const publicPath = (latestBuild, root = "") =>
|
|||||||
module.exports.config = {
|
module.exports.config = {
|
||||||
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
||||||
return {
|
return {
|
||||||
name: "app" + nameSuffix(latestBuild),
|
name: "frontend" + nameSuffix(latestBuild),
|
||||||
entry: {
|
entry: {
|
||||||
service_worker: "./src/entrypoints/service_worker.ts",
|
service_worker: "./src/entrypoints/service_worker.ts",
|
||||||
app: "./src/entrypoints/app.ts",
|
app: "./src/entrypoints/app.ts",
|
||||||
|
@@ -45,8 +45,8 @@ gulp.task(
|
|||||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||||
|
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod"),
|
||||||
// Don't compress running tests
|
// Don't compress running tests
|
||||||
...(env.isTestBuild() ? [] : ["compress-app"]),
|
...(env.isTestBuild() ? [] : ["compress-app"])
|
||||||
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod")
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
|
import path from "path";
|
||||||
import mapStream from "map-stream";
|
import mapStream from "map-stream";
|
||||||
import transform from "gulp-json-transform";
|
import transform from "gulp-json-transform";
|
||||||
|
import { LokaliseApi } from "@lokalise/node-api";
|
||||||
|
import JSZip from "jszip";
|
||||||
|
|
||||||
const inDirFrontend = "translations/frontend";
|
const inDir = "translations";
|
||||||
const inDirBackend = "translations/backend";
|
const inDirFrontend = `${inDir}/frontend`;
|
||||||
|
const inDirBackend = `${inDir}/backend`;
|
||||||
const srcMeta = "src/translations/translationMetadata.json";
|
const srcMeta = "src/translations/translationMetadata.json";
|
||||||
const encoding = "utf8";
|
const encoding = "utf8";
|
||||||
|
|
||||||
@@ -68,8 +72,9 @@ gulp.task("convert-backend-translations", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("check-translations-html", function () {
|
gulp.task("check-translations-html", function () {
|
||||||
// We exclude backend translations because they are not compliant with the HTML rule for now
|
return gulp
|
||||||
return gulp.src([`${inDirFrontend}/*.json`]).pipe(checkHtml());
|
.src([`${inDirFrontend}/*.json`, `${inDirBackend}/*.json`])
|
||||||
|
.pipe(checkHtml());
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("check-all-files-exist", async function () {
|
gulp.task("check-all-files-exist", async function () {
|
||||||
@@ -89,7 +94,83 @@ gulp.task("check-all-files-exist", async function () {
|
|||||||
await Promise.allSettled(writings);
|
await Promise.allSettled(writings);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const lokaliseProjects = {
|
||||||
|
backend: "130246255a974bd3b5e8a1.51616605",
|
||||||
|
frontend: "3420425759f6d6d241f598.13594006",
|
||||||
|
};
|
||||||
|
|
||||||
|
gulp.task("fetch-lokalise", async function () {
|
||||||
|
let apiKey;
|
||||||
|
try {
|
||||||
|
apiKey =
|
||||||
|
process.env.LOKALISE_TOKEN ||
|
||||||
|
(await fs.readFile(".lokalise_token", { encoding }));
|
||||||
|
} catch {
|
||||||
|
throw new Error(
|
||||||
|
"An Administrator Lokalise API token is required to download the latest set of translations. Place your token in a new file `.lokalise_token` in the repo root directory."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const lokaliseApi = new LokaliseApi({ apiKey });
|
||||||
|
|
||||||
|
const mkdirPromise = Promise.all([
|
||||||
|
fs.mkdir(inDirFrontend, { recursive: true }),
|
||||||
|
fs.mkdir(inDirBackend, { recursive: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
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",
|
||||||
|
})
|
||||||
|
.then((download) => fetch(download.bundle_url))
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 200 || response.status === 0) {
|
||||||
|
return response.arrayBuffer();
|
||||||
|
}
|
||||||
|
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 }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"check-downloaded-translations",
|
"download-translations",
|
||||||
gulp.series("check-translations-html", "check-all-files-exist")
|
gulp.series(
|
||||||
|
"fetch-lokalise",
|
||||||
|
"convert-backend-translations",
|
||||||
|
"check-translations-html",
|
||||||
|
"check-all-files-exist"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
@@ -4,6 +4,7 @@ import fs from "fs-extra";
|
|||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import paths from "../paths.cjs";
|
import paths from "../paths.cjs";
|
||||||
|
import env from "../env.cjs";
|
||||||
|
|
||||||
const npmPath = (...parts) =>
|
const npmPath = (...parts) =>
|
||||||
path.resolve(paths.polymer_dir, "node_modules", ...parts);
|
path.resolve(paths.polymer_dir, "node_modules", ...parts);
|
||||||
@@ -62,6 +63,9 @@ function copyPolyfills(staticDir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function copyLoaderJS(staticDir) {
|
function copyLoaderJS(staticDir) {
|
||||||
|
if (!env.useRollup()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
|
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
|
||||||
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
|
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
|
||||||
|
@@ -1,51 +1,54 @@
|
|||||||
import { deleteSync } from "del";
|
import { deleteSync } from "del";
|
||||||
import { mkdir, readFile, writeFile } from "fs/promises";
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
import path from "path";
|
import { join, resolve } from "node:path";
|
||||||
import paths from "../paths.cjs";
|
import paths from "../paths.cjs";
|
||||||
|
|
||||||
const outDir = path.join(paths.build_dir, "locale-data");
|
const formatjsDir = join(paths.polymer_dir, "node_modules", "@formatjs");
|
||||||
|
const outDir = join(paths.build_dir, "locale-data");
|
||||||
|
|
||||||
const INTL_PACKAGES = {
|
const INTL_POLYFILLS = {
|
||||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
|
||||||
"intl-datetimeformat": "DateTimeFormat",
|
"intl-datetimeformat": "DateTimeFormat",
|
||||||
"intl-numberformat": "NumberFormat",
|
|
||||||
"intl-displaynames": "DisplayNames",
|
"intl-displaynames": "DisplayNames",
|
||||||
"intl-listformat": "ListFormat",
|
"intl-listformat": "ListFormat",
|
||||||
|
"intl-numberformat": "NumberFormat",
|
||||||
|
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||||
};
|
};
|
||||||
|
|
||||||
const convertToJSON = async (pkg, lang) => {
|
const convertToJSON = async (
|
||||||
|
pkg,
|
||||||
|
lang,
|
||||||
|
subDir = "locale-data",
|
||||||
|
addFunc = "__addLocaleData",
|
||||||
|
skipMissing = true
|
||||||
|
) => {
|
||||||
let localeData;
|
let localeData;
|
||||||
try {
|
try {
|
||||||
localeData = await readFile(
|
localeData = await readFile(
|
||||||
path.resolve(
|
join(formatjsDir, pkg, subDir, `${lang}.js`),
|
||||||
paths.polymer_dir,
|
|
||||||
`node_modules/@formatjs/${pkg}/locale-data/${lang}.js`
|
|
||||||
),
|
|
||||||
"utf-8"
|
"utf-8"
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore if language is missing (i.e. not supported by @formatjs)
|
// Ignore if language is missing (i.e. not supported by @formatjs)
|
||||||
if (e.code === "ENOENT") {
|
if (e.code === "ENOENT" && skipMissing) {
|
||||||
|
console.warn(`Skipped missing data for language ${lang} from ${pkg}`);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
// Convert to JSON
|
// Convert to JSON
|
||||||
const className = INTL_PACKAGES[pkg];
|
const obj = INTL_POLYFILLS[pkg];
|
||||||
localeData = localeData
|
const dataRegex = new RegExp(
|
||||||
.replace(
|
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
|
||||||
new RegExp(
|
"s"
|
||||||
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
);
|
||||||
"im"
|
localeData = localeData.match(dataRegex)?.groups?.data;
|
||||||
),
|
if (!localeData) {
|
||||||
""
|
throw Error(`Failed to extract data for language ${lang} from ${pkg}`);
|
||||||
)
|
}
|
||||||
.replace(/\)\s*}/im, "");
|
|
||||||
// Parse to validate JSON, then stringify to minify
|
// Parse to validate JSON, then stringify to minify
|
||||||
localeData = JSON.stringify(JSON.parse(localeData));
|
localeData = JSON.stringify(JSON.parse(localeData));
|
||||||
await writeFile(path.join(outDir, `${pkg}/${lang}.json`), localeData);
|
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||||
};
|
};
|
||||||
|
|
||||||
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
||||||
@@ -53,17 +56,27 @@ gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
|||||||
gulp.task("create-locale-data", async () => {
|
gulp.task("create-locale-data", async () => {
|
||||||
const translationMeta = JSON.parse(
|
const translationMeta = JSON.parse(
|
||||||
await readFile(
|
await readFile(
|
||||||
path.resolve(paths.translations_src, "translationMetadata.json"),
|
resolve(paths.translations_src, "translationMetadata.json"),
|
||||||
"utf-8"
|
"utf-8"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const conversions = [];
|
const conversions = [];
|
||||||
for (const pkg of Object.keys(INTL_PACKAGES)) {
|
for (const pkg of Object.keys(INTL_POLYFILLS)) {
|
||||||
await mkdir(path.join(outDir, pkg), { recursive: true });
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await mkdir(join(outDir, pkg), { recursive: true });
|
||||||
for (const lang of Object.keys(translationMeta)) {
|
for (const lang of Object.keys(translationMeta)) {
|
||||||
conversions.push(convertToJSON(pkg, lang));
|
conversions.push(convertToJSON(pkg, lang));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
conversions.push(
|
||||||
|
convertToJSON(
|
||||||
|
"intl-datetimeformat",
|
||||||
|
"add-all-tz",
|
||||||
|
".",
|
||||||
|
"__addTZData",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
await Promise.all(conversions);
|
await Promise.all(conversions);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,12 +1,7 @@
|
|||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
import { deleteSync } from "del";
|
import { deleteSync } from "del";
|
||||||
import {
|
import { mkdirSync, readdirSync, readFileSync, renameSync } from "fs";
|
||||||
mkdirSync,
|
import { writeFile } from "node:fs/promises";
|
||||||
readdirSync,
|
|
||||||
readFileSync,
|
|
||||||
renameSync,
|
|
||||||
writeFile,
|
|
||||||
} from "fs";
|
|
||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
import flatmap from "gulp-flatmap";
|
import flatmap from "gulp-flatmap";
|
||||||
import transform from "gulp-json-transform";
|
import transform from "gulp-json-transform";
|
||||||
@@ -136,27 +131,23 @@ gulp.task("ensure-translations-build-dir", async () => {
|
|||||||
mkdirSync(workDir, { recursive: true });
|
mkdirSync(workDir, { recursive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("create-test-metadata", (cb) => {
|
gulp.task("create-test-metadata", () =>
|
||||||
writeFile(
|
env.isProdBuild()
|
||||||
workDir + "/testMetadata.json",
|
? Promise.resolve()
|
||||||
JSON.stringify({
|
: writeFile(
|
||||||
test: {
|
workDir + "/testMetadata.json",
|
||||||
nativeName: "Test",
|
JSON.stringify({ test: { nativeName: "Test" } })
|
||||||
},
|
)
|
||||||
}),
|
);
|
||||||
cb
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("create-test-translation", () =>
|
||||||
"create-test-translation",
|
env.isProdBuild()
|
||||||
gulp.series("create-test-metadata", () =>
|
? Promise.resolve()
|
||||||
gulp
|
: gulp
|
||||||
.src(path.join(paths.translations_src, "en.json"))
|
.src(path.join(paths.translations_src, "en.json"))
|
||||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||||
.pipe(rename("test.json"))
|
.pipe(rename("test.json"))
|
||||||
.pipe(gulp.dest(workDir))
|
.pipe(gulp.dest(workDir))
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -188,16 +179,11 @@ gulp.task("build-master-translation", () => {
|
|||||||
|
|
||||||
gulp.task("build-merged-translations", () =>
|
gulp.task("build-merged-translations", () =>
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src([
|
||||||
[
|
inFrontendDir + "/*.json",
|
||||||
inFrontendDir + "/*.json",
|
"!" + inFrontendDir + "/en.json",
|
||||||
"!" + inFrontendDir + "/en.json",
|
...(env.isProdBuild() ? [] : [workDir + "/test.json"]),
|
||||||
workDir + "/test.json",
|
])
|
||||||
],
|
|
||||||
{
|
|
||||||
allowEmpty: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||||
.pipe(
|
.pipe(
|
||||||
flatmap((stream, file) => {
|
flatmap((stream, file) => {
|
||||||
@@ -377,14 +363,11 @@ gulp.task("build-translation-flatten-supervisor", () =>
|
|||||||
|
|
||||||
gulp.task("build-translation-write-metadata", () =>
|
gulp.task("build-translation-write-metadata", () =>
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src([
|
||||||
[
|
path.join(paths.translations_src, "translationMetadata.json"),
|
||||||
path.join(paths.translations_src, "translationMetadata.json"),
|
...(env.isProdBuild() ? [] : [workDir + "/testMetadata.json"]),
|
||||||
workDir + "/testMetadata.json",
|
workDir + "/translationFingerprints.json",
|
||||||
workDir + "/translationFingerprints.json",
|
])
|
||||||
],
|
|
||||||
{ allowEmpty: true }
|
|
||||||
)
|
|
||||||
.pipe(merge({}))
|
.pipe(merge({}))
|
||||||
.pipe(
|
.pipe(
|
||||||
transform((data) => {
|
transform((data) => {
|
||||||
@@ -415,7 +398,7 @@ gulp.task("build-translation-write-metadata", () =>
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"create-translations",
|
"create-translations",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
...(env.isProdBuild() ? [] : ["create-test-translation"]),
|
gulp.parallel("create-test-metadata", "create-test-translation"),
|
||||||
"build-master-translation",
|
"build-master-translation",
|
||||||
"build-merged-translations",
|
"build-merged-translations",
|
||||||
gulp.parallel(...splitTasks),
|
gulp.parallel(...splitTasks),
|
||||||
|
@@ -6,6 +6,8 @@ import presetEnv from "@babel/preset-env";
|
|||||||
import compilationTargets from "@babel/helper-compilation-targets";
|
import compilationTargets from "@babel/helper-compilation-targets";
|
||||||
import coreJSCompat from "core-js-compat";
|
import coreJSCompat from "core-js-compat";
|
||||||
import { logPlugin } from "@babel/preset-env/lib/debug.js";
|
import { logPlugin } from "@babel/preset-env/lib/debug.js";
|
||||||
|
// eslint-disable-next-line import/no-relative-packages
|
||||||
|
import shippedPolyfills from "../node_modules/babel-plugin-polyfill-corejs3/lib/shipped-proposals.js";
|
||||||
import { babelOptions } from "./bundle.cjs";
|
import { babelOptions } from "./bundle.cjs";
|
||||||
|
|
||||||
const detailsOpen = (heading) =>
|
const detailsOpen = (heading) =>
|
||||||
@@ -26,6 +28,22 @@ const dummyAPI = {
|
|||||||
targets: () => ({}),
|
targets: () => ({}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Generate filter function based on proposal/method inputs
|
||||||
|
// Copied and adapted from babel-plugin-polyfill-corejs3/esm/index.mjs
|
||||||
|
const polyfillFilter = (method, proposals, shippedProposals) => (name) => {
|
||||||
|
if (proposals || method === "entry-global") return true;
|
||||||
|
if (shippedProposals && shippedPolyfills.default.has(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name.startsWith("esnext.")) {
|
||||||
|
const esName = `es.${name.slice(7)}`;
|
||||||
|
// If its imaginative esName is not in latest compat data, it means the proposal is not stage 4
|
||||||
|
return esName in coreJSCompat.data;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log the plugins and polyfills for each build environment
|
||||||
for (const buildType of ["Modern", "Legacy"]) {
|
for (const buildType of ["Modern", "Legacy"]) {
|
||||||
const browserslistEnv = buildType.toLowerCase();
|
const browserslistEnv = buildType.toLowerCase();
|
||||||
const babelOpts = babelOptions({ latestBuild: browserslistEnv === "modern" });
|
const babelOpts = babelOptions({ latestBuild: browserslistEnv === "modern" });
|
||||||
@@ -46,7 +64,13 @@ for (const buildType of ["Modern", "Legacy"]) {
|
|||||||
const targets = compilationTargets.default(babelOpts?.targets, {
|
const targets = compilationTargets.default(babelOpts?.targets, {
|
||||||
browserslistEnv,
|
browserslistEnv,
|
||||||
});
|
});
|
||||||
const polyfillList = coreJSCompat({ targets }).list;
|
const polyfillList = coreJSCompat({ targets }).list.filter(
|
||||||
|
polyfillFilter(
|
||||||
|
`${presetEnvOpts.useBuiltIns}-global`,
|
||||||
|
presetEnvOpts?.corejs?.proposals,
|
||||||
|
presetEnvOpts?.shippedProposals
|
||||||
|
)
|
||||||
|
);
|
||||||
console.log(
|
console.log(
|
||||||
"The following %i polyfills may be injected by Babel:\n",
|
"The following %i polyfills may be injected by Babel:\n",
|
||||||
polyfillList.length
|
polyfillList.length
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
const webpack = require("webpack");
|
const { existsSync } = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
const { StatsWriterPlugin } = require("webpack-stats-plugin");
|
||||||
|
const filterStats = require("@bundle-stats/plugin-webpack-filter").default;
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||||
const log = require("fancy-log");
|
const log = require("fancy-log");
|
||||||
@@ -151,6 +154,15 @@ const createWebpackConfig = ({
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
!isProdBuild && new LogStartCompilePlugin(),
|
!isProdBuild && new LogStartCompilePlugin(),
|
||||||
|
isProdBuild &&
|
||||||
|
new StatsWriterPlugin({
|
||||||
|
filename: path.relative(
|
||||||
|
outputPath,
|
||||||
|
path.join(paths.build_dir, "stats", `${name}.json`)
|
||||||
|
),
|
||||||
|
stats: { assets: true, chunks: true, modules: true },
|
||||||
|
transform: (stats) => JSON.stringify(filterStats(stats)),
|
||||||
|
}),
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".ts", ".js", ".json"],
|
extensions: [".ts", ".js", ".json"],
|
||||||
@@ -164,11 +176,14 @@ const createWebpackConfig = ({
|
|||||||
"lit/directives/guard$": "lit/directives/guard.js",
|
"lit/directives/guard$": "lit/directives/guard.js",
|
||||||
"lit/directives/cache$": "lit/directives/cache.js",
|
"lit/directives/cache$": "lit/directives/cache.js",
|
||||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||||
|
"lit/directives/live$": "lit/directives/live.js",
|
||||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||||
"@lit-labs/virtualizer/layouts/grid":
|
"@lit-labs/virtualizer/layouts/grid":
|
||||||
"@lit-labs/virtualizer/layouts/grid.js",
|
"@lit-labs/virtualizer/layouts/grid.js",
|
||||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
||||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
||||||
|
"@lit-labs/observers/resize-controller":
|
||||||
|
"@lit-labs/observers/resize-controller.js",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@@ -181,6 +196,7 @@ const createWebpackConfig = ({
|
|||||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
|
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
|
||||||
assetModuleFilename:
|
assetModuleFilename:
|
||||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
|
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
|
||||||
|
crossOriginLoading: "use-credentials",
|
||||||
hashFunction: "xxhash64",
|
hashFunction: "xxhash64",
|
||||||
hashDigest: "base64url",
|
hashDigest: "base64url",
|
||||||
hashDigestLength: 11, // full length of 64 bit base64url
|
hashDigestLength: 11, // full length of 64 bit base64url
|
||||||
@@ -191,19 +207,26 @@ const createWebpackConfig = ({
|
|||||||
// Since production source maps don't include sources, we need to point to them elsewhere
|
// Since production source maps don't include sources, we need to point to them elsewhere
|
||||||
// For dependencies, just provide the path (no source in browser)
|
// For dependencies, just provide the path (no source in browser)
|
||||||
// Otherwise, point to the raw code on GitHub for browser to load
|
// Otherwise, point to the raw code on GitHub for browser to load
|
||||||
devtoolModuleFilenameTemplate:
|
...Object.fromEntries(
|
||||||
!isTestBuild && isProdBuild
|
["", "Fallback"].map((v) => [
|
||||||
? (info) => {
|
`devtool${v}ModuleFilenameTemplate`,
|
||||||
const sourcePath = info.resourcePath.replace(/^\.\//, "");
|
!isTestBuild && isProdBuild
|
||||||
if (
|
? (info) => {
|
||||||
sourcePath.startsWith("node_modules") ||
|
if (
|
||||||
sourcePath.startsWith("webpack")
|
!path.isAbsolute(info.absoluteResourcePath) ||
|
||||||
) {
|
!existsSync(info.resourcePath) ||
|
||||||
return `no-source/${sourcePath}`;
|
info.resourcePath.startsWith("./node_modules")
|
||||||
|
) {
|
||||||
|
// Source URLs are unknown for dependencies, so we use a relative URL with a
|
||||||
|
// non - existent top directory. This results in a clean source tree in browser
|
||||||
|
// dev tools, and they stay happy getting 404s with valid requests.
|
||||||
|
return `/unknown${path.resolve("/", info.resourcePath)}`;
|
||||||
|
}
|
||||||
|
return new URL(info.resourcePath, bundle.sourceMapURL()).href;
|
||||||
}
|
}
|
||||||
return `${bundle.sourceMapURL()}/${sourcePath}`;
|
: undefined,
|
||||||
}
|
])
|
||||||
: undefined,
|
),
|
||||||
},
|
},
|
||||||
experiments: {
|
experiments: {
|
||||||
outputModule: true,
|
outputModule: true,
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import "../../../src/resources/safari-14-attachshadow-patch";
|
import "../../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "../../../src/resources/ha-style";
|
|
||||||
import "../../../src/resources/roboto";
|
|
||||||
import "./layout/hc-connect";
|
import "./layout/hc-connect";
|
||||||
|
|
||||||
|
import("../../../src/resources/ha-style");
|
||||||
|
@@ -3,7 +3,7 @@ import { mdiCast, mdiCastConnected } from "@mdi/js";
|
|||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||||
import {
|
import {
|
||||||
@@ -22,8 +22,9 @@ import "../../../../src/components/ha-svg-icon";
|
|||||||
import {
|
import {
|
||||||
getLegacyLovelaceCollection,
|
getLegacyLovelaceCollection,
|
||||||
getLovelaceCollection,
|
getLovelaceCollection,
|
||||||
LovelaceConfig,
|
|
||||||
} from "../../../../src/data/lovelace";
|
} from "../../../../src/data/lovelace";
|
||||||
|
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
|
||||||
|
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
|
||||||
import "../../../../src/layouts/hass-loading-screen";
|
import "../../../../src/layouts/hass-loading-screen";
|
||||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||||
import "./hc-layout";
|
import "./hc-layout";
|
||||||
@@ -38,10 +39,10 @@ class HcCast extends LitElement {
|
|||||||
|
|
||||||
@state() private askWrite = false;
|
@state() private askWrite = false;
|
||||||
|
|
||||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (this.lovelaceConfig === undefined) {
|
if (this.lovelaceViews === undefined) {
|
||||||
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,9 +87,10 @@ class HcCast extends LitElement {
|
|||||||
attr-for-selected="data-path"
|
attr-for-selected="data-path"
|
||||||
.selected=${this.castManager.status.lovelacePath || ""}
|
.selected=${this.castManager.status.lovelacePath || ""}
|
||||||
>
|
>
|
||||||
${(this.lovelaceConfig
|
${(
|
||||||
? this.lovelaceConfig.views
|
this.lovelaceViews ?? [
|
||||||
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
|
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
|
||||||
|
]
|
||||||
).map(
|
).map(
|
||||||
(view, idx) => html`
|
(view, idx) => html`
|
||||||
<paper-icon-item
|
<paper-icon-item
|
||||||
@@ -136,11 +138,15 @@ class HcCast extends LitElement {
|
|||||||
llColl.refresh().then(
|
llColl.refresh().then(
|
||||||
() => {
|
() => {
|
||||||
llColl.subscribe((config) => {
|
llColl.subscribe((config) => {
|
||||||
this.lovelaceConfig = config;
|
if (isStrategyDashboard(config)) {
|
||||||
|
this.lovelaceViews = null;
|
||||||
|
} else {
|
||||||
|
this.lovelaceViews = config.views;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
this.lovelaceConfig = null;
|
this.lovelaceViews = null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -159,9 +165,7 @@ class HcCast extends LitElement {
|
|||||||
toggleAttribute(
|
toggleAttribute(
|
||||||
this,
|
this,
|
||||||
"hide-icons",
|
"hide-icons",
|
||||||
this.lovelaceConfig
|
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
|
||||||
? !this.lovelaceConfig.views.some((view) => view.icon)
|
|
||||||
: true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
import {
|
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
|
||||||
LovelaceCardConfig,
|
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||||
LovelaceConfig,
|
|
||||||
} from "../../../../src/data/lovelace";
|
|
||||||
import { castContext } from "../cast_context";
|
import { castContext } from "../cast_context";
|
||||||
|
|
||||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { html, nothing } from "lit";
|
import { html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||||
import {
|
import {
|
||||||
MockHomeAssistant,
|
MockHomeAssistant,
|
||||||
provideHass,
|
provideHass,
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||||
import "../../../../src/panels/lovelace/views/hui-view";
|
import "../../../../src/panels/lovelace/views/hui-view";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
@@ -14,7 +14,8 @@ import "./hc-launch-screen";
|
|||||||
class HcLovelace extends LitElement {
|
class HcLovelace extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
|
@property({ attribute: false })
|
||||||
|
public lovelaceConfig!: LovelaceConfig;
|
||||||
|
|
||||||
@property() public viewPath?: string | number;
|
@property() public viewPath?: string | number;
|
||||||
|
|
||||||
|
@@ -21,17 +21,27 @@ import {
|
|||||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||||
import {
|
import {
|
||||||
fetchResources,
|
|
||||||
getLegacyLovelaceCollection,
|
getLegacyLovelaceCollection,
|
||||||
getLovelaceCollection,
|
getLovelaceCollection,
|
||||||
|
} from "../../../../src/data/lovelace";
|
||||||
|
import {
|
||||||
|
isStrategyDashboard,
|
||||||
LegacyLovelaceConfig,
|
LegacyLovelaceConfig,
|
||||||
LovelaceConfig,
|
LovelaceConfig,
|
||||||
} from "../../../../src/data/lovelace";
|
LovelaceDashboardStrategyConfig,
|
||||||
|
} from "../../../../src/data/lovelace/config/types";
|
||||||
|
import { fetchResources } from "../../../../src/data/lovelace/resource";
|
||||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||||
import { HassElement } from "../../../../src/state/hass-element";
|
import { HassElement } from "../../../../src/state/hass-element";
|
||||||
import { castContext } from "../cast_context";
|
import { castContext } from "../cast_context";
|
||||||
import "./hc-launch-screen";
|
import "./hc-launch-screen";
|
||||||
|
|
||||||
|
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
|
||||||
|
strategy: {
|
||||||
|
type: "original-states",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let resourcesLoaded = false;
|
let resourcesLoaded = false;
|
||||||
@customElement("hc-main")
|
@customElement("hc-main")
|
||||||
export class HcMain extends HassElement {
|
export class HcMain extends HassElement {
|
||||||
@@ -91,14 +101,16 @@ export class HcMain extends HassElement {
|
|||||||
.lovelaceConfig=${this._lovelaceConfig}
|
.lovelaceConfig=${this._lovelaceConfig}
|
||||||
.viewPath=${this._lovelacePath}
|
.viewPath=${this._lovelacePath}
|
||||||
.urlPath=${this._urlPath}
|
.urlPath=${this._urlPath}
|
||||||
@config-refresh=${this._generateLovelaceConfig}
|
@config-refresh=${this._generateDefaultLovelaceConfig}
|
||||||
></hc-lovelace>
|
></hc-lovelace>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
import("../second-load");
|
import("./hc-lovelace");
|
||||||
|
import("../../../../src/resources/ha-style");
|
||||||
|
|
||||||
window.addEventListener("location-changed", () => {
|
window.addEventListener("location-changed", () => {
|
||||||
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
||||||
if (location.pathname.startsWith(panelPath)) {
|
if (location.pathname.startsWith(panelPath)) {
|
||||||
@@ -258,7 +270,6 @@ export class HcMain extends HassElement {
|
|||||||
{
|
{
|
||||||
strategy: {
|
strategy: {
|
||||||
type: "energy",
|
type: "energy",
|
||||||
options: { show_date_selection: true },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -281,9 +292,20 @@ export class HcMain extends HassElement {
|
|||||||
// configuration.
|
// configuration.
|
||||||
try {
|
try {
|
||||||
await llColl.refresh();
|
await llColl.refresh();
|
||||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
||||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
if (isStrategyDashboard(rawConfig)) {
|
||||||
);
|
const { generateLovelaceDashboardStrategy } = await import(
|
||||||
|
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||||
|
);
|
||||||
|
const config = await generateLovelaceDashboardStrategy(
|
||||||
|
rawConfig.strategy,
|
||||||
|
this.hass!
|
||||||
|
);
|
||||||
|
this._handleNewLovelaceConfig(config);
|
||||||
|
} else {
|
||||||
|
this._handleNewLovelaceConfig(rawConfig);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (
|
if (
|
||||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
||||||
@@ -297,7 +319,7 @@ export class HcMain extends HassElement {
|
|||||||
}
|
}
|
||||||
// Generate a Lovelace config.
|
// Generate a Lovelace config.
|
||||||
this._unsubLovelace = () => undefined;
|
this._unsubLovelace = () => undefined;
|
||||||
await this._generateLovelaceConfig();
|
await this._generateDefaultLovelaceConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!resourcesLoaded) {
|
if (!resourcesLoaded) {
|
||||||
@@ -306,24 +328,21 @@ export class HcMain extends HassElement {
|
|||||||
? await fetchResources(this.hass!.connection)
|
? await fetchResources(this.hass!.connection)
|
||||||
: (this._lovelaceConfig as LegacyLovelaceConfig).resources;
|
: (this._lovelaceConfig as LegacyLovelaceConfig).resources;
|
||||||
if (resources) {
|
if (resources) {
|
||||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
loadLovelaceResources(resources, this.hass!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sendStatus();
|
this._sendStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _generateLovelaceConfig() {
|
private async _generateDefaultLovelaceConfig() {
|
||||||
const { generateLovelaceDashboardStrategy } = await import(
|
const { generateLovelaceDashboardStrategy } = await import(
|
||||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||||
);
|
);
|
||||||
this._handleNewLovelaceConfig(
|
this._handleNewLovelaceConfig(
|
||||||
await generateLovelaceDashboardStrategy(
|
await generateLovelaceDashboardStrategy(
|
||||||
{
|
DEFAULT_CONFIG.strategy,
|
||||||
hass: this.hass!,
|
this.hass!
|
||||||
narrow: false,
|
|
||||||
},
|
|
||||||
"original-states"
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
import "../../../src/resources/ha-style";
|
|
||||||
import "../../../src/resources/roboto";
|
|
||||||
import "./layout/hc-lovelace";
|
|
@@ -8,25 +8,67 @@
|
|||||||
"src": "/static/icons/favicon-192x192.png",
|
"src": "/static/icons/favicon-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/icons/favicon-384x384.png",
|
"src": "/static/icons/favicon-384x384.png",
|
||||||
"sizes": "384x384",
|
"sizes": "384x384",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/icons/favicon-512x512.png",
|
"src": "/static/icons/favicon-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/icons/favicon-1024x1024.png",
|
"src": "/static/icons/favicon-1024x1024.png",
|
||||||
"sizes": "1024x1024",
|
"sizes": "1024x1024",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-48x48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-128x128.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lang": "en-US",
|
"lang": "en-US",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"zone.home": {
|
"zone.home": {
|
||||||
entity_id: "zone.home",
|
entity_id: "zone.home",
|
||||||
state: "zoning",
|
state: "zoning",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"zone.powertec": {
|
"zone.powertec": {
|
||||||
entity_id: "zone.powertec",
|
entity_id: "zone.powertec",
|
||||||
state: "zoning",
|
state: "zoning",
|
||||||
|
@@ -4,16 +4,11 @@ export const demoThemeJimpower = () => ({
|
|||||||
"primary-color": "#5294E2",
|
"primary-color": "#5294E2",
|
||||||
"label-badge-red": "var(--accent-color)",
|
"label-badge-red": "var(--accent-color)",
|
||||||
"paper-tabs-selection-bar-color": "green",
|
"paper-tabs-selection-bar-color": "green",
|
||||||
"paper-slider-knob-color": "var(--accent-color)",
|
|
||||||
"light-primary-color": "var(--accent-color)",
|
"light-primary-color": "var(--accent-color)",
|
||||||
"primary-background-color": "#383C45",
|
"primary-background-color": "#383C45",
|
||||||
"primary-text-color": "#FFFFFF",
|
"primary-text-color": "#FFFFFF",
|
||||||
"paper-item-selected_-_background-color": "#434954",
|
"paper-item-selected_-_background-color": "#434954",
|
||||||
"paper-slider-active-color": "var(--accent-color)",
|
|
||||||
"secondary-background-color": "#383C45",
|
"secondary-background-color": "#383C45",
|
||||||
"paper-slider-container-color":
|
|
||||||
"linear-gradient(var(--primary-background-color), var(--secondary-background-color)) no-repeat",
|
|
||||||
"paper-slider-disabled-active-color": "var(--disabled-text-color)",
|
|
||||||
"disabled-text-color": "#7F848E",
|
"disabled-text-color": "#7F848E",
|
||||||
"paper-item-icon_-_color": "green",
|
"paper-item-icon_-_color": "green",
|
||||||
"paper-grey-200": "#414A59",
|
"paper-grey-200": "#414A59",
|
||||||
@@ -32,14 +27,10 @@ export const demoThemeJimpower = () => ({
|
|||||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||||
"label-badge-border-color": "green",
|
"label-badge-border-color": "green",
|
||||||
"paper-listbox-color": "var(--primary-color)",
|
"paper-listbox-color": "var(--primary-color)",
|
||||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
|
||||||
"card-background-color": "#434954",
|
"card-background-color": "#434954",
|
||||||
"label-badge-text-color": "var(--primary-text-color)",
|
"label-badge-text-color": "var(--primary-text-color)",
|
||||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
|
||||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||||
"dark-primary-color": "var(--accent-color)",
|
"dark-primary-color": "var(--accent-color)",
|
||||||
"paper-slider-secondary-color": "var(--secondary-background-color)",
|
|
||||||
"paper-slider-pin-color": "var(--accent-color)",
|
|
||||||
"paper-item-icon-active-color": "#F9C536",
|
"paper-item-icon-active-color": "#F9C536",
|
||||||
"accent-color": "#E45E65",
|
"accent-color": "#E45E65",
|
||||||
"table-row-alternative-background-color": "#3E424B",
|
"table-row-alternative-background-color": "#3E424B",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"zone.anna": {
|
"zone.anna": {
|
||||||
entity_id: "zone.anna",
|
entity_id: "zone.anna",
|
||||||
state: "zoning",
|
state: "zoning",
|
||||||
|
@@ -5,17 +5,12 @@ export const demoThemeKernehed = () => ({
|
|||||||
"primary-color": "#2980b9",
|
"primary-color": "#2980b9",
|
||||||
"label-badge-red": "var(--accent-color)",
|
"label-badge-red": "var(--accent-color)",
|
||||||
"paper-tabs-selection-bar-color": "green",
|
"paper-tabs-selection-bar-color": "green",
|
||||||
"paper-slider-knob-color": "var(--accent-color)",
|
|
||||||
"primary-text-color": "#FFFFFF",
|
"primary-text-color": "#FFFFFF",
|
||||||
"light-primary-color": "var(--accent-color)",
|
"light-primary-color": "var(--accent-color)",
|
||||||
"primary-background-color": "#222222",
|
"primary-background-color": "#222222",
|
||||||
"sidebar-icon-color": "#777777",
|
"sidebar-icon-color": "#777777",
|
||||||
"paper-item-selected_-_background-color": "#292929",
|
"paper-item-selected_-_background-color": "#292929",
|
||||||
"paper-slider-active-color": "var(--accent-color)",
|
|
||||||
"secondary-background-color": "#222222",
|
"secondary-background-color": "#222222",
|
||||||
"paper-slider-container-color":
|
|
||||||
"linear-gradient(var(--primary-background-color), var(--secondary-background-color)) no-repeat",
|
|
||||||
"paper-slider-disabled-active-color": "var(--disabled-text-color)",
|
|
||||||
"disabled-text-color": "#777777",
|
"disabled-text-color": "#777777",
|
||||||
"paper-item-icon_-_color": "green",
|
"paper-item-icon_-_color": "green",
|
||||||
"paper-grey-200": "#222222",
|
"paper-grey-200": "#222222",
|
||||||
@@ -33,14 +28,10 @@ export const demoThemeKernehed = () => ({
|
|||||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||||
"label-badge-border-color": "green",
|
"label-badge-border-color": "green",
|
||||||
"paper-listbox-color": "#777777",
|
"paper-listbox-color": "#777777",
|
||||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
|
||||||
"card-background-color": "#292929",
|
"card-background-color": "#292929",
|
||||||
"label-badge-text-color": "var(--primary-text-color)",
|
"label-badge-text-color": "var(--primary-text-color)",
|
||||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
|
||||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||||
"dark-primary-color": "var(--accent-color)",
|
"dark-primary-color": "var(--accent-color)",
|
||||||
"paper-slider-secondary-color": "var(--secondary-background-color)",
|
|
||||||
"paper-slider-pin-color": "var(--accent-color)",
|
|
||||||
"paper-item-icon-active-color": "#b58e31",
|
"paper-item-icon-active-color": "#b58e31",
|
||||||
"accent-color": "#2980b9",
|
"accent-color": "#2980b9",
|
||||||
"table-row-alternative-background-color": "#292929",
|
"table-row-alternative-background-color": "#292929",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"sensor.pollen_grabo": {
|
"sensor.pollen_grabo": {
|
||||||
entity_id: "sensor.pollen_grabo",
|
entity_id: "sensor.pollen_grabo",
|
||||||
state: "",
|
state: "",
|
||||||
|
@@ -220,7 +220,8 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
state_filter: ["on"],
|
state_filter: ["on"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "shopping-list",
|
type: "todo-list",
|
||||||
|
entity: "todo.shopping_list",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
entities: [
|
entities: [
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
export const demoThemeTeachingbirds = () => ({
|
export const demoThemeTeachingbirds = () => ({
|
||||||
"paper-card-header-color": "var(--paper-item-icon-color)",
|
"paper-card-header-color": "var(--paper-item-icon-color)",
|
||||||
"paper-slider-pin-color": "var(--primary-color)",
|
|
||||||
"paper-listbox-background-color": "#202020",
|
"paper-listbox-background-color": "#202020",
|
||||||
"paper-grey-50": "var(--primary-text-color)",
|
"paper-grey-50": "var(--primary-text-color)",
|
||||||
"paper-item-icon-color": "#d3d3d3",
|
"paper-item-icon-color": "#d3d3d3",
|
||||||
@@ -8,8 +7,6 @@ export const demoThemeTeachingbirds = () => ({
|
|||||||
"primary-color": "#389638",
|
"primary-color": "#389638",
|
||||||
"light-primary-color": "#6f956f",
|
"light-primary-color": "#6f956f",
|
||||||
"label-badge-red": "var(--primary-color)",
|
"label-badge-red": "var(--primary-color)",
|
||||||
"paper-slider-secondary-color": "var(--light-primary-color)",
|
|
||||||
"paper-slider-knob-color": "var(--primary-color)",
|
|
||||||
"paper-listbox-color": "#FFFFFF",
|
"paper-listbox-color": "#FFFFFF",
|
||||||
"paper-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
"paper-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
||||||
"switch-unchecked-track-color": "var(--primary-text-color)",
|
"switch-unchecked-track-color": "var(--primary-text-color)",
|
||||||
@@ -17,9 +14,7 @@ export const demoThemeTeachingbirds = () => ({
|
|||||||
"label-badge-text-color": "var(--text-primary-color)",
|
"label-badge-text-color": "var(--text-primary-color)",
|
||||||
"primary-background-color": "#303030",
|
"primary-background-color": "#303030",
|
||||||
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
||||||
"paper-slider-active-color": "#d8bf50",
|
|
||||||
"secondary-background-color": "#2b2b2b",
|
"secondary-background-color": "#2b2b2b",
|
||||||
"paper-slider-knob-start-color": "var(--primary-color)",
|
|
||||||
"paper-item-icon-active-color": "#d8bf50",
|
"paper-item-icon-active-color": "#d8bf50",
|
||||||
"switch-checked-color": "var(--primary-color)",
|
"switch-checked-color": "var(--primary-color)",
|
||||||
"secondary-text-color": "#389638",
|
"secondary-text-color": "#389638",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { LocalizeFunc } from "../../../src/common/translations/localize";
|
import { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||||
import { LovelaceConfig } from "../../../src/data/lovelace";
|
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||||
import { Entity } from "../../../src/fake_data/entity";
|
import { Entity } from "../../../src/fake_data/entity";
|
||||||
|
|
||||||
export interface DemoConfig {
|
export interface DemoConfig {
|
||||||
|
@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { until } from "lit/directives/until";
|
import { until } from "lit/directives/until";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-circular-progress";
|
import "../../../src/components/ha-circular-progress";
|
||||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
||||||
import {
|
import {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import "../../src/resources/ha-style";
|
|
||||||
import "../../src/resources/roboto";
|
|
||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
import "../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "./ha-demo";
|
import "./ha-demo";
|
||||||
|
|
||||||
|
import("../../src/resources/ha-style");
|
||||||
|
@@ -22,7 +22,7 @@ import { mockLovelace } from "./stubs/lovelace";
|
|||||||
import { mockMediaPlayer } from "./stubs/media_player";
|
import { mockMediaPlayer } from "./stubs/media_player";
|
||||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||||
import { mockRecorder } from "./stubs/recorder";
|
import { mockRecorder } from "./stubs/recorder";
|
||||||
import { mockShoppingList } from "./stubs/shopping_list";
|
import { mockTodo } from "./stubs/todo";
|
||||||
import { mockSystemLog } from "./stubs/system_log";
|
import { mockSystemLog } from "./stubs/system_log";
|
||||||
import { mockTemplate } from "./stubs/template";
|
import { mockTemplate } from "./stubs/template";
|
||||||
import { mockTranslations } from "./stubs/translations";
|
import { mockTranslations } from "./stubs/translations";
|
||||||
@@ -49,7 +49,7 @@ export class HaDemo extends HomeAssistantAppEl {
|
|||||||
mockTranslations(hass);
|
mockTranslations(hass);
|
||||||
mockHistory(hass);
|
mockHistory(hass);
|
||||||
mockRecorder(hass);
|
mockRecorder(hass);
|
||||||
mockShoppingList(hass);
|
mockTodo(hass);
|
||||||
mockSystemLog(hass);
|
mockSystemLog(hass);
|
||||||
mockTemplate(hass);
|
mockTemplate(hass);
|
||||||
mockEvents(hass);
|
mockEvents(hass);
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,44 +0,0 @@
|
|||||||
import { ShoppingListItem } from "../../../src/data/shopping-list";
|
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|
||||||
|
|
||||||
let items: ShoppingListItem[] = [
|
|
||||||
{
|
|
||||||
id: 12,
|
|
||||||
name: "Milk",
|
|
||||||
complete: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 13,
|
|
||||||
name: "Eggs",
|
|
||||||
complete: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 14,
|
|
||||||
name: "Oranges",
|
|
||||||
complete: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const mockShoppingList = (hass: MockHomeAssistant) => {
|
|
||||||
hass.mockWS("shopping_list/items", () => items);
|
|
||||||
hass.mockWS("shopping_list/items/add", (msg) => {
|
|
||||||
const item: ShoppingListItem = {
|
|
||||||
id: new Date().getTime(),
|
|
||||||
complete: false,
|
|
||||||
name: msg.name,
|
|
||||||
};
|
|
||||||
items.push(item);
|
|
||||||
hass.mockEvent("shopping_list_updated");
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
hass.mockWS("shopping_list/items/update", ({ type, item_id, ...updates }) => {
|
|
||||||
items = items.map((item) =>
|
|
||||||
item.id === item_id ? { ...item, ...updates } : item
|
|
||||||
);
|
|
||||||
hass.mockEvent("shopping_list_updated");
|
|
||||||
});
|
|
||||||
hass.mockWS("shopping_list/items/clear", () => {
|
|
||||||
items = items.filter((item) => !item.complete);
|
|
||||||
hass.mockEvent("shopping_list_updated");
|
|
||||||
});
|
|
||||||
};
|
|
24
demo/src/stubs/todo.ts
Normal file
24
demo/src/stubs/todo.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { TodoItem, TodoItemStatus } from "../../../src/data/todo";
|
||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockTodo = (hass: MockHomeAssistant) => {
|
||||||
|
hass.mockWS("todo/item/list", () => ({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
uid: "12",
|
||||||
|
summary: "Milk",
|
||||||
|
status: TodoItemStatus.NeedsAction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "13",
|
||||||
|
summary: "Eggs",
|
||||||
|
status: TodoItemStatus.NeedsAction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "14",
|
||||||
|
summary: "Oranges",
|
||||||
|
status: TodoItemStatus.Completed,
|
||||||
|
},
|
||||||
|
] as TodoItem[],
|
||||||
|
}));
|
||||||
|
};
|
4
gallery/public/images/brand/README.md
Normal file
4
gallery/public/images/brand/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Note!
|
||||||
|
|
||||||
|
Note, the assets in this folder, are not part of the CC license this repository is shipped in.
|
||||||
|
All rights reserved.
|
BIN
gallery/public/images/brand/logo-exclusion-zone.png
Normal file
BIN
gallery/public/images/brand/logo-exclusion-zone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
gallery/public/images/brand/logo-layout-variants.png
Normal file
BIN
gallery/public/images/brand/logo-layout-variants.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
gallery/public/images/brand/logo.png
Normal file
BIN
gallery/public/images/brand/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
@@ -1,4 +1,3 @@
|
|||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import { html, css, LitElement } from "lit";
|
import { html, css, LitElement } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||||
@@ -7,6 +6,7 @@ import "../../../src/components/ha-switch";
|
|||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import "./demo-card";
|
import "./demo-card";
|
||||||
import type { DemoCardConfig } from "./demo-card";
|
import type { DemoCardConfig } from "./demo-card";
|
||||||
|
import "../ha-demo-options";
|
||||||
|
|
||||||
@customElement("demo-cards")
|
@customElement("demo-cards")
|
||||||
class DemoCards extends LitElement {
|
class DemoCards extends LitElement {
|
||||||
@@ -20,20 +20,14 @@ class DemoCards extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<app-toolbar>
|
<ha-demo-options>
|
||||||
<div class="filters">
|
<ha-formfield label="Show config">
|
||||||
<ha-formfield label="Show config">
|
<ha-switch @change=${this._showConfigToggled}> </ha-switch>
|
||||||
<ha-switch
|
</ha-formfield>
|
||||||
.checked=${this._showConfig}
|
<ha-formfield label="Dark theme">
|
||||||
@change=${this._showConfigToggled}
|
<ha-switch @change=${this._darkThemeToggled}> </ha-switch>
|
||||||
>
|
</ha-formfield>
|
||||||
</ha-switch>
|
</ha-demo-options>
|
||||||
</ha-formfield>
|
|
||||||
<ha-formfield label="Dark theme">
|
|
||||||
<ha-switch @change=${this._darkThemeToggled}> </ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
</app-toolbar>
|
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
${this.configs.map(
|
${this.configs.map(
|
||||||
@@ -69,12 +63,6 @@ class DemoCards extends LitElement {
|
|||||||
demo-card {
|
demo-card {
|
||||||
margin: 16px 16px 32px;
|
margin: 16px 16px 32px;
|
||||||
}
|
}
|
||||||
app-toolbar {
|
|
||||||
background-color: var(--light-primary-color);
|
|
||||||
}
|
|
||||||
.filters {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
ha-formfield {
|
ha-formfield {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
@@ -1,93 +0,0 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../../../src/components/ha-card";
|
|
||||||
import "../../../src/dialogs/more-info/more-info-content";
|
|
||||||
import "../../../src/state-summary/state-card-content";
|
|
||||||
|
|
||||||
class DemoMoreInfo extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
#card {
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100vw;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
width: 352px;
|
|
||||||
padding: 20px 24px;
|
|
||||||
}
|
|
||||||
state-card-content {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
width: 400px;
|
|
||||||
margin: 0 16px;
|
|
||||||
overflow: auto;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 800px) {
|
|
||||||
.root {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div class="root">
|
|
||||||
<div id="card">
|
|
||||||
<ha-card>
|
|
||||||
<state-card-content
|
|
||||||
state-obj="[[_stateObj]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
in-dialog
|
|
||||||
></state-card-content>
|
|
||||||
|
|
||||||
<more-info-content
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[_stateObj]]"
|
|
||||||
></more-info-content>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
<template is="dom-if" if="[[showConfig]]">
|
|
||||||
<pre>[[_jsonEntity(_stateObj)]]</pre>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
entityId: String,
|
|
||||||
showConfig: Boolean,
|
|
||||||
_stateObj: {
|
|
||||||
type: Object,
|
|
||||||
computed: "_getState(entityId, hass.states)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_getState(entityId, states) {
|
|
||||||
return states[entityId];
|
|
||||||
}
|
|
||||||
|
|
||||||
_jsonEntity(stateObj) {
|
|
||||||
// We are caching some things on stateObj
|
|
||||||
// (it sucks, we will remove in the future)
|
|
||||||
const tmp = {};
|
|
||||||
Object.keys(stateObj).forEach((key) => {
|
|
||||||
if (key[0] !== "_") {
|
|
||||||
tmp[key] = stateObj[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return JSON.stringify(tmp, null, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("demo-more-info", DemoMoreInfo);
|
|
93
gallery/src/components/demo-more-info.ts
Normal file
93
gallery/src/components/demo-more-info.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { LitElement, css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/dialogs/more-info/more-info-content";
|
||||||
|
import "../../../src/state-summary/state-card-content";
|
||||||
|
import "../ha-demo-options";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
|
@customElement("demo-more-info")
|
||||||
|
class DemoMoreInfo extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public entityId!: string;
|
||||||
|
|
||||||
|
@property() public showConfig!: boolean;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const state = this._getState(this.entityId, this.hass.states);
|
||||||
|
return html`
|
||||||
|
<div class="root">
|
||||||
|
<div id="card">
|
||||||
|
<ha-card>
|
||||||
|
<state-card-content
|
||||||
|
.stateObj=${state}
|
||||||
|
.hass=${this.hass}
|
||||||
|
in-dialog
|
||||||
|
></state-card-content>
|
||||||
|
|
||||||
|
<more-info-content
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${state}
|
||||||
|
></more-info-content>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
${this.showConfig ? html`<pre>${this._jsonEntity(state)}</pre>` : ""}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getState(entityId, states) {
|
||||||
|
return states[entityId];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _jsonEntity(stateObj) {
|
||||||
|
// We are caching some things on stateObj
|
||||||
|
// (it sucks, we will remove in the future)
|
||||||
|
const tmp = {};
|
||||||
|
Object.keys(stateObj).forEach((key) => {
|
||||||
|
if (key[0] !== "_") {
|
||||||
|
tmp[key] = stateObj[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return JSON.stringify(tmp, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#card {
|
||||||
|
max-width: 400px;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
width: 352px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
}
|
||||||
|
state-card-content {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 16px;
|
||||||
|
overflow: auto;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 800px) {
|
||||||
|
.root {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info": DemoMoreInfo;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,83 +0,0 @@
|
|||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
|
||||||
import "../../../src/components/ha-formfield";
|
|
||||||
import "../../../src/components/ha-switch";
|
|
||||||
import "./demo-more-info";
|
|
||||||
|
|
||||||
class DemoMoreInfos extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
#container {
|
|
||||||
min-height: calc(100vh - 128px);
|
|
||||||
background: var(--primary-background-color);
|
|
||||||
}
|
|
||||||
.cards {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
demo-more-info {
|
|
||||||
margin: 16px 16px 32px;
|
|
||||||
}
|
|
||||||
app-toolbar {
|
|
||||||
background-color: var(--light-primary-color);
|
|
||||||
}
|
|
||||||
.filters {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
ha-formfield {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<app-toolbar>
|
|
||||||
<div class="filters">
|
|
||||||
<ha-formfield label="Show entities">
|
|
||||||
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
|
||||||
</ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<ha-formfield label="Dark theme">
|
|
||||||
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
</app-toolbar>
|
|
||||||
<div id="container">
|
|
||||||
<div class="cards">
|
|
||||||
<template is="dom-repeat" items="[[entities]]">
|
|
||||||
<demo-more-info
|
|
||||||
entity-id="[[item]]"
|
|
||||||
show-config="[[_showConfig]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
></demo-more-info>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
entities: Array,
|
|
||||||
hass: Object,
|
|
||||||
_showConfig: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_showConfigToggled(ev) {
|
|
||||||
this._showConfig = ev.target.checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
_darkThemeToggled(ev) {
|
|
||||||
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
|
|
||||||
dark: ev.target.checked,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("demo-more-infos", DemoMoreInfos);
|
|
87
gallery/src/components/demo-more-infos.ts
Normal file
87
gallery/src/components/demo-more-infos.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { LitElement, css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||||
|
import "../../../src/components/ha-formfield";
|
||||||
|
import "../../../src/components/ha-switch";
|
||||||
|
import "./demo-more-info";
|
||||||
|
import "../ha-demo-options";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
|
@customElement("demo-more-infos")
|
||||||
|
class DemoMoreInfos extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public entities!: [];
|
||||||
|
|
||||||
|
@property({ attribute: false }) _showConfig: boolean = false;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<ha-demo-options>
|
||||||
|
<ha-formfield label="Show config">
|
||||||
|
<ha-switch @change=${this._showConfigToggled}> </ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<ha-formfield label="Dark theme">
|
||||||
|
<ha-switch @change=${this._darkThemeToggled}> </ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
</ha-demo-options>
|
||||||
|
<div id="container">
|
||||||
|
<div class="cards">
|
||||||
|
${this.entities.map(
|
||||||
|
(item) =>
|
||||||
|
html`<demo-more-info
|
||||||
|
.entityId=${item}
|
||||||
|
.showConfig=${this._showConfig}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></demo-more-info>`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
#container {
|
||||||
|
min-height: calc(100vh - 128px);
|
||||||
|
background: var(--primary-background-color);
|
||||||
|
}
|
||||||
|
.cards {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
demo-more-info {
|
||||||
|
margin: 16px 16px 32px;
|
||||||
|
}
|
||||||
|
ha-formfield {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
_showConfigToggled(ev) {
|
||||||
|
this._showConfig = ev.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
_darkThemeToggled(ev) {
|
||||||
|
applyThemesOnElement(
|
||||||
|
this.shadowRoot!.querySelector("#container"),
|
||||||
|
{
|
||||||
|
default_theme: "default",
|
||||||
|
default_dark_theme: "default",
|
||||||
|
themes: {},
|
||||||
|
darkMode: false,
|
||||||
|
theme: "default",
|
||||||
|
},
|
||||||
|
"default",
|
||||||
|
{
|
||||||
|
dark: ev.target.checked,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-infos": DemoMoreInfos;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
import "../../src/resources/ha-style";
|
|
||||||
import "../../src/resources/roboto";
|
|
||||||
import "./ha-gallery";
|
import "./ha-gallery";
|
||||||
|
|
||||||
|
import("../../src/resources/ha-style");
|
||||||
|
|
||||||
document.body.appendChild(document.createElement("ha-gallery"));
|
document.body.appendChild(document.createElement("ha-gallery"));
|
||||||
|
47
gallery/src/ha-demo-options.ts
Normal file
47
gallery/src/ha-demo-options.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import "@material/mwc-drawer";
|
||||||
|
import "@material/mwc-top-app-bar-fixed";
|
||||||
|
import { html, css, LitElement } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import "../../src/components/ha-icon-button";
|
||||||
|
import "../../src/managers/notification-manager";
|
||||||
|
import { haStyle } from "../../src/resources/styles";
|
||||||
|
import "./components/page-description";
|
||||||
|
|
||||||
|
@customElement("ha-demo-options")
|
||||||
|
class HaDemoOptions extends LitElement {
|
||||||
|
render() {
|
||||||
|
return html`<slot></slot>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
background-color: var(--light-primary-color);
|
||||||
|
margin-left: 60px
|
||||||
|
margin-right: 60px;
|
||||||
|
display: var(--layout-horizontal_-_display);
|
||||||
|
-ms-flex-direction: var(--layout-horizontal_-_-ms-flex-direction);
|
||||||
|
-webkit-flex-direction: var(
|
||||||
|
--layout-horizontal_-_-webkit-flex-direction
|
||||||
|
);
|
||||||
|
flex-direction: var(--layout-horizontal_-_flex-direction);
|
||||||
|
-ms-flex-align: var(--layout-center_-_-ms-flex-align);
|
||||||
|
-webkit-align-items: var(--layout-center_-_-webkit-align-items);
|
||||||
|
align-items: var(--layout-center_-_align-items);
|
||||||
|
position: relative;
|
||||||
|
height: 64px;
|
||||||
|
padding: 0 16px;
|
||||||
|
pointer-events: none;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-demo-options": HaDemoOptions;
|
||||||
|
}
|
||||||
|
}
|
@@ -2,30 +2,86 @@
|
|||||||
title: "Logo"
|
title: "Logo"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Using our logo
|
# Our logo
|
||||||
|
|
||||||
As a community, we are proud of our logo. Follow these guidelines to ensure it always looks its best. Our logo follows Google's material design spec and uses the blue interface color.
|
As a community, we are proud of our logo. Follow these guidelines to ensure it always represents the identity of the Home Assistant project and community the best way possible.
|
||||||
|
|
||||||
[Download Logo](https://github.com/home-assistant/assets/tree/master/logo)
|
[Download Logo](https://github.com/home-assistant/assets/tree/master/logo)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Using the icon
|
Please note that this logo is not released under the CC license. All rights reserved.
|
||||||
|
|
||||||
Our icon is a shorter and most used version of our logo. The icon can exist without the wordmark, the wordmark should never exist without the icon.
|
# Design
|
||||||
|
|
||||||

|
At the core of the Home Assistant logomark is the Blue House with Antenna, the three most recognizable and distinct features of the previous logo throughout the past decade.
|
||||||
|
|
||||||
## Using the right variant
|
### Blue
|
||||||
|
|
||||||
The pretty blue logo with a background shadow, pictured top left, is our primary logo. It should only be used with black, white, and non-duotone photography.
|
Blue feels stable and essential. A bright sky blue is joyful, clear, and free of clouds.
|
||||||
|
|
||||||
When needed you can use our logo without a shadow, as seen as the second variant.
|
### House
|
||||||
|
|
||||||
The outlined logo should only be used on packaging.
|
Of all possible combinations of shapes, a home is best abstracted in the shape of a structure with a pitched roof. With the vast amount of logos based on this shape, the best we can do is to make it more iconic. The house is further simplified - there is no gable and there is no chimney - to an orthogonal shape with an elegant and deliberate proportion.
|
||||||
|
|
||||||
## Exclusion zone
|
### Antenna
|
||||||
|
|
||||||
The logo needs some personal space. It's exclusion zone is equal to a quarter the height of the icon.
|
Call it a tree, a set of nodes, a PCB, or an antenna. The antenna is the most recognizable and memorable part of the previous Home Assistant logo, and is an easily understandable symbol that conveys technologies that are smart, connected, and growing evergreen.
|
||||||
|
|
||||||

|
# Usage
|
||||||
|
|
||||||
|
The default variation is the static colored wordmark in horizontal layout and dark text on a light background.
|
||||||
|
|
||||||
|
## Layout variations
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The default layout is the wordmark in horizontal layout. It provides the clearest context to the brand identity of Home Assistant.
|
||||||
|
|
||||||
|
Use the logomark variant when the context is clear that the logo is about Home Assistant. For example, inside the Home Assistant app where users are already aware of where they are at, the logomark variant without the wordmark can be used. The logomark can exist without the wordmark, however, the wordmark should never exist without the icon.
|
||||||
|
|
||||||
|
Use the wordmark in vertical layout when the space available has an aspect ratio less than 4:3. For example, in a square space on a t-shirt where a logo is needed, since there is no established context of Home Assistant, the wordmark in vertical layout should be used.
|
||||||
|
|
||||||
|
Lastly, use the wordmark in vertical layout with small logomark when Home Assistant is displayed in context of other Home Assistant-related projects. For example, in a flowchart showing the voice pipeline, use this layout for Home Assistant and its other related projects.
|
||||||
|
|
||||||
|
## Color variations, backgrounds, and placement
|
||||||
|
|
||||||
|
The default color is the colored version on light background with dark text.
|
||||||
|
|
||||||
|
For backgrounds that are dark, for example, when it is used on a page in a dark theme, use the colored version on dark background with light text.
|
||||||
|
|
||||||
|
In printed materials where color is unavailable, use the monochrome color variations.
|
||||||
|
|
||||||
|
On background that are dark or photographic, use the light monochrome color on dark background variation.
|
||||||
|
|
||||||
|
On backgrounds that are light or photographic, use the colored version. Do not use the monochrome variations.
|
||||||
|
|
||||||
|
Do not enclose the logmark in a square or color or any confined backgrounds, except in specific situations enforced by another company's marketplace guidelines, for example, an iOS app icon.
|
||||||
|
|
||||||
|
Do not add drop shadow to the logomark or the wordmark. If legibility is compromised due to the background, change the background to provide more contrast, or in last resort, add a heavily blurred drop shadaow.
|
||||||
|
|
||||||
|
It should only be used with black, white, and non-duotone photography.
|
||||||
|
|
||||||
|
Unlike the previous version of our logo, no outlined variants are available. Use the monochrome variants in those spaces.
|
||||||
|
|
||||||
|
### Exclusion zone
|
||||||
|
|
||||||
|
The logo needs some personal space. Its exclusion zone is equal to a quarter the height of the icon.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Animation
|
||||||
|
|
||||||
|
The default is the static variant.
|
||||||
|
|
||||||
|
Use the animated variant only for introductory purposes, for example, in the beginning of a video or on a loading screen.
|
||||||
|
|
||||||
|
Use the animated with sound variant only when sound is warranted in the user's context. For example, use it in the beginning of a video since sounds are expected in a video, but do not use it on a loading screen since sounds are not expected in a user interface.
|
||||||
|
|
||||||
|
Do not repeat the logo animation.
|
||||||
|
|
||||||
|
## Sizes and app icon variants
|
||||||
|
|
||||||
|
Special variants are created for specific contexts.
|
||||||
|
|
||||||
|
Use the tiny variants when the logomark is used in a very small space (16x16 dp), for example, the favicon of the Home Assistant website, a notification on Android, or the menubar of macOS.
|
||||||
|
@@ -1,19 +1,17 @@
|
|||||||
import { mdiHomeAssistant } from "@mdi/js";
|
import { css, html, LitElement, TemplateResult, nothing } from "lit";
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-chip";
|
import "../../../../src/components/chips/ha-chip-set";
|
||||||
import "../../../../src/components/ha-chip-set";
|
import "../../../../src/components/chips/ha-assist-chip";
|
||||||
|
import "../../../../src/components/chips/ha-input-chip";
|
||||||
|
import "../../../../src/components/chips/ha-filter-chip";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||||
|
|
||||||
const chips: {
|
const chips: {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
}[] = [
|
}[] = [
|
||||||
{},
|
|
||||||
{
|
|
||||||
icon: mdiHomeAssistant,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
content: "Content",
|
content: "Content",
|
||||||
},
|
},
|
||||||
@@ -29,31 +27,73 @@ export class DemoHaChips extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<ha-card header="ha-chip demo">
|
<ha-card header="ha-chip demo">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${chips.map(
|
<p>Action chip</p>
|
||||||
(chip) => html`
|
|
||||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
|
||||||
${chip.icon
|
|
||||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
|
||||||
</ha-svg-icon>`
|
|
||||||
: ""}
|
|
||||||
${chip.content}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
<ha-card header="ha-chip-set demo">
|
|
||||||
<div class="card-content">
|
|
||||||
<ha-chip-set>
|
<ha-chip-set>
|
||||||
${chips.map(
|
${chips.map(
|
||||||
(chip) => html`
|
(chip) => html`
|
||||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
<ha-assist-chip .label=${chip.content}>
|
||||||
|
${chip.icon
|
||||||
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
|
</ha-svg-icon>`
|
||||||
|
: nothing}
|
||||||
|
</ha-assist-chip>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
${chips.map(
|
||||||
|
(chip) => html`
|
||||||
|
<ha-assist-chip .label=${chip.content} selected>
|
||||||
|
${chip.icon
|
||||||
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
|
</ha-svg-icon>`
|
||||||
|
: nothing}
|
||||||
|
</ha-assist-chip>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-chip-set>
|
||||||
|
<p>Filter chip</p>
|
||||||
|
<ha-chip-set>
|
||||||
|
${chips.map(
|
||||||
|
(chip) => html`
|
||||||
|
<ha-filter-chip .label=${chip.content}>
|
||||||
|
${chip.icon
|
||||||
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
|
</ha-svg-icon>`
|
||||||
|
: nothing}
|
||||||
|
</ha-filter-chip>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
${chips.map(
|
||||||
|
(chip) => html`
|
||||||
|
<ha-filter-chip .label=${chip.content} selected>
|
||||||
|
${chip.icon
|
||||||
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
|
</ha-svg-icon>`
|
||||||
|
: nothing}
|
||||||
|
</ha-filter-chip>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-chip-set>
|
||||||
|
<p>Input chip</p>
|
||||||
|
<ha-chip-set>
|
||||||
|
${chips.map(
|
||||||
|
(chip) => html`
|
||||||
|
<ha-input-chip .label=${chip.content}>
|
||||||
${chip.icon
|
${chip.icon
|
||||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
</ha-svg-icon>`
|
</ha-svg-icon>`
|
||||||
: ""}
|
: ""}
|
||||||
${chip.content}
|
${chip.content}
|
||||||
</ha-chip>
|
</ha-input-chip>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
${chips.map(
|
||||||
|
(chip) => html`
|
||||||
|
<ha-input-chip .label=${chip.content} selected>
|
||||||
|
${chip.icon
|
||||||
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
|
</ha-svg-icon>`
|
||||||
|
: nothing}
|
||||||
|
</ha-input-chip>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ha-chip-set>
|
</ha-chip-set>
|
||||||
@@ -68,12 +108,10 @@ export class DemoHaChips extends LitElement {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
}
|
}
|
||||||
ha-chip {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.card-content {
|
.card-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -49,11 +49,11 @@ export class DemoHaCircularSlider extends LitElement {
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<p>Current</p>
|
<p>Current</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
min="10"
|
min="10"
|
||||||
max="30"
|
max="30"
|
||||||
.value=${this.current}
|
.value=${this.current}
|
||||||
@change=${this._currentChanged}
|
@change=${this._currentChanged}
|
||||||
pin
|
|
||||||
></ha-slider>
|
></ha-slider>
|
||||||
<p>${this.current} °C</p>
|
<p>${this.current} °C</p>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Control Number Buttons
|
||||||
|
---
|
107
gallery/src/pages/components/ha-control-number-buttons.ts
Normal file
107
gallery/src/pages/components/ha-control-number-buttons.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { LitElement, TemplateResult, css, html } from "lit";
|
||||||
|
import { customElement, state } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-control-number-buttons";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
|
||||||
|
const buttons: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
step?: number;
|
||||||
|
unit?: string;
|
||||||
|
class?: string;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
id: "basic",
|
||||||
|
label: "Basic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "min_max_step",
|
||||||
|
label: "With min/max and step",
|
||||||
|
min: 5,
|
||||||
|
max: 25,
|
||||||
|
step: 0.5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "custom",
|
||||||
|
label: "Custom",
|
||||||
|
class: "custom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "unit",
|
||||||
|
label: "With unit",
|
||||||
|
unit: "m",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-control-number-buttons")
|
||||||
|
export class DemoHarControlNumberButtons extends LitElement {
|
||||||
|
@state() value = 5;
|
||||||
|
|
||||||
|
private _valueChanged(ev) {
|
||||||
|
this.value = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${repeat(buttons, (button) => {
|
||||||
|
const { id, label, ...config } = button;
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<label id=${id}>${label}</label>
|
||||||
|
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||||
|
<ha-control-number-buttons
|
||||||
|
.value=${this.value}
|
||||||
|
.unit=${config.unit}
|
||||||
|
.min=${config.min}
|
||||||
|
.max=${config.max}
|
||||||
|
.step=${config.step}
|
||||||
|
class=${ifDefined(config.class)}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
.label=${label}
|
||||||
|
>
|
||||||
|
</ha-control-number-buttons>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.custom {
|
||||||
|
color: #2196f3;
|
||||||
|
--control-number-buttons-color: #2196f3;
|
||||||
|
--control-number-buttons-background-color: #2196f3;
|
||||||
|
--control-number-buttons-background-opacity: 0.1;
|
||||||
|
--control-number-buttons-thickness: 100px;
|
||||||
|
--control-number-buttons-border-radius: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-control-number-buttons": DemoHarControlNumberButtons;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Control Select Menu
|
||||||
|
---
|
146
gallery/src/pages/components/ha-control-select-menu.ts
Normal file
146
gallery/src/pages/components/ha-control-select-menu.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import { mdiFan, mdiFanSpeed1, mdiFanSpeed2, mdiFanSpeed3 } from "@mdi/js";
|
||||||
|
import { LitElement, TemplateResult, css, html, nothing } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-control-select-menu";
|
||||||
|
import "../../../../src/components/ha-list-item";
|
||||||
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
|
||||||
|
type SelectMenuOptions = {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
icon?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SelectMenu = {
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
class?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
options: SelectMenuOptions[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const selects: SelectMenu[] = [
|
||||||
|
{
|
||||||
|
label: "Basic select",
|
||||||
|
icon: mdiFan,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "low",
|
||||||
|
label: "Low",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "medium",
|
||||||
|
label: "Medium",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "high",
|
||||||
|
label: "High",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Select with icons",
|
||||||
|
icon: mdiFan,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: "low",
|
||||||
|
label: "Low",
|
||||||
|
icon: mdiFanSpeed1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "medium",
|
||||||
|
label: "Medium",
|
||||||
|
icon: mdiFanSpeed2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "high",
|
||||||
|
label: "High",
|
||||||
|
icon: mdiFanSpeed3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Disabled select",
|
||||||
|
icon: mdiFan,
|
||||||
|
options: [],
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-control-select-menu")
|
||||||
|
export class DemoHaControlSelectMenu extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
${repeat(
|
||||||
|
selects,
|
||||||
|
(select) => html`
|
||||||
|
<div class="card-content">
|
||||||
|
<ha-control-select-menu
|
||||||
|
.label=${select.label}
|
||||||
|
?disabled=${select.disabled}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${select.icon}></ha-svg-icon>
|
||||||
|
${select.options.map(
|
||||||
|
(option) => html`
|
||||||
|
<ha-list-item
|
||||||
|
.value=${option.value}
|
||||||
|
.graphic=${option.icon ? "icon" : undefined}
|
||||||
|
>
|
||||||
|
${option.icon
|
||||||
|
? html`
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${option.icon}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${option.label ?? option.value}
|
||||||
|
</ha-list-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-control-select-menu>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.custom {
|
||||||
|
--control-button-icon-color: var(--primary-color);
|
||||||
|
--control-button-background-color: var(--primary-color);
|
||||||
|
--control-button-background-opacity: 0.2;
|
||||||
|
--control-button-border-radius: 18px;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-control-select-menu": DemoHaControlSelectMenu;
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ const sliders: {
|
|||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
mode?: "start" | "end" | "cursor";
|
mode?: "start" | "end" | "cursor";
|
||||||
|
unit?: string;
|
||||||
class?: string;
|
class?: string;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
@@ -31,18 +32,21 @@ const sliders: {
|
|||||||
label: "Slider (start mode) and custom style",
|
label: "Slider (start mode) and custom style",
|
||||||
mode: "start",
|
mode: "start",
|
||||||
class: "custom",
|
class: "custom",
|
||||||
|
unit: "mm",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "slider-end-custom",
|
id: "slider-end-custom",
|
||||||
label: "Slider (end mode) and custom style",
|
label: "Slider (end mode) and custom style",
|
||||||
mode: "end",
|
mode: "end",
|
||||||
class: "custom",
|
class: "custom",
|
||||||
|
unit: "mm",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "slider-cursor-custom",
|
id: "slider-cursor-custom",
|
||||||
label: "Slider (cursor mode) and custom style",
|
label: "Slider (cursor mode) and custom style",
|
||||||
mode: "cursor",
|
mode: "cursor",
|
||||||
class: "custom",
|
class: "custom",
|
||||||
|
unit: "mm",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ export class DemoHaBarSlider extends LitElement {
|
|||||||
@value-changed=${this.handleValueChanged}
|
@value-changed=${this.handleValueChanged}
|
||||||
@slider-moved=${this.handleSliderMoved}
|
@slider-moved=${this.handleSliderMoved}
|
||||||
aria-labelledby=${id}
|
aria-labelledby=${id}
|
||||||
|
.unit=${config.unit}
|
||||||
>
|
>
|
||||||
</ha-control-slider>
|
</ha-control-slider>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,6 +119,7 @@ export class DemoHaBarSlider extends LitElement {
|
|||||||
@value-changed=${this.handleValueChanged}
|
@value-changed=${this.handleValueChanged}
|
||||||
@slider-moved=${this.handleSliderMoved}
|
@slider-moved=${this.handleSliderMoved}
|
||||||
aria-label=${label}
|
aria-label=${label}
|
||||||
|
.unit=${config.unit}
|
||||||
>
|
>
|
||||||
</ha-control-slider>
|
</ha-control-slider>
|
||||||
`;
|
`;
|
||||||
|
@@ -5,9 +5,22 @@ subtitle: Dialogs provide important prompts in a user flow.
|
|||||||
|
|
||||||
# Material Design 3
|
# Material Design 3
|
||||||
|
|
||||||
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||||
|
|
||||||
# Highlighted guidelines
|
# Guidelines
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
|
||||||
|
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
|
||||||
|
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
|
||||||
|
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
|
||||||
|
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
|
||||||
|
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||||
|
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||||
|
- Destructive actions should be a red warning button.
|
||||||
|
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||||
|
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||||
|
|
||||||
## Content
|
## Content
|
||||||
|
|
||||||
@@ -17,14 +30,6 @@ Our dialogs are based on the latest version of Material Design. Specs and guidel
|
|||||||
- If users become unsure, they read the description. Make sure this explains what will happen.
|
- If users become unsure, they read the description. Make sure this explains what will happen.
|
||||||
- Strive for minimalism.
|
- Strive for minimalism.
|
||||||
|
|
||||||
## Buttons and X-icon
|
|
||||||
|
|
||||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
|
||||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
|
||||||
- Destructive actions should be a red warning button.
|
|
||||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
|
||||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
### Confirmation dialog
|
### Confirmation dialog
|
||||||
|
@@ -57,6 +57,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: "backyard",
|
area_id: "backyard",
|
||||||
@@ -74,6 +75,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@@ -91,6 +93,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -57,8 +57,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
|||||||
></ha-hs-color-picker>
|
></ha-hs-color-picker>
|
||||||
<p>Hue : ${this.value[0]}</p>
|
<p>Hue : ${this.value[0]}</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
step="1"
|
step="1"
|
||||||
pin
|
|
||||||
min="0"
|
min="0"
|
||||||
max="360"
|
max="360"
|
||||||
.value=${this.value[0]}
|
.value=${this.value[0]}
|
||||||
@@ -67,8 +67,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
|||||||
</ha-slider>
|
</ha-slider>
|
||||||
<p>Saturation : ${this.value[1]}</p>
|
<p>Saturation : ${this.value[1]}</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
step="0.01"
|
step="0.01"
|
||||||
pin
|
|
||||||
min="0"
|
min="0"
|
||||||
max="1"
|
max="1"
|
||||||
.value=${this.value[1]}
|
.value=${this.value[1]}
|
||||||
@@ -77,8 +77,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
|||||||
</ha-slider>
|
</ha-slider>
|
||||||
<p>Color Brighness : ${this.brightness}</p>
|
<p>Color Brighness : ${this.brightness}</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
step="1"
|
step="1"
|
||||||
pin
|
|
||||||
min="0"
|
min="0"
|
||||||
max="255"
|
max="255"
|
||||||
.value=${this.brightness}
|
.value=${this.brightness}
|
||||||
|
@@ -53,6 +53,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: "backyard",
|
area_id: "backyard",
|
||||||
@@ -70,6 +71,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@@ -87,6 +89,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Temp Color Picker
|
|
||||||
---
|
|
@@ -1,117 +0,0 @@
|
|||||||
import "../../../../src/components/ha-temp-color-picker";
|
|
||||||
|
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators";
|
|
||||||
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-slider";
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-temp-color-picker")
|
|
||||||
export class DemoHaTempColorPicker extends LitElement {
|
|
||||||
@state()
|
|
||||||
min = 3000;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
max = 7000;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
value = 4000;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
liveValue?: number;
|
|
||||||
|
|
||||||
private _minChanged(ev) {
|
|
||||||
this.min = Number(ev.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _maxChanged(ev) {
|
|
||||||
this.max = Number(ev.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _valueChanged(ev) {
|
|
||||||
this.value = Number(ev.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _tempColorCursor(ev) {
|
|
||||||
this.liveValue = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _tempColorChanged(ev) {
|
|
||||||
this.value = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
<p class="value">${this.liveValue ?? this.value} K</p>
|
|
||||||
<ha-temp-color-picker
|
|
||||||
.min=${this.min}
|
|
||||||
.max=${this.max}
|
|
||||||
.value=${this.value}
|
|
||||||
@value-changed=${this._tempColorChanged}
|
|
||||||
@cursor-moved=${this._tempColorCursor}
|
|
||||||
></ha-temp-color-picker>
|
|
||||||
<p>Min temp : ${this.min} K</p>
|
|
||||||
<ha-slider
|
|
||||||
step="1"
|
|
||||||
pin
|
|
||||||
min="2000"
|
|
||||||
max="10000"
|
|
||||||
.value=${this.min}
|
|
||||||
@change=${this._minChanged}
|
|
||||||
>
|
|
||||||
</ha-slider>
|
|
||||||
<p>Max temp : ${this.max} K</p>
|
|
||||||
<ha-slider
|
|
||||||
step="1"
|
|
||||||
pin
|
|
||||||
min="2000"
|
|
||||||
max="10000"
|
|
||||||
.value=${this.max}
|
|
||||||
@change=${this._maxChanged}
|
|
||||||
>
|
|
||||||
</ha-slider>
|
|
||||||
<p>Value : ${this.value} K</p>
|
|
||||||
<ha-slider
|
|
||||||
step="1"
|
|
||||||
pin
|
|
||||||
min=${this.min}
|
|
||||||
max=${this.max}
|
|
||||||
.value=${this.value}
|
|
||||||
@change=${this._valueChanged}
|
|
||||||
>
|
|
||||||
</ha-slider>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
ha-temp-color-picker {
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-temp-color-picker": DemoHaTempColorPicker;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -20,7 +20,7 @@ We want to make it as easy for designers to contribute as it is for developers.
|
|||||||
|
|
||||||
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
|
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
|
||||||
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
|
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
|
||||||
- Find the lates UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
|
- Find the latest UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
|
||||||
|
|
||||||
## Developers
|
## Developers
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Shopping List Card
|
|
||||||
---
|
|
@@ -14,7 +14,7 @@ const ENTITIES = [
|
|||||||
}),
|
}),
|
||||||
getEntity("light", "bed_light", "on", {
|
getEntity("light", "bed_light", "on", {
|
||||||
friendly_name: "Bed Light",
|
friendly_name: "Bed Light",
|
||||||
supported_color_modes: [LightColorMode.HS],
|
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
|
||||||
}),
|
}),
|
||||||
getEntity("light", "unavailable", "unavailable", {
|
getEntity("light", "unavailable", "unavailable", {
|
||||||
friendly_name: "Unavailable entity",
|
friendly_name: "Unavailable entity",
|
||||||
@@ -116,6 +116,15 @@ const CONFIGS = [
|
|||||||
- type: "light-brightness"
|
- type: "light-brightness"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
heading: "Light color temperature feature",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: light.bed_light
|
||||||
|
features:
|
||||||
|
- type: "color-temp"
|
||||||
|
`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
heading: "Vacuum commands feature",
|
heading: "Vacuum commands feature",
|
||||||
config: `
|
config: `
|
||||||
|
3
gallery/src/pages/lovelace/todo-list-card.markdown
Normal file
3
gallery/src/pages/lovelace/todo-list-card.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Todo List Card
|
||||||
|
---
|
@@ -2,25 +2,39 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
|||||||
import { customElement, query } from "lit/decorators";
|
import { customElement, query } from "lit/decorators";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import "../../components/demo-cards";
|
import "../../components/demo-cards";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import { mockTodo } from "../../../../demo/src/stubs/todo";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("todo", "shopping_list", "2", {
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
supported_features: 15,
|
||||||
|
}),
|
||||||
|
getEntity("todo", "read_only", "2", {
|
||||||
|
friendly_name: "Read only",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
{
|
{
|
||||||
heading: "List example",
|
heading: "List example",
|
||||||
config: `
|
config: `
|
||||||
- type: shopping-list
|
- type: todo-list
|
||||||
|
entity: todo.shopping_list
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "List with title example",
|
heading: "List with title example",
|
||||||
config: `
|
config: `
|
||||||
- type: shopping-list
|
- type: todo-list
|
||||||
title: Shopping List
|
title: Shopping List
|
||||||
|
entity: todo.read_only
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-lovelace-shopping-list-card")
|
@customElement("demo-lovelace-todo-list-card")
|
||||||
class DemoShoppingListEntity extends LitElement {
|
class DemoTodoListEntity extends LitElement {
|
||||||
@query("#demos") private _demoRoot!: HTMLElement;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -32,18 +46,14 @@ class DemoShoppingListEntity extends LitElement {
|
|||||||
const hass = provideHass(this._demoRoot);
|
const hass = provideHass(this._demoRoot);
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
hass.updateTranslations("lovelace", "en");
|
hass.updateTranslations("lovelace", "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
|
||||||
hass.mockAPI("shopping_list", () => [
|
mockTodo(hass);
|
||||||
{ name: "list", id: 1, complete: false },
|
|
||||||
{ name: "all", id: 2, complete: false },
|
|
||||||
{ name: "the", id: 3, complete: false },
|
|
||||||
{ name: "things", id: 4, complete: true },
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"demo-lovelace-shopping-list-card": DemoShoppingListEntity;
|
"demo-lovelace-todo-list-card": DemoTodoListEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -10,7 +10,6 @@ import { computeStateDisplay } from "../../../../src/common/entity/compute_state
|
|||||||
import "../../../../src/components/data-table/ha-data-table";
|
import "../../../../src/components/data-table/ha-data-table";
|
||||||
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||||
import "../../../../src/components/entity/state-badge";
|
import "../../../../src/components/entity/state-badge";
|
||||||
import "../../../../src/components/ha-chip";
|
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
|
||||||
@@ -284,6 +283,13 @@ const ENTITIES: HassEntity[] = [
|
|||||||
installed_version: "1.0.0",
|
installed_version: "1.0.0",
|
||||||
latest_version: "2.0.0",
|
latest_version: "2.0.0",
|
||||||
}),
|
}),
|
||||||
|
createEntity("water_heater.off", "off"),
|
||||||
|
createEntity("water_heater.eco", "eco"),
|
||||||
|
createEntity("water_heater.electric", "electric"),
|
||||||
|
createEntity("water_heater.performance", "performance"),
|
||||||
|
createEntity("water_heater.high_demand", "high_demand"),
|
||||||
|
createEntity("water_heater.heat_pump", "heat_pump"),
|
||||||
|
createEntity("water_heater.gas", "gas"),
|
||||||
];
|
];
|
||||||
|
|
||||||
function createEntity(
|
function createEntity(
|
||||||
@@ -336,7 +342,7 @@ export class DemoEntityState extends LitElement {
|
|||||||
const columns: DataTableColumnContainer<EntityRowData> = {
|
const columns: DataTableColumnContainer<EntityRowData> = {
|
||||||
icon: {
|
icon: {
|
||||||
title: "Icon",
|
title: "Icon",
|
||||||
template: (_, entry) => html`
|
template: (entry) => html`
|
||||||
<state-badge
|
<state-badge
|
||||||
.stateObj=${entry.stateObj}
|
.stateObj=${entry.stateObj}
|
||||||
.stateColor=${true}
|
.stateColor=${true}
|
||||||
@@ -353,7 +359,7 @@ export class DemoEntityState extends LitElement {
|
|||||||
title: "State",
|
title: "State",
|
||||||
width: "20%",
|
width: "20%",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
template: (_, entry) =>
|
template: (entry) =>
|
||||||
html`${computeStateDisplay(
|
html`${computeStateDisplay(
|
||||||
hass.localize,
|
hass.localize,
|
||||||
entry.stateObj,
|
entry.stateObj,
|
||||||
@@ -364,14 +370,14 @@ export class DemoEntityState extends LitElement {
|
|||||||
},
|
},
|
||||||
device_class: {
|
device_class: {
|
||||||
title: "Device class",
|
title: "Device class",
|
||||||
template: (dc) => html`${dc ?? "-"}`,
|
template: (entry) => html`${entry.device_class ?? "-"}`,
|
||||||
width: "20%",
|
width: "20%",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
title: "Domain",
|
title: "Domain",
|
||||||
template: (_, entry) => html`${computeDomain(entry.entity_id)}`,
|
template: (entry) => html`${computeDomain(entry.entity_id)}`,
|
||||||
width: "20%",
|
width: "20%",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
@@ -213,6 +213,7 @@ const createDeviceRegistryEntries = (
|
|||||||
name: "Tag Reader",
|
name: "Tag Reader",
|
||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: "1.0.0",
|
hw_version: "1.0.0",
|
||||||
|
serial_number: "00_12_4B_00_22_98_88_7F",
|
||||||
id: "mock-device-id",
|
id: "mock-device-id",
|
||||||
identifiers: [],
|
identifiers: [],
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
@@ -2,7 +2,7 @@ import "@material/mwc-button";
|
|||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
|
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
|
||||||
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||||
|
|
||||||
@customElement("demo-misc-util-long-press")
|
@customElement("demo-misc-util-long-press")
|
||||||
|
@@ -43,6 +43,28 @@ const ENTITIES = [
|
|||||||
target_temp_low: 20,
|
target_temp_low: 20,
|
||||||
target_temp_high: 25,
|
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", "unavailable", "unavailable", {
|
getEntity("climate", "unavailable", "unavailable", {
|
||||||
friendly_name: "Unavailable heater",
|
friendly_name: "Unavailable heater",
|
||||||
hvac_modes: ["heat", "off"],
|
hvac_modes: ["heat", "off"],
|
||||||
|
3
gallery/src/pages/more-info/water-heater.markdown
Normal file
3
gallery/src/pages/more-info/water-heater.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Water Heater
|
||||||
|
---
|
70
gallery/src/pages/more-info/water-heater.ts
Normal file
70
gallery/src/pages/more-info/water-heater.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
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 {
|
||||||
|
MockHomeAssistant,
|
||||||
|
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,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-water-heater")
|
||||||
|
class DemoMoreInfoWaterHeater extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-water-heater": DemoMoreInfoWaterHeater;
|
||||||
|
}
|
||||||
|
}
|
@@ -7,7 +7,6 @@ import {
|
|||||||
mdiDocker,
|
mdiDocker,
|
||||||
mdiExclamationThick,
|
mdiExclamationThick,
|
||||||
mdiFlask,
|
mdiFlask,
|
||||||
mdiHomeAssistant,
|
|
||||||
mdiKey,
|
mdiKey,
|
||||||
mdiLinkLock,
|
mdiLinkLock,
|
||||||
mdiNetwork,
|
mdiNetwork,
|
||||||
@@ -22,7 +21,7 @@ import {
|
|||||||
mdiPound,
|
mdiPound,
|
||||||
mdiShield,
|
mdiShield,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -32,19 +31,19 @@ import { navigate } from "../../../../src/common/navigate";
|
|||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-chip";
|
import "../../../../src/components/chips/ha-chip-set";
|
||||||
import "../../../../src/components/ha-chip-set";
|
import "../../../../src/components/chips/ha-assist-chip";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
import "../../../../src/components/ha-settings-row";
|
import "../../../../src/components/ha-settings-row";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import {
|
import {
|
||||||
AddonCapability,
|
AddonCapability,
|
||||||
fetchHassioAddonChangelog,
|
|
||||||
fetchHassioAddonInfo,
|
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
HassioAddonSetOptionParams,
|
HassioAddonSetOptionParams,
|
||||||
HassioAddonSetSecurityParams,
|
HassioAddonSetSecurityParams,
|
||||||
|
fetchHassioAddonChangelog,
|
||||||
|
fetchHassioAddonInfo,
|
||||||
installHassioAddon,
|
installHassioAddon,
|
||||||
rebuildLocalAddon,
|
rebuildLocalAddon,
|
||||||
restartHassioAddon,
|
restartHassioAddon,
|
||||||
@@ -56,9 +55,9 @@ import {
|
|||||||
validateHassioAddonOption,
|
validateHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import {
|
import {
|
||||||
|
HassioStats,
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
fetchHassioStats,
|
fetchHassioStats,
|
||||||
HassioStats,
|
|
||||||
} from "../../../../src/data/hassio/common";
|
} from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
StoreAddon,
|
StoreAddon,
|
||||||
@@ -69,6 +68,7 @@ import {
|
|||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
|
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../../src/types";
|
import { HomeAssistant, Route } from "../../../../src/types";
|
||||||
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
||||||
@@ -78,6 +78,7 @@ import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-has
|
|||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "../../update-available/update-available-card";
|
import "../../update-available/update-available-card";
|
||||||
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
|
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
|
||||||
|
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
|
||||||
|
|
||||||
const STAGE_ICON = {
|
const STAGE_ICON = {
|
||||||
stable: mdiCheckCircle,
|
stable: mdiCheckCircle,
|
||||||
@@ -234,28 +235,32 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
<ha-chip-set class="capabilities">
|
<ha-chip-set class="capabilities">
|
||||||
${this.addon.stage !== "stable"
|
${this.addon.stage !== "stable"
|
||||||
? html` <ha-chip
|
? html`
|
||||||
hasIcon
|
<ha-assist-chip
|
||||||
class=${classMap({
|
filled
|
||||||
yellow: this.addon.stage === "experimental",
|
class=${classMap({
|
||||||
red: this.addon.stage === "deprecated",
|
yellow: this.addon.stage === "experimental",
|
||||||
})}
|
red: this.addon.stage === "deprecated",
|
||||||
@click=${this._showMoreInfo}
|
})}
|
||||||
id="stage"
|
@click=${this._showMoreInfo}
|
||||||
>
|
id="stage"
|
||||||
<ha-svg-icon
|
.label=${capitalizeFirstLetter(
|
||||||
slot="icon"
|
this.supervisor.localize(
|
||||||
.path=${STAGE_ICON[this.addon.stage]}
|
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||||
|
)
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
</ha-svg-icon>
|
<ha-svg-icon
|
||||||
${this.supervisor.localize(
|
slot="icon"
|
||||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
.path=${STAGE_ICON[this.addon.stage]}
|
||||||
)}
|
>
|
||||||
</ha-chip>`
|
</ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-chip
|
<ha-assist-chip
|
||||||
hasIcon
|
filled
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
green: Number(this.addon.rating) >= 6,
|
green: Number(this.addon.rating) >= 6,
|
||||||
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
|
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
|
||||||
@@ -263,138 +268,183 @@ class HassioAddonInfo extends LitElement {
|
|||||||
})}
|
})}
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="rating"
|
id="rating"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.rating"
|
||||||
|
)
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
|
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
|
||||||
</ha-svg-icon>
|
</ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.capability.label.rating"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
${this.addon.host_network
|
${this.addon.host_network
|
||||||
? html`
|
? html`
|
||||||
<ha-chip
|
<ha-assist-chip
|
||||||
hasIcon
|
filled
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="host_network"
|
id="host_network"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.host"
|
||||||
|
)
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
|
||||||
${this.supervisor.localize(
|
</ha-assist-chip>
|
||||||
"addon.dashboard.capability.label.host"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.full_access
|
${this.addon.full_access
|
||||||
? html`
|
? html`
|
||||||
<ha-chip
|
<ha-assist-chip
|
||||||
hasIcon
|
filled
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="full_access"
|
id="full_access"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.hardware"
|
||||||
|
)
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
|
||||||
${this.supervisor.localize(
|
</ha-assist-chip>
|
||||||
"addon.dashboard.capability.label.hardware"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.homeassistant_api
|
${this.addon.homeassistant_api
|
||||||
? html`
|
? html`
|
||||||
<ha-chip
|
<ha-assist-chip
|
||||||
hasIcon
|
filled
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="homeassistant_api"
|
id="homeassistant_api"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.core"
|
||||||
|
)
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="icon"
|
||||||
.path=${mdiHomeAssistant}
|
.path=${mdiHomeAssistant}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.supervisor.localize(
|
</ha-assist-chip>
|
||||||
"addon.dashboard.capability.label.core"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this._computeHassioApi
|
${this._computeHassioApi
|
||||||
? html`
|
? html`
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
|
<ha-assist-chip
|
||||||
|
filled
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="hassio_api"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||||
|
) || this.addon.hassio_role
|
||||||
|
)}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="icon"
|
||||||
.path=${mdiHomeAssistant}
|
.path=${mdiHomeAssistant}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.supervisor.localize(
|
</ha-assist-chip>
|
||||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
|
||||||
) || this.addon.hassio_role}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.docker_api
|
${this.addon.docker_api
|
||||||
? html`
|
? html`
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
|
<ha-assist-chip
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
filled
|
||||||
${this.supervisor.localize(
|
@click=${this._showMoreInfo}
|
||||||
"addon.dashboard.capability.label.docker"
|
id="docker_api"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.docker"
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</ha-chip>
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.host_pid
|
${this.addon.host_pid
|
||||||
? html`
|
? html`
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
|
<ha-assist-chip
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
filled
|
||||||
${this.supervisor.localize(
|
@click=${this._showMoreInfo}
|
||||||
"addon.dashboard.capability.label.host_pid"
|
id="host_pid"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.host_pid"
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</ha-chip>
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.apparmor !== "default"
|
${this.addon.apparmor !== "default"
|
||||||
? html`
|
? html`
|
||||||
<ha-chip
|
<ha-assist-chip
|
||||||
hasIcon
|
filled
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
class=${this._computeApparmorClassName}
|
class=${this._computeApparmorClassName}
|
||||||
id="apparmor"
|
id="apparmor"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.apparmor"
|
||||||
|
)
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
|
||||||
${this.supervisor.localize(
|
</ha-assist-chip>
|
||||||
"addon.dashboard.capability.label.apparmor"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.auth_api
|
${this.addon.auth_api
|
||||||
? html`
|
? html`
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
|
<ha-assist-chip
|
||||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
filled
|
||||||
${this.supervisor.localize(
|
@click=${this._showMoreInfo}
|
||||||
"addon.dashboard.capability.label.auth"
|
id="auth_api"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.auth"
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</ha-chip>
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.ingress
|
${this.addon.ingress
|
||||||
? html`
|
? html`
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
|
<ha-assist-chip
|
||||||
|
filled
|
||||||
|
@click=${this._showMoreInfo}
|
||||||
|
id="ingress"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.ingress"
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="icon"
|
slot="icon"
|
||||||
.path=${mdiCursorDefaultClickOutline}
|
.path=${mdiCursorDefaultClickOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.supervisor.localize(
|
</ha-assist-chip>
|
||||||
"addon.dashboard.capability.label.ingress"
|
|
||||||
)}
|
|
||||||
</ha-chip>
|
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.signed
|
${this.addon.signed
|
||||||
? html`
|
? html`
|
||||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="signed">
|
<ha-assist-chip
|
||||||
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
filled
|
||||||
${this.supervisor.localize(
|
@click=${this._showMoreInfo}
|
||||||
"addon.dashboard.capability.label.signed"
|
id="signed"
|
||||||
|
.label=${capitalizeFirstLetter(
|
||||||
|
this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.signed"
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</ha-chip>
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-chip-set>
|
</ha-chip-set>
|
||||||
@@ -1185,23 +1235,35 @@ class HassioAddonInfo extends LitElement {
|
|||||||
.description a {
|
.description a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
ha-chip {
|
ha-assist-chip {
|
||||||
text-transform: capitalize;
|
--md-sys-color-primary: var(--text-primary-color);
|
||||||
--ha-chip-text-color: var(--text-primary-color);
|
--md-sys-color-on-surface: var(--text-primary-color);
|
||||||
--ha-chip-background-color: var(--primary-color);
|
--ha-assist-chip-filled-container-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.red {
|
.red {
|
||||||
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
|
--ha-assist-chip-filled-container-color: var(
|
||||||
|
--label-badge-red,
|
||||||
|
#df4c1e
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.blue {
|
.blue {
|
||||||
--ha-chip-background-color: var(--label-badge-blue, #039be5);
|
--ha-assist-chip-filled-container-color: var(
|
||||||
|
--label-badge-blue,
|
||||||
|
#039be5
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.green {
|
.green {
|
||||||
--ha-chip-background-color: var(--label-badge-green, #0da035);
|
--ha-assist-chip-filled-container-color: var(
|
||||||
|
--label-badge-green,
|
||||||
|
#0da035
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.yellow {
|
.yellow {
|
||||||
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
|
--ha-assist-chip-filled-container-color: var(
|
||||||
|
--label-badge-yellow,
|
||||||
|
#f4b400
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.capabilities {
|
.capabilities {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@@ -1260,9 +1322,6 @@ class HassioAddonInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
ha-chip {
|
|
||||||
line-height: 36px;
|
|
||||||
}
|
|
||||||
.addon-options {
|
.addon-options {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
@@ -49,6 +49,10 @@ import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hass
|
|||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
|
type BackupItem = HassioBackup & {
|
||||||
|
secondary: string;
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("hassio-backups")
|
@customElement("hassio-backups")
|
||||||
export class HassioBackups extends LitElement {
|
export class HassioBackups extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -117,15 +121,15 @@ export class HassioBackups extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(narrow: boolean): DataTableColumnContainer => ({
|
(narrow: boolean): DataTableColumnContainer<BackupItem> => ({
|
||||||
name: {
|
name: {
|
||||||
title: this.supervisor.localize("backup.name"),
|
title: this.supervisor.localize("backup.name"),
|
||||||
main: true,
|
main: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (entry: string, backup: any) =>
|
template: (backup) =>
|
||||||
html`${entry || backup.slug}
|
html`${backup.name || backup.slug}
|
||||||
<div class="secondary">${backup.secondary}</div>`,
|
<div class="secondary">${backup.secondary}</div>`,
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
@@ -134,7 +138,7 @@ export class HassioBackups extends LitElement {
|
|||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
template: (entry: number) => Math.ceil(entry * 10) / 10 + " MB",
|
template: (backup) => Math.ceil(backup.size * 10) / 10 + " MB",
|
||||||
},
|
},
|
||||||
location: {
|
location: {
|
||||||
title: this.supervisor.localize("backup.location"),
|
title: this.supervisor.localize("backup.location"),
|
||||||
@@ -142,8 +146,8 @@ export class HassioBackups extends LitElement {
|
|||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
template: (entry: string | null) =>
|
template: (backup) =>
|
||||||
entry || this.supervisor.localize("backup.data_disk"),
|
backup.location || this.supervisor.localize("backup.data_disk"),
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
title: this.supervisor.localize("backup.created"),
|
title: this.supervisor.localize("backup.created"),
|
||||||
@@ -152,8 +156,8 @@ export class HassioBackups extends LitElement {
|
|||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
template: (entry: string) =>
|
template: (backup) =>
|
||||||
relativeTime(new Date(entry), this.hass.locale),
|
relativeTime(new Date(backup.date), this.hass.locale),
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
title: "",
|
title: "",
|
||||||
@@ -163,7 +167,7 @@ export class HassioBackups extends LitElement {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
private _backupData = memoizeOne((backups: HassioBackup[]) =>
|
private _backupData = memoizeOne((backups: HassioBackup[]): BackupItem[] =>
|
||||||
backups.map((backup) => ({
|
backups.map((backup) => ({
|
||||||
...backup,
|
...backup,
|
||||||
secondary: this._computeBackupContent(backup),
|
secondary: this._computeBackupContent(backup),
|
||||||
@@ -356,11 +360,9 @@ export class HassioBackups extends LitElement {
|
|||||||
if (this.supervisor!.info.state !== "running") {
|
if (this.supervisor!.info.state !== "running") {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor!.localize("backup.could_not_create"),
|
title: this.supervisor!.localize("backup.could_not_create"),
|
||||||
text: this.supervisor!.localize(
|
text: this.supervisor!.localize("backup.create_blocked_not_running", {
|
||||||
"backup.create_blocked_not_running",
|
state: this.supervisor!.info.state,
|
||||||
"state",
|
}),
|
||||||
this.supervisor!.info.state
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
|
import type { IFuseOptions } from "fuse.js";
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||||
|
|
||||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
||||||
const options: Fuse.IFuseOptions<StoreAddon> = {
|
const options: IFuseOptions<StoreAddon> = {
|
||||||
keys: ["name", "description", "slug"],
|
keys: ["name", "description", "slug"],
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
minMatchCharLength: 2,
|
minMatchCharLength: 2,
|
||||||
|
@@ -31,8 +31,8 @@ export class HassioUploadBackup extends LitElement {
|
|||||||
.icon=${mdiFolderUpload}
|
.icon=${mdiFolderUpload}
|
||||||
accept="application/x-tar"
|
accept="application/x-tar"
|
||||||
label="Upload backup"
|
label="Upload backup"
|
||||||
|
supports="Supports .TAR files"
|
||||||
@file-picked=${this._uploadFile}
|
@file-picked=${this._uploadFile}
|
||||||
auto-open-file-dialog
|
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { mdiFolder, mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
|
import { mdiFolder, mdiPuzzle } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
|
||||||
LitElement,
|
LitElement,
|
||||||
nothing,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
HassioPartialBackupCreateParams,
|
HassioPartialBackupCreateParams,
|
||||||
} from "../../../src/data/hassio/backup";
|
} from "../../../src/data/hassio/backup";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
|
import { mdiHomeAssistant } from "../../../src/resources/home-assistant-logo-svg";
|
||||||
import {
|
import {
|
||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
TranslationDict,
|
TranslationDict,
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { mdiHomeAssistant } from "@mdi/js";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
|
import { mdiHomeAssistant } from "../../../src/resources/home-assistant-logo-svg";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
@@ -31,6 +31,7 @@ import { fileDownload } from "../../../../src/util/file_download";
|
|||||||
import "../../components/supervisor-backup-content";
|
import "../../components/supervisor-backup-content";
|
||||||
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||||
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
||||||
|
import { BackupOrRestoreKey } from "../../util/translations";
|
||||||
|
|
||||||
@customElement("dialog-hassio-backup")
|
@customElement("dialog-hassio-backup")
|
||||||
class HassioBackupDialog
|
class HassioBackupDialog
|
||||||
@@ -64,6 +65,13 @@ class HassioBackupDialog
|
|||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _localize(key: BackupOrRestoreKey) {
|
||||||
|
return (
|
||||||
|
this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
|
||||||
|
this._dialogParams!.localize!(`ui.panel.page-onboarding.restore.${key}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._dialogParams || !this._backup) {
|
if (!this._dialogParams || !this._backup) {
|
||||||
return nothing;
|
return nothing;
|
||||||
@@ -79,7 +87,7 @@ class HassioBackupDialog
|
|||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title">${this._backup.name}</span>
|
<span slot="title">${this._backup.name}</span>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass?.localize("ui.common.close") || "Close"}
|
.label=${this._localize("close")}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
slot="actionItems"
|
slot="actionItems"
|
||||||
dialogAction="cancel"
|
dialogAction="cancel"
|
||||||
@@ -87,29 +95,31 @@ class HassioBackupDialog
|
|||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
</div>
|
</div>
|
||||||
${this._restoringBackup
|
${this._restoringBackup
|
||||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||||
: html`<supervisor-backup-content
|
: html`
|
||||||
.hass=${this.hass}
|
<supervisor-backup-content
|
||||||
.supervisor=${this._dialogParams.supervisor}
|
.hass=${this.hass}
|
||||||
.backup=${this._backup}
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
.onboarding=${this._dialogParams.onboarding || false}
|
.backup=${this._backup}
|
||||||
.localize=${this._dialogParams.localize}
|
.onboarding=${this._dialogParams.onboarding || false}
|
||||||
dialogInitialFocus
|
.localize=${this._dialogParams.localize}
|
||||||
>
|
dialogInitialFocus
|
||||||
</supervisor-backup-content>`}
|
>
|
||||||
|
</supervisor-backup-content>
|
||||||
|
`}
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: nothing}
|
||||||
|
|
||||||
<mwc-button
|
<mwc-button
|
||||||
.disabled=${this._restoringBackup}
|
.disabled=${this._restoringBackup}
|
||||||
slot="secondaryAction"
|
slot="secondaryAction"
|
||||||
@click=${this._restoreClicked}
|
@click=${this._restoreClicked}
|
||||||
>
|
>
|
||||||
Restore
|
${this._localize("restore")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
|
|
||||||
${!this._dialogParams.onboarding
|
${!this._dialogParams.onboarding && this._dialogParams.supervisor
|
||||||
? html`<ha-button-menu
|
? html`<ha-button-menu
|
||||||
fixed
|
fixed
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@@ -117,22 +127,24 @@ class HassioBackupDialog
|
|||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass!.localize("ui.common.menu") || "Menu"}
|
.label=${this._dialogParams.supervisor.localize(
|
||||||
|
"backup.more_actions"
|
||||||
|
)}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
>${this._dialogParams.supervisor?.localize(
|
>${this._dialogParams.supervisor.localize(
|
||||||
"backup.download_backup"
|
"backup.download_backup"
|
||||||
)}</mwc-list-item
|
)}</mwc-list-item
|
||||||
>
|
>
|
||||||
<mwc-list-item class="error"
|
<mwc-list-item class="error"
|
||||||
>${this._dialogParams.supervisor?.localize(
|
>${this._dialogParams.supervisor.localize(
|
||||||
"backup.delete_backup_title"
|
"backup.delete_backup_title"
|
||||||
)}</mwc-list-item
|
)}</mwc-list-item
|
||||||
>
|
>
|
||||||
</ha-button-menu>`
|
</ha-button-menu>`
|
||||||
: ""}
|
: nothing}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -173,6 +185,7 @@ class HassioBackupDialog
|
|||||||
private async _restoreClicked() {
|
private async _restoreClicked() {
|
||||||
const backupDetails = this._backupContent.backupDetails();
|
const backupDetails = this._backupContent.backupDetails();
|
||||||
this._restoringBackup = true;
|
this._restoringBackup = true;
|
||||||
|
this._dialogParams?.onRestoring?.();
|
||||||
if (this._backupContent.backupType === "full") {
|
if (this._backupContent.backupType === "full") {
|
||||||
await this._fullRestoreClicked(backupDetails);
|
await this._fullRestoreClicked(backupDetails);
|
||||||
} else {
|
} else {
|
||||||
@@ -182,21 +195,22 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _partialRestoreClicked(backupDetails) {
|
private async _partialRestoreClicked(backupDetails) {
|
||||||
if (
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
this._dialogParams?.supervisor !== undefined &&
|
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||||
this._dialogParams?.supervisor.info.state !== "running"
|
|
||||||
) {
|
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore backup",
|
title: supervisor.localize("backup.could_not_restore"),
|
||||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
text: supervisor.localize("backup.restore_blocked_not_running", {
|
||||||
|
state: supervisor.info.state,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: "Are you sure you want to restore this partial backup?",
|
title: this._localize("confirm_restore_partial_backup_title"),
|
||||||
confirmText: "restore",
|
text: this._localize("confirm_restore_partial_backup_text"),
|
||||||
dismissText: "cancel",
|
confirmText: this._localize("restore"),
|
||||||
|
dismissText: this._localize("cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -219,7 +233,7 @@ class HassioBackupDialog
|
|||||||
this._error = error.body.message;
|
this._error = error.body.message;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
this._dialogParams?.onRestoring?.();
|
||||||
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(backupDetails),
|
body: JSON.stringify(backupDetails),
|
||||||
@@ -229,22 +243,22 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _fullRestoreClicked(backupDetails) {
|
private async _fullRestoreClicked(backupDetails) {
|
||||||
if (
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
this._dialogParams?.supervisor !== undefined &&
|
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||||
this._dialogParams?.supervisor.info.state !== "running"
|
|
||||||
) {
|
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore backup",
|
title: supervisor.localize("backup.could_not_restore"),
|
||||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
text: supervisor.localize("backup.restore_blocked_not_running", {
|
||||||
|
state: supervisor.info.state,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title:
|
title: this._localize("confirm_restore_full_backup_title"),
|
||||||
"Are you sure you want to wipe your system and restore this backup?",
|
text: this._localize("confirm_restore_full_backup_text"),
|
||||||
confirmText: "restore",
|
confirmText: this._localize("restore"),
|
||||||
dismissText: "cancel",
|
dismissText: this._localize("cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -268,7 +282,7 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
this._dialogParams?.onRestoring?.();
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(backupDetails),
|
body: JSON.stringify(backupDetails),
|
||||||
@@ -278,11 +292,15 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _deleteClicked() {
|
private async _deleteClicked() {
|
||||||
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
|
if (!supervisor) return;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: "Are you sure you want to delete this backup?",
|
title: supervisor!.localize("backup.confirm_delete_title"),
|
||||||
confirmText: "delete",
|
text: supervisor!.localize("backup.confirm_delete_text"),
|
||||||
dismissText: "cancel",
|
confirmText: supervisor!.localize("backup.delete"),
|
||||||
|
dismissText: supervisor!.localize("backup.cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -300,6 +318,9 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _downloadClicked() {
|
private async _downloadClicked() {
|
||||||
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
|
if (!supervisor) return;
|
||||||
|
|
||||||
let signedPath: { path: string };
|
let signedPath: { path: string };
|
||||||
try {
|
try {
|
||||||
signedPath = await getSignedPath(
|
signedPath = await getSignedPath(
|
||||||
@@ -319,10 +340,10 @@ class HassioBackupDialog
|
|||||||
|
|
||||||
if (window.location.href.includes("ui.nabu.casa")) {
|
if (window.location.href.includes("ui.nabu.casa")) {
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: "Potential slow download",
|
title: supervisor.localize("backup.remote_download_title"),
|
||||||
text: "Downloading backups over the Nabu Casa URL will take some time, it is recomended to use your local URL instead, do you want to continue?",
|
text: supervisor.localize("backup.remote_download_text"),
|
||||||
confirmText: "continue",
|
confirmText: supervisor.localize("backup.download"),
|
||||||
dismissText: "cancel",
|
dismissText: this._localize("cancel"),
|
||||||
});
|
});
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
return;
|
return;
|
||||||
|
@@ -89,8 +89,7 @@ class HassioCreateBackupDialog extends LitElement {
|
|||||||
),
|
),
|
||||||
text: this._dialogParams!.supervisor.localize(
|
text: this._dialogParams!.supervisor.localize(
|
||||||
"backup.create_blocked_not_running",
|
"backup.create_blocked_not_running",
|
||||||
"state",
|
{ state: this._dialogParams!.supervisor.info.state }
|
||||||
this._dialogParams!.supervisor.info.state
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@@ -5,6 +5,7 @@ import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
|||||||
export interface HassioBackupDialogParams {
|
export interface HassioBackupDialogParams {
|
||||||
slug: string;
|
slug: string;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
|
onRestoring?: () => void;
|
||||||
onboarding?: boolean;
|
onboarding?: boolean;
|
||||||
supervisor?: Supervisor;
|
supervisor?: Supervisor;
|
||||||
localize?: LocalizeFunc;
|
localize?: LocalizeFunc;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
|
import { dump } from "js-yaml";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -9,7 +10,6 @@ import "../../../../src/components/ha-expansion-panel";
|
|||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/search-input";
|
import "../../../../src/components/search-input";
|
||||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||||
import { dump } from "../../../../src/resources/js-yaml-dump";
|
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
|
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../../src/resources/compatibility";
|
import "../../src/resources/compatibility";
|
||||||
import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings";
|
|
||||||
import "../../src/resources/roboto";
|
|
||||||
import "../../src/resources/ha-style";
|
|
||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
import "../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "./hassio-main";
|
import "./hassio-main";
|
||||||
|
|
||||||
setCancelSyntheticClickEvents(false);
|
import("../../src/resources/ha-style");
|
||||||
|
import("@polymer/polymer/lib/utils/settings").then(
|
||||||
|
({ setCancelSyntheticClickEvents }) => setCancelSyntheticClickEvents(false)
|
||||||
|
);
|
||||||
|
|
||||||
const styleEl = document.createElement("style");
|
const styleEl = document.createElement("style");
|
||||||
styleEl.innerHTML = `
|
styleEl.textContent = `
|
||||||
body {
|
body {
|
||||||
font-family: Roboto, sans-serif;
|
font-family: Roboto, sans-serif;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
4
hassio/src/util/translations.ts
Normal file
4
hassio/src/util/translations.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import type { TranslationDict } from "../../../src/types";
|
||||||
|
|
||||||
|
export type BackupOrRestoreKey = keyof TranslationDict["supervisor"]["backup"] &
|
||||||
|
keyof TranslationDict["ui"]["panel"]["page-onboarding"]["restore"];
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user