Compare commits
589 Commits
delay-init
...
20231204.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ae2e8e7402 | ||
![]() |
6d0eb05954 | ||
![]() |
96fbbc55e1 | ||
![]() |
7e74502ba3 | ||
![]() |
b273b31b03 | ||
![]() |
cf00ac74c6 | ||
![]() |
cc702f0fb3 | ||
![]() |
cc6eb5789d | ||
![]() |
669b80a9e1 | ||
![]() |
4c4fddee94 | ||
![]() |
b28a4e6b50 | ||
![]() |
bdd6a55a84 | ||
![]() |
d1ebc06994 | ||
![]() |
2a150788b4 | ||
![]() |
b854d23431 | ||
![]() |
72caf72e80 | ||
![]() |
a6ff1899df | ||
![]() |
6a999597df | ||
![]() |
219fc9e53a | ||
![]() |
861959ed2d | ||
![]() |
f7f50294e7 | ||
![]() |
a226333c1e | ||
![]() |
75f18aa69b | ||
![]() |
60ff4fdc1f | ||
![]() |
8d14cb0ab7 | ||
![]() |
606c809880 | ||
![]() |
f1692038f8 | ||
![]() |
79f5ec0cd5 | ||
![]() |
416a2a080d | ||
![]() |
92dc8b1561 | ||
![]() |
07b807adfc | ||
![]() |
843430ef41 | ||
![]() |
b5b2392dd2 | ||
![]() |
ef735d65cf | ||
![]() |
bf24740c7b | ||
![]() |
4ec11c7cf6 | ||
![]() |
2803e6aa95 | ||
![]() |
8ff36d0ccf | ||
![]() |
e7e465df69 | ||
![]() |
0c042079ed | ||
![]() |
a5a9bcafa7 | ||
![]() |
867f625b0a | ||
![]() |
3876c67588 | ||
![]() |
93158bb3af | ||
![]() |
5c47d8652d | ||
![]() |
eda0d12867 | ||
![]() |
4a53de4466 | ||
![]() |
e730649a8d | ||
![]() |
3a94deef69 | ||
![]() |
c1c186d279 | ||
![]() |
7356db919a | ||
![]() |
44157a5df3 | ||
![]() |
2acd1ff5e8 | ||
![]() |
150a5571cf | ||
![]() |
1eb25f4829 | ||
![]() |
d8d4ecf79f | ||
![]() |
081bd98e98 | ||
![]() |
6134f415e9 | ||
![]() |
b6a7581eca | ||
![]() |
7727f34e8f | ||
![]() |
9b20e1cf56 | ||
![]() |
b901ecacca | ||
![]() |
7691f7eb05 | ||
![]() |
8c0c56b954 | ||
![]() |
d968a20862 | ||
![]() |
9f1cd80a31 | ||
![]() |
5a9ccc5ae7 | ||
![]() |
6d7b0c5626 | ||
![]() |
400b8034e1 | ||
![]() |
05a22e3271 | ||
![]() |
c1f76e0565 | ||
![]() |
4582c3ba0a | ||
![]() |
f4ee734ea3 | ||
![]() |
2087028c47 | ||
![]() |
37a56b250a | ||
![]() |
71a41be20a | ||
![]() |
ffb19b31a5 | ||
![]() |
db68c7faa9 | ||
![]() |
07ae958eb0 | ||
![]() |
b62b5f6575 | ||
![]() |
2e1fb9df66 | ||
![]() |
31c91cea9a | ||
![]() |
9dc844ca28 | ||
![]() |
02ebc028c8 | ||
![]() |
5d35605b82 | ||
![]() |
22e86af5b3 | ||
![]() |
8c39ed46a8 | ||
![]() |
5965c3fdaa | ||
![]() |
15a5d2bc38 | ||
![]() |
0432cc95fc | ||
![]() |
6dafaac021 | ||
![]() |
d8929c9252 | ||
![]() |
5fa3a720b3 | ||
![]() |
6d3b838b6a | ||
![]() |
58e0179321 | ||
![]() |
eaf7e29b8a | ||
![]() |
260f1df1b9 | ||
![]() |
a88a4ef82c | ||
![]() |
075cca5991 | ||
![]() |
1d2dc37f75 | ||
![]() |
3da7025d78 | ||
![]() |
15b1d8ee14 | ||
![]() |
239d8fa948 | ||
![]() |
1de2e6161d | ||
![]() |
3e7f008277 | ||
![]() |
31a93d360d | ||
![]() |
2afd2788e2 | ||
![]() |
322fa99147 | ||
![]() |
c43ad52ff1 | ||
![]() |
6aa9737481 | ||
![]() |
7d9d6b99d9 | ||
![]() |
9c69e6f87c | ||
![]() |
0c90021895 | ||
![]() |
9163b9c124 | ||
![]() |
61717e1529 | ||
![]() |
168d418c27 | ||
![]() |
c787c920fc | ||
![]() |
1526209f82 | ||
![]() |
cf355c419d | ||
![]() |
eef024587b | ||
![]() |
270d463d02 | ||
![]() |
0735705dd3 | ||
![]() |
0a1dbe6d9d | ||
![]() |
15395275ba | ||
![]() |
6e66ba202f | ||
![]() |
5ebe1e0369 | ||
![]() |
860ab7a3ba | ||
![]() |
d499640304 | ||
![]() |
4f3f516767 | ||
![]() |
0996dbf2a1 | ||
![]() |
bc59e52cea | ||
![]() |
b8d4e957e2 | ||
![]() |
8002ec75bc | ||
![]() |
a45eefa742 | ||
![]() |
a1236924aa | ||
![]() |
9320e8c1a3 | ||
![]() |
addcd3a983 | ||
![]() |
e1dc73e992 | ||
![]() |
ba8849ed4d | ||
![]() |
36bca04341 | ||
![]() |
fb95de1f73 | ||
![]() |
3addfc3548 | ||
![]() |
e2e80d1f49 | ||
![]() |
a96eba2637 | ||
![]() |
274810b954 | ||
![]() |
842df983c3 | ||
![]() |
0eeadcd31a | ||
![]() |
8d37c5612b | ||
![]() |
e94461f7fe | ||
![]() |
f5edee1e91 | ||
![]() |
98cdbb4559 | ||
![]() |
f3f63d95b5 | ||
![]() |
244340f211 | ||
![]() |
75626431b1 | ||
![]() |
244868348b | ||
![]() |
a909c6d905 | ||
![]() |
f2efff9d71 | ||
![]() |
08a7a10e1c | ||
![]() |
b35e5abd83 | ||
![]() |
291e9fc0a2 | ||
![]() |
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 | ||
![]() |
18b5215ce6 | ||
![]() |
00a311bd63 | ||
![]() |
749c2ab8ac | ||
![]() |
d06fdeb265 | ||
![]() |
06d76be2c2 | ||
![]() |
6728e8d107 | ||
![]() |
533e2bed8a | ||
![]() |
310a08f3e6 | ||
![]() |
0566889a1e | ||
![]() |
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 | ||
![]() |
ddca7584ef | ||
![]() |
e6cfe74cac | ||
![]() |
d4218250af | ||
![]() |
78783942be | ||
![]() |
17ad8013ca | ||
![]() |
f70767f084 | ||
![]() |
3972394d82 | ||
![]() |
5331fb4f5f | ||
![]() |
16df352ba8 | ||
![]() |
3b553a3a4b | ||
![]() |
8e1e75dee1 | ||
![]() |
d27b4e04a9 | ||
![]() |
36f7b34ac5 | ||
![]() |
c0ff24bf1b | ||
![]() |
abb5aa348f | ||
![]() |
37ab5cbdc3 | ||
![]() |
42be3b331c | ||
![]() |
71e05f79c2 | ||
![]() |
c5a0a5bbbf | ||
![]() |
c3d809fcf3 | ||
![]() |
cbd0c39091 | ||
![]() |
113eb5be24 | ||
![]() |
c497669fd3 | ||
![]() |
c32ca5885b | ||
![]() |
3c792c4019 | ||
![]() |
9762e61ee2 | ||
![]() |
c09c39998b | ||
![]() |
d37e29c247 | ||
![]() |
51f22cd74a | ||
![]() |
b57dc968bd | ||
![]() |
e2e8cb785a | ||
![]() |
3d674cf237 | ||
![]() |
9961a4ae3f | ||
![]() |
c25755bfcd | ||
![]() |
40983619d6 | ||
![]() |
54758b5962 | ||
![]() |
4f09485b20 | ||
![]() |
9207f6c407 | ||
![]() |
1a2312460a | ||
![]() |
951b88ab4c | ||
![]() |
d3cc57d8b4 | ||
![]() |
4e9b118728 | ||
![]() |
4e6e924a40 | ||
![]() |
f1748e4dd5 | ||
![]() |
d491d8f5ac | ||
![]() |
cf0fde0f3c | ||
![]() |
a7dc2cfaa6 | ||
![]() |
d8c7db6ebf | ||
![]() |
c3743b57ea | ||
![]() |
e16a101de8 | ||
![]() |
94ad47c60e | ||
![]() |
184ef7b7ff | ||
![]() |
81053f2e07 | ||
![]() |
e8b4eeec67 | ||
![]() |
b0b7e77e28 | ||
![]() |
073ead5828 | ||
![]() |
763f80b46a | ||
![]() |
0c2531a7ee | ||
![]() |
8d2ec8098c | ||
![]() |
fdaefadd18 | ||
![]() |
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 | ||
![]() |
40bb6566b8 | ||
![]() |
db272e3e18 | ||
![]() |
35496ead23 | ||
![]() |
41403a5d35 | ||
![]() |
8acf557137 | ||
![]() |
4e62370d18 | ||
![]() |
f90ab60354 | ||
![]() |
d187aa0ac6 | ||
![]() |
96597b3963 |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -9,7 +9,7 @@ body:
|
||||
|
||||
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
|
||||
|
||||
**Please not not report issues for custom cards.**
|
||||
**Please do not report issues for custom cards.**
|
||||
|
||||
[fr]: https://github.com/home-assistant/frontend/discussions
|
||||
[releases]: https://github.com/home-assistant/home-assistant/releases
|
||||
|
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,9 +2,7 @@
|
||||
You are amazing! Thanks for contributing to our project!
|
||||
Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
|
||||
-->
|
||||
|
||||
## Breaking change
|
||||
|
||||
<!--
|
||||
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.
|
||||
@@ -13,8 +11,8 @@
|
||||
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
|
||||
maintainers why we should accept this pull request. If it fixes a bug
|
||||
@@ -22,8 +20,8 @@
|
||||
in the additional information section.
|
||||
-->
|
||||
|
||||
## Type of change
|
||||
|
||||
## Type of change
|
||||
<!--
|
||||
What type of change does your PR introduce to the Home Assistant frontend?
|
||||
NOTE: Please, check only 1! box!
|
||||
@@ -38,7 +36,6 @@
|
||||
- [ ] Code quality improvements to existing code or addition of tests
|
||||
|
||||
## Example configuration
|
||||
|
||||
<!--
|
||||
Supplying a configuration snippet, makes it easier for a maintainer to test
|
||||
your PR.
|
||||
@@ -49,7 +46,6 @@
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
<!--
|
||||
Details are important, and help maintainers processing your PR.
|
||||
Please be sure to fill out additional details, if applicable.
|
||||
@@ -60,7 +56,6 @@
|
||||
- Link to documentation pull request:
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--
|
||||
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.
|
||||
|
8
.github/workflows/cast_deployment.yaml
vendored
@@ -21,12 +21,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -57,12 +57,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
28
.github/workflows/ci.yaml
vendored
@@ -24,9 +24,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -55,9 +55,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -73,9 +73,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -85,15 +85,21 @@ jobs:
|
||||
run: ./node_modules/.bin/gulp build-app
|
||||
env:
|
||||
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:
|
||||
name: Build supervisor
|
||||
needs: [lint, test]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -103,3 +109,9 @@ jobs:
|
||||
run: ./node_modules/.bin/gulp build-hassio
|
||||
env:
|
||||
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
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
|
8
.github/workflows/demo_deployment.yaml
vendored
@@ -22,12 +22,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -58,12 +58,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
4
.github/workflows/design_deployment.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
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')
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
3
.github/workflows/lock.yml
vendored
@@ -9,9 +9,10 @@ jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4.0.1
|
||||
- uses: dessant/lock-threads@v5.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
process-only: "issues, prs"
|
||||
issue-lock-inactive-days: "30"
|
||||
issue-exclude-created-before: "2020-10-01T00:00:00Z"
|
||||
issue-lock-reason: ""
|
||||
|
8
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@v4
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -57,14 +57,14 @@ jobs:
|
||||
run: tar -czvf translations.tar.gz translations
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist/home_assistant_frontend*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
|
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
@@ -23,7 +23,7 @@ jobs:
|
||||
contents: write # Required to upload release assets
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Verify version
|
||||
uses: home-assistant/actions/helpers/verify-version@master
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2023.04.0
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: cp311
|
||||
tag: musllinux_1_2
|
||||
|
2
.github/workflows/translations.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.0.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
|
3
.gitignore
vendored
@@ -47,3 +47,6 @@ src/cast/dev_const.ts
|
||||
|
||||
# Home Assistant config
|
||||
/config/
|
||||
|
||||
# Jetbrains
|
||||
/.idea/
|
||||
|
@@ -1,3 +1,4 @@
|
||||
CLA.md
|
||||
CODE_OF_CONDUCT.md
|
||||
LICENSE.md
|
||||
PULL_REQUEST_TEMPLATE.md
|
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
874
.yarn/releases/yarn-3.6.3.cjs
vendored
893
.yarn/releases/yarn-4.0.2.cjs
vendored
Executable file
12
.yarnrc.yml
@@ -1,11 +1,9 @@
|
||||
compressionLevel: mixed
|
||||
|
||||
defaultSemverRangePrefix: ""
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.0.2.cjs
|
||||
|
56
build-scripts/babel-plugins/custom-polyfill-plugin.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import defineProvider from "@babel/helper-define-polyfill-provider";
|
||||
|
||||
// List of polyfill keys with supported browser targets for the functionality
|
||||
const PolyfillSupport = {
|
||||
fetch: {
|
||||
android: 42,
|
||||
chrome: 42,
|
||||
edge: 14,
|
||||
firefox: 39,
|
||||
ios: 10.3,
|
||||
opera: 29,
|
||||
opera_mobile: 29,
|
||||
safari: 10.1,
|
||||
samsung: 4.0,
|
||||
},
|
||||
proxy: {
|
||||
android: 49,
|
||||
chrome: 49,
|
||||
edge: 12,
|
||||
firefox: 18,
|
||||
ios: 10.0,
|
||||
opera: 36,
|
||||
opera_mobile: 36,
|
||||
safari: 10.0,
|
||||
samsung: 5.0,
|
||||
},
|
||||
};
|
||||
|
||||
// Map of global variables and/or instance and static properties to the
|
||||
// corresponding polyfill key and actual module to import
|
||||
const polyfillMap = {
|
||||
global: {
|
||||
Proxy: { key: "proxy", module: "proxy-polyfill" },
|
||||
fetch: { key: "fetch", module: "unfetch/polyfill" },
|
||||
},
|
||||
instance: {},
|
||||
static: {},
|
||||
};
|
||||
|
||||
// Create plugin using the same factory as for CoreJS
|
||||
export default defineProvider(
|
||||
({ createMetaResolver, debug, shouldInjectPolyfill }) => {
|
||||
const resolvePolyfill = createMetaResolver(polyfillMap);
|
||||
return {
|
||||
name: "HA Custom",
|
||||
polyfills: PolyfillSupport,
|
||||
usageGlobal(meta, utils) {
|
||||
const polyfill = resolvePolyfill(meta);
|
||||
if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) {
|
||||
debug(polyfill.desc.key);
|
||||
utils.injectGlobalImport(polyfill.desc.module);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
@@ -12,11 +12,7 @@ module.exports.sourceMapURL = () => {
|
||||
};
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
module.exports.ignorePackages = () => [];
|
||||
|
||||
// Files from NPM packages that we should replace with empty file
|
||||
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
@@ -35,8 +31,6 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
require.resolve(
|
||||
path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
|
||||
),
|
||||
// This polyfill is loaded in workers to support ES5, filter it out.
|
||||
latestBuild && require.resolve("proxy-polyfill/src/index.js"),
|
||||
// Icons in supervisor conflict with icons in HA so we don't load.
|
||||
isHassioBuild &&
|
||||
require.resolve(
|
||||
@@ -91,14 +85,12 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
setSpreadProperties: true,
|
||||
},
|
||||
browserslistEnv: latestBuild ? "modern" : "legacy",
|
||||
// Must be unambiguous because some dependencies are CommonJS only
|
||||
sourceType: "unambiguous",
|
||||
presets: [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
useBuiltIns: latestBuild ? false : "entry",
|
||||
corejs: latestBuild ? false : { version: "3.32", proposals: true },
|
||||
useBuiltIns: latestBuild ? false : "usage",
|
||||
corejs: latestBuild ? false : "3.33",
|
||||
bugfixes: true,
|
||||
shippedProposals: true,
|
||||
},
|
||||
@@ -116,21 +108,33 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
ignoreModuleNotFound: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
"build-scripts/babel-plugins/custom-polyfill-plugin.js"
|
||||
),
|
||||
{ method: "usage-global" },
|
||||
],
|
||||
// Minify template literals for production
|
||||
isProdBuild && [
|
||||
"template-html-minifier",
|
||||
{
|
||||
modules: {
|
||||
lit: [
|
||||
"html",
|
||||
{ name: "svg", encapsulation: "svg" },
|
||||
{ name: "css", encapsulation: "style" },
|
||||
],
|
||||
"@polymer/polymer/lib/utils/html-tag": ["html"],
|
||||
...Object.fromEntries(
|
||||
["lit", "lit-element", "lit-html"].map((m) => [
|
||||
m,
|
||||
[
|
||||
"html",
|
||||
{ name: "svg", encapsulation: "svg" },
|
||||
{ name: "css", encapsulation: "style" },
|
||||
],
|
||||
])
|
||||
),
|
||||
"@polymer/polymer/lib/utils/html-tag.js": ["html"],
|
||||
},
|
||||
strictCSS: true,
|
||||
htmlMinifier: module.exports.htmlMinifierOptions,
|
||||
failOnError: true, // we can turn this off in case of false positives
|
||||
failOnError: false, // we can turn this off in case of false positives
|
||||
},
|
||||
],
|
||||
// Import helpers and regenerator from runtime package
|
||||
@@ -147,9 +151,21 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
/node_modules[\\/]webpack[\\/]buildin/,
|
||||
],
|
||||
sourceMaps: !isTestBuild,
|
||||
overrides: [
|
||||
{
|
||||
// Use unambiguous for dependencies so that require() is correctly injected into CommonJS files
|
||||
// Exclusions are needed in some cases where ES modules have no static imports or exports, such as polyfills
|
||||
sourceType: "unambiguous",
|
||||
include: /\/node_modules\//,
|
||||
exclude: [
|
||||
"element-internals-polyfill",
|
||||
"@?lit(?:-labs|-element|-html)?",
|
||||
].map((p) => new RegExp(`/node_modules/${p}/`)),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const nameSuffix = (latestBuild) => (latestBuild ? "-latest" : "-es5");
|
||||
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
@@ -183,7 +199,7 @@ const publicPath = (latestBuild, root = "") =>
|
||||
module.exports.config = {
|
||||
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
||||
return {
|
||||
name: "app" + nameSuffix(latestBuild),
|
||||
name: "frontend" + nameSuffix(latestBuild),
|
||||
entry: {
|
||||
service_worker: "./src/entrypoints/service_worker.ts",
|
||||
app: "./src/entrypoints/app.ts",
|
||||
|
@@ -30,8 +30,8 @@ gulp.task(
|
||||
env.useWDS()
|
||||
? "wds-watch-app"
|
||||
: env.useRollup()
|
||||
? "rollup-watch-app"
|
||||
: "webpack-watch-app"
|
||||
? "rollup-watch-app"
|
||||
: "webpack-watch-app"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -45,8 +45,8 @@ gulp.task(
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
"copy-static-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
|
||||
...(env.isTestBuild() ? [] : ["compress-app"]),
|
||||
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod")
|
||||
...(env.isTestBuild() ? [] : ["compress-app"])
|
||||
)
|
||||
);
|
||||
|
@@ -161,6 +161,10 @@ gulp.task("fetch-lokalise", async function () {
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
throw err;
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
@@ -4,6 +4,7 @@ import fs from "fs-extra";
|
||||
import gulp from "gulp";
|
||||
import path from "path";
|
||||
import paths from "../paths.cjs";
|
||||
import env from "../env.cjs";
|
||||
|
||||
const npmPath = (...parts) =>
|
||||
path.resolve(paths.polymer_dir, "node_modules", ...parts);
|
||||
@@ -62,6 +63,9 @@ function copyPolyfills(staticDir) {
|
||||
}
|
||||
|
||||
function copyLoaderJS(staticDir) {
|
||||
if (!env.useRollup()) {
|
||||
return;
|
||||
}
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
|
||||
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
|
||||
|
@@ -1,51 +1,54 @@
|
||||
import { deleteSync } from "del";
|
||||
import { mkdir, readFile, writeFile } from "fs/promises";
|
||||
import gulp from "gulp";
|
||||
import path from "path";
|
||||
import { join, resolve } from "node:path";
|
||||
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 = {
|
||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||
const INTL_POLYFILLS = {
|
||||
"intl-datetimeformat": "DateTimeFormat",
|
||||
"intl-numberformat": "NumberFormat",
|
||||
"intl-displaynames": "DisplayNames",
|
||||
"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;
|
||||
try {
|
||||
localeData = await readFile(
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
`node_modules/@formatjs/${pkg}/locale-data/${lang}.js`
|
||||
),
|
||||
join(formatjsDir, pkg, subDir, `${lang}.js`),
|
||||
"utf-8"
|
||||
);
|
||||
} catch (e) {
|
||||
// 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;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
// Convert to JSON
|
||||
const className = INTL_PACKAGES[pkg];
|
||||
localeData = localeData
|
||||
.replace(
|
||||
new RegExp(
|
||||
`\\/\\*\\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"
|
||||
),
|
||||
""
|
||||
)
|
||||
.replace(/\)\s*}/im, "");
|
||||
const obj = INTL_POLYFILLS[pkg];
|
||||
const dataRegex = new RegExp(
|
||||
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
|
||||
"s"
|
||||
);
|
||||
localeData = localeData.match(dataRegex)?.groups?.data;
|
||||
if (!localeData) {
|
||||
throw Error(`Failed to extract data for language ${lang} from ${pkg}`);
|
||||
}
|
||||
// Parse to validate JSON, then stringify to minify
|
||||
localeData = JSON.stringify(JSON.parse(localeData));
|
||||
await writeFile(path.join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||
};
|
||||
|
||||
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 () => {
|
||||
const translationMeta = JSON.parse(
|
||||
await readFile(
|
||||
path.resolve(paths.translations_src, "translationMetadata.json"),
|
||||
resolve(paths.translations_src, "translationMetadata.json"),
|
||||
"utf-8"
|
||||
)
|
||||
);
|
||||
const conversions = [];
|
||||
for (const pkg of Object.keys(INTL_PACKAGES)) {
|
||||
await mkdir(path.join(outDir, pkg), { recursive: true });
|
||||
for (const pkg of Object.keys(INTL_POLYFILLS)) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await mkdir(join(outDir, pkg), { recursive: true });
|
||||
for (const lang of Object.keys(translationMeta)) {
|
||||
conversions.push(convertToJSON(pkg, lang));
|
||||
}
|
||||
}
|
||||
conversions.push(
|
||||
convertToJSON(
|
||||
"intl-datetimeformat",
|
||||
"add-all-tz",
|
||||
".",
|
||||
"__addTZData",
|
||||
false
|
||||
)
|
||||
);
|
||||
await Promise.all(conversions);
|
||||
});
|
||||
|
||||
|
@@ -1,12 +1,7 @@
|
||||
import { createHash } from "crypto";
|
||||
import { deleteSync } from "del";
|
||||
import {
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
writeFile,
|
||||
} from "fs";
|
||||
import { mkdirSync, readdirSync, readFileSync, renameSync } from "fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import gulp from "gulp";
|
||||
import flatmap from "gulp-flatmap";
|
||||
import transform from "gulp-json-transform";
|
||||
@@ -136,27 +131,23 @@ gulp.task("ensure-translations-build-dir", async () => {
|
||||
mkdirSync(workDir, { recursive: true });
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", (cb) => {
|
||||
writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
test: {
|
||||
nativeName: "Test",
|
||||
},
|
||||
}),
|
||||
cb
|
||||
);
|
||||
});
|
||||
gulp.task("create-test-metadata", () =>
|
||||
env.isProdBuild()
|
||||
? Promise.resolve()
|
||||
: writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({ test: { nativeName: "Test" } })
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"create-test-translation",
|
||||
gulp.series("create-test-metadata", () =>
|
||||
gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
)
|
||||
gulp.task("create-test-translation", () =>
|
||||
env.isProdBuild()
|
||||
? Promise.resolve()
|
||||
: gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -188,16 +179,11 @@ gulp.task("build-master-translation", () => {
|
||||
|
||||
gulp.task("build-merged-translations", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
inFrontendDir + "/*.json",
|
||||
"!" + inFrontendDir + "/en.json",
|
||||
workDir + "/test.json",
|
||||
],
|
||||
{
|
||||
allowEmpty: true,
|
||||
}
|
||||
)
|
||||
.src([
|
||||
inFrontendDir + "/*.json",
|
||||
"!" + inFrontendDir + "/en.json",
|
||||
...(env.isProdBuild() ? [] : [workDir + "/test.json"]),
|
||||
])
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
flatmap((stream, file) => {
|
||||
@@ -377,14 +363,11 @@ gulp.task("build-translation-flatten-supervisor", () =>
|
||||
|
||||
gulp.task("build-translation-write-metadata", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.src([
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
...(env.isProdBuild() ? [] : [workDir + "/testMetadata.json"]),
|
||||
workDir + "/translationFingerprints.json",
|
||||
])
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform((data) => {
|
||||
@@ -415,7 +398,7 @@ gulp.task("build-translation-write-metadata", () =>
|
||||
gulp.task(
|
||||
"create-translations",
|
||||
gulp.series(
|
||||
...(env.isProdBuild() ? [] : ["create-test-translation"]),
|
||||
gulp.parallel("create-test-metadata", "create-test-translation"),
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
|
@@ -1,6 +1,8 @@
|
||||
const { existsSync } = require("fs");
|
||||
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 { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const log = require("fancy-log");
|
||||
@@ -49,8 +51,8 @@ const createWebpackConfig = ({
|
||||
devtool: isTestBuild
|
||||
? false
|
||||
: isProdBuild
|
||||
? "nosources-source-map"
|
||||
: "eval-cheap-module-source-map",
|
||||
? "nosources-source-map"
|
||||
: "eval-cheap-module-source-map",
|
||||
entry,
|
||||
node: false,
|
||||
module: {
|
||||
@@ -152,6 +154,15 @@ const createWebpackConfig = ({
|
||||
)
|
||||
),
|
||||
!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),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
@@ -165,11 +176,14 @@ const createWebpackConfig = ({
|
||||
"lit/directives/guard$": "lit/directives/guard.js",
|
||||
"lit/directives/cache$": "lit/directives/cache.js",
|
||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||
"lit/directives/live$": "lit/directives/live.js",
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
"@lit-labs/virtualizer/layouts/grid":
|
||||
"@lit-labs/virtualizer/layouts/grid.js",
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
||||
"@lit-labs/observers/resize-controller":
|
||||
"@lit-labs/observers/resize-controller.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
@@ -177,11 +191,12 @@ const createWebpackConfig = ({
|
||||
filename: ({ chunk }) =>
|
||||
!isProdBuild || isStatsBuild || dontHash.has(chunk.name)
|
||||
? "[name].js"
|
||||
: "[name]-[contenthash].js",
|
||||
: "[name].[contenthash].js",
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
|
||||
isProdBuild && !isStatsBuild ? "[name].[contenthash].js" : "[name].js",
|
||||
assetModuleFilename:
|
||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
|
||||
isProdBuild && !isStatsBuild ? "[id].[contenthash][ext]" : "[id][ext]",
|
||||
crossOriginLoading: "use-credentials",
|
||||
hashFunction: "xxhash64",
|
||||
hashDigest: "base64url",
|
||||
hashDigestLength: 11, // full length of 64 bit base64url
|
||||
|
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/ha-style";
|
||||
import "../../../src/resources/roboto";
|
||||
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-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
@@ -22,8 +22,9 @@ import "../../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
|
||||
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
@@ -38,10 +39,10 @@ class HcCast extends LitElement {
|
||||
|
||||
@state() private askWrite = false;
|
||||
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
if (this.lovelaceViews === undefined) {
|
||||
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
||||
}
|
||||
|
||||
@@ -72,43 +73,44 @@ class HcCast extends LitElement {
|
||||
${error
|
||||
? html` <div class="card-content">${error}</div> `
|
||||
: !this.castManager.status
|
||||
? html`
|
||||
<p class="center-item">
|
||||
<mwc-button raised @click=${this._handleLaunch}>
|
||||
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
|
||||
Start Casting
|
||||
</mwc-button>
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<div class="section-header">PICK A VIEW</div>
|
||||
<paper-listbox
|
||||
attr-for-selected="data-path"
|
||||
.selected=${this.castManager.status.lovelacePath || ""}
|
||||
>
|
||||
${(this.lovelaceConfig
|
||||
? this.lovelaceConfig.views
|
||||
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
|
||||
).map(
|
||||
(view, idx) => html`
|
||||
<paper-icon-item
|
||||
@click=${this._handlePickView}
|
||||
data-path=${view.path || idx}
|
||||
>
|
||||
${view.icon
|
||||
? html`
|
||||
<ha-icon
|
||||
.icon=${view.icon}
|
||||
slot="item-icon"
|
||||
></ha-icon>
|
||||
`
|
||||
: ""}
|
||||
${view.title || view.path}
|
||||
</paper-icon-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
`}
|
||||
? html`
|
||||
<p class="center-item">
|
||||
<mwc-button raised @click=${this._handleLaunch}>
|
||||
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
|
||||
Start Casting
|
||||
</mwc-button>
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<div class="section-header">PICK A VIEW</div>
|
||||
<paper-listbox
|
||||
attr-for-selected="data-path"
|
||||
.selected=${this.castManager.status.lovelacePath || ""}
|
||||
>
|
||||
${(
|
||||
this.lovelaceViews ?? [
|
||||
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
|
||||
]
|
||||
).map(
|
||||
(view, idx) => html`
|
||||
<paper-icon-item
|
||||
@click=${this._handlePickView}
|
||||
data-path=${view.path || idx}
|
||||
>
|
||||
${view.icon
|
||||
? html`
|
||||
<ha-icon
|
||||
.icon=${view.icon}
|
||||
slot="item-icon"
|
||||
></ha-icon>
|
||||
`
|
||||
: ""}
|
||||
${view.title || view.path}
|
||||
</paper-icon-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
`}
|
||||
<div class="card-actions">
|
||||
${this.castManager.status
|
||||
? html`
|
||||
@@ -136,11 +138,15 @@ class HcCast extends LitElement {
|
||||
llColl.refresh().then(
|
||||
() => {
|
||||
llColl.subscribe((config) => {
|
||||
this.lovelaceConfig = config;
|
||||
if (isStrategyDashboard(config)) {
|
||||
this.lovelaceViews = null;
|
||||
} else {
|
||||
this.lovelaceViews = config.views;
|
||||
}
|
||||
});
|
||||
},
|
||||
async () => {
|
||||
this.lovelaceConfig = null;
|
||||
this.lovelaceViews = null;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -159,9 +165,7 @@ class HcCast extends LitElement {
|
||||
toggleAttribute(
|
||||
this,
|
||||
"hide-icons",
|
||||
this.lovelaceConfig
|
||||
? !this.lovelaceConfig.views.some((view) => view.icon)
|
||||
: true
|
||||
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiCastConnected, mdiCast } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
Auth,
|
||||
Connection,
|
||||
@@ -24,6 +23,7 @@ import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
||||
import "./hc-layout";
|
||||
import "../../../../src/components/ha-textfield";
|
||||
|
||||
const seeFAQ = (qid) => html`
|
||||
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
|
||||
@@ -33,13 +33,13 @@ const translateErr = (err) =>
|
||||
err === ERR_CANNOT_CONNECT
|
||||
? "Unable to connect"
|
||||
: err === ERR_HASS_HOST_REQUIRED
|
||||
? "Please enter a Home Assistant URL."
|
||||
: err === ERR_INVALID_HTTPS_TO_HTTP
|
||||
? html`
|
||||
Cannot connect to Home Assistant instances over "http://".
|
||||
${seeFAQ("https")}
|
||||
`
|
||||
: `Unknown error (${err}).`;
|
||||
? "Please enter a Home Assistant URL."
|
||||
: err === ERR_INVALID_HTTPS_TO_HTTP
|
||||
? html`
|
||||
Cannot connect to Home Assistant instances over "http://".
|
||||
${seeFAQ("https")}
|
||||
`
|
||||
: `Unknown error (${err}).`;
|
||||
|
||||
const INTRO = html`
|
||||
<p>
|
||||
@@ -116,13 +116,11 @@ export class HcConnect extends LitElement {
|
||||
To get started, enter your Home Assistant URL and click authorize.
|
||||
If you want a preview instead, click the show demo button.
|
||||
</p>
|
||||
<p>
|
||||
<paper-input
|
||||
label="Home Assistant URL"
|
||||
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
|
||||
@keydown=${this._handleInputKeyDown}
|
||||
></paper-input>
|
||||
</p>
|
||||
<ha-textfield
|
||||
label="Home Assistant URL"
|
||||
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
|
||||
@keydown=${this._handleInputKeyDown}
|
||||
></ha-textfield>
|
||||
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
@@ -196,7 +194,7 @@ export class HcConnect extends LitElement {
|
||||
}
|
||||
|
||||
private async _handleConnect() {
|
||||
const inputEl = this.shadowRoot!.querySelector("paper-input")!;
|
||||
const inputEl = this.shadowRoot!.querySelector("ha-textfield")!;
|
||||
const value = inputEl.value || "";
|
||||
this.error = undefined;
|
||||
|
||||
@@ -315,6 +313,10 @@ export class HcConnect extends LitElement {
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
LovelaceCardConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
@@ -14,7 +14,8 @@ import "./hc-launch-screen";
|
||||
class HcLovelace extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
|
||||
@property({ attribute: false })
|
||||
public lovelaceConfig!: LovelaceConfig;
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
|
||||
|
@@ -21,17 +21,27 @@ import {
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import {
|
||||
fetchResources,
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import {
|
||||
isStrategyDashboard,
|
||||
LegacyLovelaceConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
LovelaceDashboardStrategyConfig,
|
||||
} from "../../../../src/data/lovelace/config/types";
|
||||
import { fetchResources } from "../../../../src/data/lovelace/resource";
|
||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import { castContext } from "../cast_context";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
|
||||
strategy: {
|
||||
type: "original-states",
|
||||
},
|
||||
};
|
||||
|
||||
let resourcesLoaded = false;
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@@ -91,14 +101,16 @@ export class HcMain extends HassElement {
|
||||
.lovelaceConfig=${this._lovelaceConfig}
|
||||
.viewPath=${this._lovelacePath}
|
||||
.urlPath=${this._urlPath}
|
||||
@config-refresh=${this._generateLovelaceConfig}
|
||||
@config-refresh=${this._generateDefaultLovelaceConfig}
|
||||
></hc-lovelace>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
import("../second-load");
|
||||
import("./hc-lovelace");
|
||||
import("../../../../src/resources/ha-style");
|
||||
|
||||
window.addEventListener("location-changed", () => {
|
||||
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
||||
if (location.pathname.startsWith(panelPath)) {
|
||||
@@ -258,7 +270,6 @@ export class HcMain extends HassElement {
|
||||
{
|
||||
strategy: {
|
||||
type: "energy",
|
||||
options: { show_date_selection: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -281,9 +292,20 @@ export class HcMain extends HassElement {
|
||||
// configuration.
|
||||
try {
|
||||
await llColl.refresh();
|
||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||
);
|
||||
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
||||
if (isStrategyDashboard(rawConfig)) {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
const config = await generateLovelaceDashboardStrategy(
|
||||
rawConfig.strategy,
|
||||
this.hass!
|
||||
);
|
||||
this._handleNewLovelaceConfig(config);
|
||||
} else {
|
||||
this._handleNewLovelaceConfig(rawConfig);
|
||||
}
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (
|
||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
||||
@@ -297,7 +319,7 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
// Generate a Lovelace config.
|
||||
this._unsubLovelace = () => undefined;
|
||||
await this._generateLovelaceConfig();
|
||||
await this._generateDefaultLovelaceConfig();
|
||||
}
|
||||
}
|
||||
if (!resourcesLoaded) {
|
||||
@@ -306,24 +328,21 @@ export class HcMain extends HassElement {
|
||||
? await fetchResources(this.hass!.connection)
|
||||
: (this._lovelaceConfig as LegacyLovelaceConfig).resources;
|
||||
if (resources) {
|
||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
||||
loadLovelaceResources(resources, this.hass!);
|
||||
}
|
||||
}
|
||||
|
||||
this._sendStatus();
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig() {
|
||||
private async _generateDefaultLovelaceConfig() {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: false,
|
||||
},
|
||||
"original-states"
|
||||
DEFAULT_CONFIG.strategy,
|
||||
this.hass!
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -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",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/favicon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/favicon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/icons/favicon-1024x1024.png",
|
||||
"sizes": "1024x1024",
|
||||
"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",
|
||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
convertEntities({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
supported_features: 15,
|
||||
friendly_name: "Shopping List",
|
||||
icon: "mdi:cart",
|
||||
},
|
||||
},
|
||||
"zone.home": {
|
||||
entity_id: "zone.home",
|
||||
state: "zoning",
|
||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
supported_features: 15,
|
||||
friendly_name: "Shopping List",
|
||||
icon: "mdi:cart",
|
||||
},
|
||||
},
|
||||
"zone.powertec": {
|
||||
entity_id: "zone.powertec",
|
||||
state: "zoning",
|
||||
|
@@ -4,16 +4,11 @@ export const demoThemeJimpower = () => ({
|
||||
"primary-color": "#5294E2",
|
||||
"label-badge-red": "var(--accent-color)",
|
||||
"paper-tabs-selection-bar-color": "green",
|
||||
"paper-slider-knob-color": "var(--accent-color)",
|
||||
"light-primary-color": "var(--accent-color)",
|
||||
"primary-background-color": "#383C45",
|
||||
"primary-text-color": "#FFFFFF",
|
||||
"paper-item-selected_-_background-color": "#434954",
|
||||
"paper-slider-active-color": "var(--accent-color)",
|
||||
"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",
|
||||
"paper-item-icon_-_color": "green",
|
||||
"paper-grey-200": "#414A59",
|
||||
@@ -32,14 +27,10 @@ export const demoThemeJimpower = () => ({
|
||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||
"label-badge-border-color": "green",
|
||||
"paper-listbox-color": "var(--primary-color)",
|
||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
||||
"card-background-color": "#434954",
|
||||
"label-badge-text-color": "var(--primary-text-color)",
|
||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
||||
"switch-unchecked-track-color": "var(--disabled-text-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",
|
||||
"accent-color": "#E45E65",
|
||||
"table-row-alternative-background-color": "#3E424B",
|
||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
supported_features: 15,
|
||||
friendly_name: "Shopping List",
|
||||
icon: "mdi:cart",
|
||||
},
|
||||
},
|
||||
"zone.anna": {
|
||||
entity_id: "zone.anna",
|
||||
state: "zoning",
|
||||
|
@@ -5,17 +5,12 @@ export const demoThemeKernehed = () => ({
|
||||
"primary-color": "#2980b9",
|
||||
"label-badge-red": "var(--accent-color)",
|
||||
"paper-tabs-selection-bar-color": "green",
|
||||
"paper-slider-knob-color": "var(--accent-color)",
|
||||
"primary-text-color": "#FFFFFF",
|
||||
"light-primary-color": "var(--accent-color)",
|
||||
"primary-background-color": "#222222",
|
||||
"sidebar-icon-color": "#777777",
|
||||
"paper-item-selected_-_background-color": "#292929",
|
||||
"paper-slider-active-color": "var(--accent-color)",
|
||||
"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",
|
||||
"paper-item-icon_-_color": "green",
|
||||
"paper-grey-200": "#222222",
|
||||
@@ -33,14 +28,10 @@ export const demoThemeKernehed = () => ({
|
||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||
"label-badge-border-color": "green",
|
||||
"paper-listbox-color": "#777777",
|
||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
||||
"card-background-color": "#292929",
|
||||
"label-badge-text-color": "var(--primary-text-color)",
|
||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
||||
"switch-unchecked-track-color": "var(--disabled-text-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",
|
||||
"accent-color": "#2980b9",
|
||||
"table-row-alternative-background-color": "#292929",
|
||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
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": {
|
||||
entity_id: "sensor.pollen_grabo",
|
||||
state: "",
|
||||
|
@@ -220,7 +220,8 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
state_filter: ["on"],
|
||||
},
|
||||
{
|
||||
type: "shopping-list",
|
||||
type: "todo-list",
|
||||
entity: "todo.shopping_list",
|
||||
},
|
||||
{
|
||||
entities: [
|
||||
|
@@ -1,6 +1,5 @@
|
||||
export const demoThemeTeachingbirds = () => ({
|
||||
"paper-card-header-color": "var(--paper-item-icon-color)",
|
||||
"paper-slider-pin-color": "var(--primary-color)",
|
||||
"paper-listbox-background-color": "#202020",
|
||||
"paper-grey-50": "var(--primary-text-color)",
|
||||
"paper-item-icon-color": "#d3d3d3",
|
||||
@@ -8,8 +7,6 @@ export const demoThemeTeachingbirds = () => ({
|
||||
"primary-color": "#389638",
|
||||
"light-primary-color": "#6f956f",
|
||||
"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-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
||||
"switch-unchecked-track-color": "var(--primary-text-color)",
|
||||
@@ -17,9 +14,7 @@ export const demoThemeTeachingbirds = () => ({
|
||||
"label-badge-text-color": "var(--text-primary-color)",
|
||||
"primary-background-color": "#303030",
|
||||
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
||||
"paper-slider-active-color": "#d8bf50",
|
||||
"secondary-background-color": "#2b2b2b",
|
||||
"paper-slider-knob-start-color": "var(--primary-color)",
|
||||
"paper-item-icon-active-color": "#d8bf50",
|
||||
"switch-checked-color": "var(--primary-color)",
|
||||
"secondary-text-color": "#389638",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import { LovelaceConfig } from "../../../src/data/lovelace";
|
||||
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||
import { Entity } from "../../../src/fake_data/entity";
|
||||
|
||||
export interface DemoConfig {
|
||||
|
@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
||||
import {
|
||||
@@ -48,8 +48,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
<a target="_blank" href=${conf.authorUrl}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.page-demo.cards.demo.demo_by",
|
||||
"name",
|
||||
conf.authorName
|
||||
{ name: conf.authorName }
|
||||
)}
|
||||
</a>
|
||||
</small>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
import "./ha-demo";
|
||||
|
||||
import("../../src/resources/ha-style");
|
||||
|
@@ -22,7 +22,7 @@ import { mockLovelace } from "./stubs/lovelace";
|
||||
import { mockMediaPlayer } from "./stubs/media_player";
|
||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||
import { mockRecorder } from "./stubs/recorder";
|
||||
import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockTodo } from "./stubs/todo";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
@@ -49,7 +49,7 @@ export class HaDemo extends HomeAssistantAppEl {
|
||||
mockTranslations(hass);
|
||||
mockHistory(hass);
|
||||
mockRecorder(hass);
|
||||
mockShoppingList(hass);
|
||||
mockTodo(hass);
|
||||
mockSystemLog(hass);
|
||||
mockTemplate(hass);
|
||||
mockEvents(hass);
|
||||
|
@@ -43,8 +43,8 @@ const generateMeanStatistics = (
|
||||
period === "day"
|
||||
? addDays(currentDate, 1)
|
||||
: period === "month"
|
||||
? addMonths(currentDate, 1)
|
||||
: addHours(currentDate, 1);
|
||||
? addMonths(currentDate, 1)
|
||||
: addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
@@ -80,8 +80,8 @@ const generateSumStatistics = (
|
||||
period === "day"
|
||||
? addDays(currentDate, 1)
|
||||
: period === "month"
|
||||
? addMonths(currentDate, 1)
|
||||
: addHours(currentDate, 1);
|
||||
? addMonths(currentDate, 1)
|
||||
: addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
@@ -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
@@ -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[],
|
||||
}));
|
||||
};
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 10 KiB |
@@ -23,7 +23,7 @@ class DemoMoreInfo extends LitElement {
|
||||
<state-card-content
|
||||
.stateObj=${state}
|
||||
.hass=${this.hass}
|
||||
in-dialog
|
||||
inDialog
|
||||
></state-card-content>
|
||||
|
||||
<more-info-content
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "./ha-gallery";
|
||||
|
||||
import("../../src/resources/ha-style");
|
||||
|
||||
document.body.appendChild(document.createElement("ha-gallery"));
|
||||
|
@@ -1,19 +1,17 @@
|
||||
import { mdiHomeAssistant } from "@mdi/js";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, TemplateResult, nothing } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-assist-chip";
|
||||
import "../../../../src/components/chips/ha-input-chip";
|
||||
import "../../../../src/components/chips/ha-filter-chip";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||
|
||||
const chips: {
|
||||
icon?: string;
|
||||
content?: string;
|
||||
}[] = [
|
||||
{},
|
||||
{
|
||||
icon: mdiHomeAssistant,
|
||||
},
|
||||
{
|
||||
content: "Content",
|
||||
},
|
||||
@@ -29,31 +27,73 @@ export class DemoHaChips extends LitElement {
|
||||
return html`
|
||||
<ha-card header="ha-chip demo">
|
||||
<div class="card-content">
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</ha-card>
|
||||
<ha-card header="ha-chip-set demo">
|
||||
<div class="card-content">
|
||||
<p>Action chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
<ha-assist-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-assist-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-assist-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-assist-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
<p>Filter chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-filter-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-filter-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
<p>Input chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-input-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-chip>
|
||||
</ha-input-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-input-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-input-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
@@ -68,12 +108,10 @@ export class DemoHaChips extends LitElement {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
ha-chip {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -49,11 +49,11 @@ export class DemoHaCircularSlider extends LitElement {
|
||||
<div class="field">
|
||||
<p>Current</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
min="10"
|
||||
max="30"
|
||||
.value=${this.current}
|
||||
@change=${this._currentChanged}
|
||||
pin
|
||||
></ha-slider>
|
||||
<p>${this.current} °C</p>
|
||||
</div>
|
||||
|
@@ -11,6 +11,7 @@ const buttons: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
unit?: string;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
@@ -29,6 +30,11 @@ const buttons: {
|
||||
label: "Custom",
|
||||
class: "custom",
|
||||
},
|
||||
{
|
||||
id: "unit",
|
||||
label: "With unit",
|
||||
unit: "m",
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-components-ha-control-number-buttons")
|
||||
@@ -50,6 +56,7 @@ export class DemoHarControlNumberButtons extends LitElement {
|
||||
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||
<ha-control-number-buttons
|
||||
.value=${this.value}
|
||||
.unit=${config.unit}
|
||||
.min=${config.min}
|
||||
.max=${config.max}
|
||||
.step=${config.step}
|
||||
|
@@ -9,6 +9,7 @@ const sliders: {
|
||||
id: string;
|
||||
label: string;
|
||||
mode?: "start" | "end" | "cursor";
|
||||
unit?: string;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
@@ -31,18 +32,21 @@ const sliders: {
|
||||
label: "Slider (start mode) and custom style",
|
||||
mode: "start",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
{
|
||||
id: "slider-end-custom",
|
||||
label: "Slider (end mode) and custom style",
|
||||
mode: "end",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
{
|
||||
id: "slider-cursor-custom",
|
||||
label: "Slider (cursor mode) and custom style",
|
||||
mode: "cursor",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -93,6 +97,7 @@ export class DemoHaBarSlider extends LitElement {
|
||||
@value-changed=${this.handleValueChanged}
|
||||
@slider-moved=${this.handleSliderMoved}
|
||||
aria-labelledby=${id}
|
||||
.unit=${config.unit}
|
||||
>
|
||||
</ha-control-slider>
|
||||
</div>
|
||||
@@ -114,6 +119,7 @@ export class DemoHaBarSlider extends LitElement {
|
||||
@value-changed=${this.handleValueChanged}
|
||||
@slider-moved=${this.handleSliderMoved}
|
||||
aria-label=${label}
|
||||
.unit=${config.unit}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
|
@@ -5,9 +5,22 @@ subtitle: Dialogs provide important prompts in a user flow.
|
||||
|
||||
# Material Design 3
|
||||
|
||||
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||
|
||||
# Highlighted guidelines
|
||||
# Guidelines
|
||||
|
||||
## Design
|
||||
|
||||
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
|
||||
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
|
||||
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
|
||||
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
|
||||
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
|
||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||
- Destructive actions should be a red warning button.
|
||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||
|
||||
## Content
|
||||
|
||||
@@ -17,14 +30,6 @@ Our dialogs are based on the latest version of Material Design. Specs and guidel
|
||||
- If users become unsure, they read the description. Make sure this explains what will happen.
|
||||
- Strive for minimalism.
|
||||
|
||||
## Buttons and X-icon
|
||||
|
||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||
- Destructive actions should be a red warning button.
|
||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||
|
||||
## Example
|
||||
|
||||
### Confirmation dialog
|
||||
|
@@ -57,6 +57,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: "backyard",
|
||||
@@ -74,6 +75,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: null,
|
||||
@@ -91,6 +93,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
@@ -57,8 +57,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
></ha-hs-color-picker>
|
||||
<p>Hue : ${this.value[0]}</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="1"
|
||||
pin
|
||||
min="0"
|
||||
max="360"
|
||||
.value=${this.value[0]}
|
||||
@@ -67,8 +67,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
</ha-slider>
|
||||
<p>Saturation : ${this.value[1]}</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="0.01"
|
||||
pin
|
||||
min="0"
|
||||
max="1"
|
||||
.value=${this.value[1]}
|
||||
@@ -77,8 +77,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
</ha-slider>
|
||||
<p>Color Brighness : ${this.brightness}</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="1"
|
||||
pin
|
||||
min="0"
|
||||
max="255"
|
||||
.value=${this.brightness}
|
||||
|
@@ -53,6 +53,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: "backyard",
|
||||
@@ -70,6 +71,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: null,
|
||||
@@ -87,6 +89,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
@@ -18,7 +18,7 @@ The Home Assistant interface is based on Material Design. It's a design system c
|
||||
|
||||
We want to make it as easy for designers to contribute as it is for developers. There’s a lot a designer can contribute to:
|
||||
|
||||
- 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://www.home-assistant.io/join-chat" 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>.
|
||||
- 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!
|
||||
|
||||
|
@@ -1,3 +0,0 @@
|
||||
---
|
||||
title: Shopping List Card
|
||||
---
|
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 { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
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 = [
|
||||
{
|
||||
heading: "List example",
|
||||
config: `
|
||||
- type: shopping-list
|
||||
- type: todo-list
|
||||
entity: todo.shopping_list
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "List with title example",
|
||||
config: `
|
||||
- type: shopping-list
|
||||
- type: todo-list
|
||||
title: Shopping List
|
||||
entity: todo.read_only
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-lovelace-shopping-list-card")
|
||||
class DemoShoppingListEntity extends LitElement {
|
||||
@customElement("demo-lovelace-todo-list-card")
|
||||
class DemoTodoListEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -32,18 +46,14 @@ class DemoShoppingListEntity extends LitElement {
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
|
||||
hass.mockAPI("shopping_list", () => [
|
||||
{ name: "list", id: 1, complete: false },
|
||||
{ name: "all", id: 2, complete: false },
|
||||
{ name: "the", id: 3, complete: false },
|
||||
{ name: "things", id: 4, complete: true },
|
||||
]);
|
||||
mockTodo(hass);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
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 type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||
import "../../../../src/components/entity/state-badge";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
|
@@ -213,6 +213,7 @@ const createDeviceRegistryEntries = (
|
||||
name: "Tag Reader",
|
||||
sw_version: null,
|
||||
hw_version: "1.0.0",
|
||||
serial_number: "00_12_4B_00_22_98_88_7F",
|
||||
id: "mock-device-id",
|
||||
identifiers: [],
|
||||
via_device_id: null,
|
||||
|
@@ -2,7 +2,7 @@ import "@material/mwc-button";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
|
||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
|
||||
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||
|
||||
@customElement("demo-misc-util-long-press")
|
||||
|
3
gallery/src/pages/more-info/input-text.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Input Text
|
||||
---
|
46
gallery/src/pages/more-info/input-text.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("input_text", "text", "Inspiration", {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-input-text")
|
||||
class DemoMoreInfoInputText 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-input-text": DemoMoreInfoInputText;
|
||||
}
|
||||
}
|
3
gallery/src/pages/more-info/lock.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Lock
|
||||
---
|
49
gallery/src/pages/more-info/lock.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("lock", "lock", "locked", {
|
||||
friendly_name: "Lock",
|
||||
device_class: "lock",
|
||||
}),
|
||||
getEntity("lock", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable lock",
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-lock")
|
||||
class DemoMoreInfoLock 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-lock": DemoMoreInfoLock;
|
||||
}
|
||||
}
|
3
gallery/src/pages/more-info/media-player.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Media Player
|
||||
---
|
41
gallery/src/pages/more-info/media-player.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { createMediaPlayerEntities } from "../../data/media_players";
|
||||
|
||||
const ENTITIES = createMediaPlayerEntities();
|
||||
|
||||
@customElement("demo-more-info-media-player")
|
||||
class DemoMoreInfoMediaPlayer 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-media-player": DemoMoreInfoMediaPlayer;
|
||||
}
|
||||
}
|
3
gallery/src/pages/more-info/number.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Number
|
||||
---
|
78
gallery/src/pages/more-info/number.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("number", "box1", 0, {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "slider1", 0, {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "auto1", 0, {
|
||||
friendly_name: "Auto1",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "auto2", 0, {
|
||||
friendly_name: "Auto2",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-number")
|
||||
class DemoMoreInfoNumber 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-number": DemoMoreInfoNumber;
|
||||
}
|
||||
}
|
3
gallery/src/pages/more-info/scene.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Scene
|
||||
---
|
49
gallery/src/pages/more-info/scene.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("scene", "romantic_lights", "scening", {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("scene", "unavailable", "unavailable", {
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-scene")
|
||||
class DemoMoreInfoScene 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-scene": DemoMoreInfoScene;
|
||||
}
|
||||
}
|
3
gallery/src/pages/more-info/timer.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Timer
|
||||
---
|
46
gallery/src/pages/more-info/timer.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("timer", "timer", "idle", {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-timer")
|
||||
class DemoMoreInfoTimer 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-timer": DemoMoreInfoTimer;
|
||||
}
|
||||
}
|
3
gallery/src/pages/more-info/vacuum.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Vacuum
|
||||
---
|
50
gallery/src/pages/more-info/vacuum.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-vacuum")
|
||||
class DemoMoreInfoVacuum 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-vacuum": DemoMoreInfoVacuum;
|
||||
}
|
||||
}
|
@@ -49,11 +49,9 @@ export class HassioAddonRepositoryEl extends LitElement {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p class="description">
|
||||
${this.supervisor.localize(
|
||||
"store.no_results_found",
|
||||
"repository",
|
||||
repo.name
|
||||
)}
|
||||
${this.supervisor.localize("store.no_results_found", {
|
||||
repository: repo.name,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
@@ -86,15 +84,15 @@ export class HassioAddonRepositoryEl extends LitElement {
|
||||
)
|
||||
: this.supervisor.localize("addon.state.installed")
|
||||
: addon.available
|
||||
? this.supervisor.localize("addon.state.not_installed")
|
||||
: this.supervisor.localize("addon.state.not_available")}
|
||||
? this.supervisor.localize("addon.state.not_installed")
|
||||
: this.supervisor.localize("addon.state.not_available")}
|
||||
.iconClass=${addon.installed
|
||||
? addon.update_available
|
||||
? "update"
|
||||
: "installed"
|
||||
: !addon.available
|
||||
? "not_available"
|
||||
: ""}
|
||||
? "not_available"
|
||||
: ""}
|
||||
.iconImage=${atLeastVersion(
|
||||
this.hass.config.version,
|
||||
0,
|
||||
@@ -108,8 +106,8 @@ export class HassioAddonRepositoryEl extends LitElement {
|
||||
? "update"
|
||||
: "installed"
|
||||
: !addon.available
|
||||
? "unavailable"
|
||||
: ""}
|
||||
? "unavailable"
|
||||
: ""}
|
||||
></hassio-card-content>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
@@ -104,50 +104,50 @@ class HassioAddonConfig extends LitElement {
|
||||
selector: { select: { options: entry.options } },
|
||||
}
|
||||
: entry.type === "string"
|
||||
? entry.multiple
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: {
|
||||
select: { options: [], multiple: true, custom_value: true },
|
||||
},
|
||||
}
|
||||
: {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: {
|
||||
text: {
|
||||
type:
|
||||
entry.format || MASKED_FIELDS.includes(entry.name)
|
||||
? "password"
|
||||
: "text",
|
||||
? entry.multiple
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: {
|
||||
select: { options: [], multiple: true, custom_value: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
: entry.type === "boolean"
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: { boolean: {} },
|
||||
}
|
||||
: entry.type === "schema"
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: { object: {} },
|
||||
}
|
||||
: entry.type === "float" || entry.type === "integer"
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: {
|
||||
number: {
|
||||
mode: "box",
|
||||
step: entry.type === "float" ? "any" : undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
: entry
|
||||
}
|
||||
: {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: {
|
||||
text: {
|
||||
type:
|
||||
entry.format || MASKED_FIELDS.includes(entry.name)
|
||||
? "password"
|
||||
: "text",
|
||||
},
|
||||
},
|
||||
}
|
||||
: entry.type === "boolean"
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: { boolean: {} },
|
||||
}
|
||||
: entry.type === "schema"
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: { object: {} },
|
||||
}
|
||||
: entry.type === "float" || entry.type === "integer"
|
||||
? {
|
||||
name: entry.name,
|
||||
required: entry.required,
|
||||
selector: {
|
||||
number: {
|
||||
mode: "box",
|
||||
step: entry.type === "float" ? "any" : undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
: entry
|
||||
)
|
||||
);
|
||||
|
||||
@@ -340,11 +340,9 @@ class HassioAddonConfig extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_reset",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_reset", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
button.progress = false;
|
||||
}
|
||||
@@ -381,11 +379,9 @@ class HassioAddonConfig extends LitElement {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
eventdata.success = false;
|
||||
}
|
||||
button.progress = false;
|
||||
|
@@ -180,11 +180,9 @@ class HassioAddonNetwork extends LitElement {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_reset",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_reset", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
button.actionError();
|
||||
}
|
||||
}
|
||||
@@ -220,11 +218,9 @@ class HassioAddonNetwork extends LitElement {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
button.actionError();
|
||||
}
|
||||
}
|
||||
|
@@ -85,8 +85,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.documentation.get_documentation",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
{ error: extractApiErrorMessage(err) }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import {
|
||||
mdiDocker,
|
||||
mdiExclamationThick,
|
||||
mdiFlask,
|
||||
mdiHomeAssistant,
|
||||
mdiKey,
|
||||
mdiLinkLock,
|
||||
mdiNetwork,
|
||||
@@ -22,7 +21,7 @@ import {
|
||||
mdiPound,
|
||||
mdiShield,
|
||||
} 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 { classMap } from "lit/directives/class-map";
|
||||
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/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-assist-chip";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-switch";
|
||||
import {
|
||||
AddonCapability,
|
||||
fetchHassioAddonChangelog,
|
||||
fetchHassioAddonInfo,
|
||||
HassioAddonDetails,
|
||||
HassioAddonSetOptionParams,
|
||||
HassioAddonSetSecurityParams,
|
||||
fetchHassioAddonChangelog,
|
||||
fetchHassioAddonInfo,
|
||||
installHassioAddon,
|
||||
rebuildLocalAddon,
|
||||
restartHassioAddon,
|
||||
@@ -56,9 +55,9 @@ import {
|
||||
validateHassioAddonOption,
|
||||
} from "../../../../src/data/hassio/addon";
|
||||
import {
|
||||
HassioStats,
|
||||
extractApiErrorMessage,
|
||||
fetchHassioStats,
|
||||
HassioStats,
|
||||
} from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
StoreAddon,
|
||||
@@ -69,6 +68,7 @@ import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../../src/types";
|
||||
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 "../../update-available/update-available-card";
|
||||
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
|
||||
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
|
||||
|
||||
const STAGE_ICON = {
|
||||
stable: mdiCheckCircle,
|
||||
@@ -234,28 +235,32 @@ class HassioAddonInfo extends LitElement {
|
||||
|
||||
<ha-chip-set class="capabilities">
|
||||
${this.addon.stage !== "stable"
|
||||
? html` <ha-chip
|
||||
hasIcon
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
)
|
||||
)}
|
||||
>
|
||||
</ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
)}
|
||||
</ha-chip>`
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
>
|
||||
</ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
class=${classMap({
|
||||
green: Number(this.addon.rating) >= 6,
|
||||
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
|
||||
@@ -263,151 +268,197 @@ class HassioAddonInfo extends LitElement {
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="rating"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
|
||||
</ha-svg-icon>
|
||||
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
${this.addon.host_network
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_network"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.full_access
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="full_access"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.homeassistant_api
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="homeassistant_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this._computeHassioApi
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="hassio_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.docker_api
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
|
||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="docker_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.host_pid
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
|
||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_pid"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.apparmor !== "default"
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
class=${this._computeApparmorClassName}
|
||||
id="apparmor"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auth_api
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
|
||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="auth_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="ingress"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiCursorDefaultClickOutline}
|
||||
></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.signed
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="signed">
|
||||
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.signed"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="signed"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.signed"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
</ha-chip-set>
|
||||
|
||||
<div class="description light-color">
|
||||
${this.addon.description}.<br />
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.visit_addon_page",
|
||||
"name",
|
||||
html`<a href=${this.addon.url!} target="_blank" rel="noreferrer"
|
||||
${this.supervisor.localize("addon.dashboard.visit_addon_page", {
|
||||
name: html`<a
|
||||
href=${this.addon.url!}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.addon.name}</a
|
||||
>`
|
||||
)}
|
||||
>`,
|
||||
})}
|
||||
</div>
|
||||
<div class="addon-container">
|
||||
<div>
|
||||
@@ -574,10 +625,10 @@ class HassioAddonInfo extends LitElement {
|
||||
<ha-alert alert-type="warning">
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.not_available_version",
|
||||
"core_version_installed",
|
||||
this.supervisor.core.version,
|
||||
"core_version_needed",
|
||||
addonStoreInfo!.homeassistant
|
||||
{
|
||||
core_version_installed: this.supervisor.core.version,
|
||||
core_version_needed: addonStoreInfo!.homeassistant,
|
||||
}
|
||||
)}
|
||||
</ha-alert>
|
||||
`
|
||||
@@ -750,12 +801,11 @@ class HassioAddonInfo extends LitElement {
|
||||
id === "stage"
|
||||
? this.supervisor.localize(
|
||||
`addon.dashboard.capability.${id}.description`,
|
||||
"icon_stable",
|
||||
`<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon>`,
|
||||
"icon_experimental",
|
||||
`<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon>`,
|
||||
"icon_deprecated",
|
||||
`<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon>`
|
||||
{
|
||||
icon_stable: `<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon>`,
|
||||
icon_experimental: `<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon>`,
|
||||
icon_deprecated: `<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon>`,
|
||||
}
|
||||
)
|
||||
: this.supervisor.localize(
|
||||
`addon.dashboard.capability.${id}.description`
|
||||
@@ -817,11 +867,9 @@ class HassioAddonInfo extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,11 +887,9 @@ class HassioAddonInfo extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,11 +907,9 @@ class HassioAddonInfo extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,11 +927,9 @@ class HassioAddonInfo extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,11 +947,9 @@ class HassioAddonInfo extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.failed_to_save", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1185,23 +1225,35 @@ class HassioAddonInfo extends LitElement {
|
||||
.description a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-chip {
|
||||
text-transform: capitalize;
|
||||
--ha-chip-text-color: var(--text-primary-color);
|
||||
--ha-chip-background-color: var(--primary-color);
|
||||
ha-assist-chip {
|
||||
--md-sys-color-primary: var(--text-primary-color);
|
||||
--md-sys-color-on-surface: var(--text-primary-color);
|
||||
--ha-assist-chip-filled-container-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.red {
|
||||
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-red,
|
||||
#df4c1e
|
||||
);
|
||||
}
|
||||
.blue {
|
||||
--ha-chip-background-color: var(--label-badge-blue, #039be5);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-blue,
|
||||
#039be5
|
||||
);
|
||||
}
|
||||
.green {
|
||||
--ha-chip-background-color: var(--label-badge-green, #0da035);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-green,
|
||||
#0da035
|
||||
);
|
||||
}
|
||||
.yellow {
|
||||
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-yellow,
|
||||
#f4b400
|
||||
);
|
||||
}
|
||||
.capabilities {
|
||||
margin-bottom: 16px;
|
||||
@@ -1260,9 +1312,6 @@ class HassioAddonInfo extends LitElement {
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
ha-chip {
|
||||
line-height: 36px;
|
||||
}
|
||||
.addon-options {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
@@ -72,11 +72,9 @@ class HassioAddonLogs extends LitElement {
|
||||
try {
|
||||
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
|
||||
} catch (err: any) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.logs.get_logs",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
this._error = this.supervisor.localize("addon.logs.get_logs", {
|
||||
error: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -360,11 +360,9 @@ export class HassioBackups extends LitElement {
|
||||
if (this.supervisor!.info.state !== "running") {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor!.localize("backup.could_not_create"),
|
||||
text: this.supervisor!.localize(
|
||||
"backup.create_blocked_not_running",
|
||||
"state",
|
||||
this.supervisor!.info.state
|
||||
),
|
||||
text: this.supervisor!.localize("backup.create_blocked_not_running", {
|
||||
state: this.supervisor!.info.state,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import Fuse from "fuse.js";
|
||||
import type { IFuseOptions } from "fuse.js";
|
||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||
|
||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
||||
const options: Fuse.IFuseOptions<StoreAddon> = {
|
||||
const options: IFuseOptions<StoreAddon> = {
|
||||
keys: ["name", "description", "slug"],
|
||||
isCaseSensitive: false,
|
||||
minMatchCharLength: 2,
|
||||
minMatchCharLength: Math.min(filter.length, 2),
|
||||
threshold: 0.2,
|
||||
};
|
||||
const fuse = new Fuse(addons, options);
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { mdiFolder, mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
|
||||
import { mdiFolder, mdiPuzzle } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
HassioPartialBackupCreateParams,
|
||||
} from "../../../src/data/hassio/backup";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { mdiHomeAssistant } from "../../../src/resources/home-assistant-logo-svg";
|
||||
import {
|
||||
HomeAssistant,
|
||||
TranslationDict,
|
||||
|
@@ -17,8 +17,11 @@ class SupervisorFormfieldLabel extends LitElement {
|
||||
${this.imageUrl
|
||||
? html`<img loading="lazy" alt="" src=${this.imageUrl} class="icon" />`
|
||||
: this.iconPath
|
||||
? html`<ha-svg-icon .path=${this.iconPath} class="icon"></ha-svg-icon>`
|
||||
: ""}
|
||||
? html`<ha-svg-icon
|
||||
.path=${this.iconPath}
|
||||
class="icon"
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
<span class="label">${this.label}</span>
|
||||
${this.version
|
||||
? html`<span class="version">(${this.version})</span>`
|
||||
|
@@ -68,17 +68,19 @@ class HassioAddons extends LitElement {
|
||||
.iconTitle=${addon.state !== "started"
|
||||
? this.supervisor.localize("dashboard.addon_stopped")
|
||||
: addon.update_available!
|
||||
? this.supervisor.localize(
|
||||
"dashboard.addon_new_version"
|
||||
)
|
||||
: this.supervisor.localize("dashboard.addon_running")}
|
||||
? this.supervisor.localize(
|
||||
"dashboard.addon_new_version"
|
||||
)
|
||||
: this.supervisor.localize(
|
||||
"dashboard.addon_running"
|
||||
)}
|
||||
.iconClass=${addon.update_available
|
||||
? addon.state === "started"
|
||||
? "update"
|
||||
: "update stopped"
|
||||
: addon.state === "started"
|
||||
? "running"
|
||||
: "stopped"}
|
||||
? "running"
|
||||
: "stopped"}
|
||||
.iconImage=${atLeastVersion(
|
||||
this.hass.config.version,
|
||||
0,
|
||||
|