mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-14 03:39:26 +00:00
Compare commits
922 Commits
combine-ap
...
20210818.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2270d8a795 | ||
![]() |
f4dcce6d6c | ||
![]() |
b802a410b9 | ||
![]() |
9e3d339ec5 | ||
![]() |
fb97a98b97 | ||
![]() |
72773f3bc8 | ||
![]() |
b0fd93e0c3 | ||
![]() |
7aa2ec78f2 | ||
![]() |
047e856a61 | ||
![]() |
dbe209e3f2 | ||
![]() |
e0d23ee6cf | ||
![]() |
7a35f46370 | ||
![]() |
4a4465efb6 | ||
![]() |
3a112531cc | ||
![]() |
456209dded | ||
![]() |
2556b0d157 | ||
![]() |
1e8903fd76 | ||
![]() |
ad9f18c231 | ||
![]() |
63e3de00cb | ||
![]() |
d06ffeeede | ||
![]() |
3479fb9d94 | ||
![]() |
304bd002ae | ||
![]() |
5dad18c85f | ||
![]() |
19e4c0657a | ||
![]() |
44548fdc33 | ||
![]() |
d8929074b5 | ||
![]() |
e11532ae92 | ||
![]() |
eff48acdf4 | ||
![]() |
a9850f9641 | ||
![]() |
aab0b8a3ce | ||
![]() |
b12e062d94 | ||
![]() |
b36e342f15 | ||
![]() |
f686816c86 | ||
![]() |
dc50e54afc | ||
![]() |
3897e3d452 | ||
![]() |
2557b03b11 | ||
![]() |
29d29a337f | ||
![]() |
34f8e5e28d | ||
![]() |
afd1a68c62 | ||
![]() |
dbcf1cb907 | ||
![]() |
9ca64f9789 | ||
![]() |
4c247ac49d | ||
![]() |
e8a406526b | ||
![]() |
7fcea16c6b | ||
![]() |
028b799d2c | ||
![]() |
3485296e23 | ||
![]() |
03078cdd45 | ||
![]() |
740310800d | ||
![]() |
6d7c558482 | ||
![]() |
fdb10515c3 | ||
![]() |
5a0f13c485 | ||
![]() |
80b330ad7b | ||
![]() |
d929e1c134 | ||
![]() |
5e40dcdc38 | ||
![]() |
1dd3e2a83b | ||
![]() |
a62742fad9 | ||
![]() |
1f9c45b11c | ||
![]() |
68bec5e158 | ||
![]() |
37f1bd7d63 | ||
![]() |
5ba24211e2 | ||
![]() |
d699647418 | ||
![]() |
b246502cb6 | ||
![]() |
9b33ead8aa | ||
![]() |
32e8c1dc6d | ||
![]() |
e09ef7862e | ||
![]() |
85420304d0 | ||
![]() |
1c097a669d | ||
![]() |
4e1497c5da | ||
![]() |
49d426675f | ||
![]() |
dc6ac668b4 | ||
![]() |
4ee24b0845 | ||
![]() |
ba20aef206 | ||
![]() |
41ef6133c1 | ||
![]() |
50bd5ee8f7 | ||
![]() |
285f3fe330 | ||
![]() |
4d01199986 | ||
![]() |
bcc0052fe0 | ||
![]() |
4b592d81bd | ||
![]() |
884e323288 | ||
![]() |
78b799dd05 | ||
![]() |
847fa2e700 | ||
![]() |
481a79e311 | ||
![]() |
f19f2ff321 | ||
![]() |
6dc4d7bb70 | ||
![]() |
83460a34f4 | ||
![]() |
2adbb47373 | ||
![]() |
2159a5419a | ||
![]() |
044d6a15d9 | ||
![]() |
b6055062c6 | ||
![]() |
6fd85e043b | ||
![]() |
e07ac52356 | ||
![]() |
0f16ba9325 | ||
![]() |
378e6d28bc | ||
![]() |
539d2b880c | ||
![]() |
2982adbfa7 | ||
![]() |
5147dff670 | ||
![]() |
2cdf78c504 | ||
![]() |
cfad45b7c2 | ||
![]() |
5234e9bce5 | ||
![]() |
0ed5454d02 | ||
![]() |
03080973be | ||
![]() |
a4f51b0cb3 | ||
![]() |
749079c1c3 | ||
![]() |
ae10ff42e1 | ||
![]() |
d4cbdab4a3 | ||
![]() |
1bd6392a4c | ||
![]() |
1531e99528 | ||
![]() |
6e7af18494 | ||
![]() |
e1bae65aeb | ||
![]() |
65bfdf94c9 | ||
![]() |
9a92825954 | ||
![]() |
469faf509b | ||
![]() |
f87d4a5ab6 | ||
![]() |
9c31b749d7 | ||
![]() |
521d5df064 | ||
![]() |
4d330fba8a | ||
![]() |
2e04a55d5c | ||
![]() |
0d9d0aa18b | ||
![]() |
f8dd1795bc | ||
![]() |
1d7007584c | ||
![]() |
8cd9f891fb | ||
![]() |
6ab0f1db57 | ||
![]() |
37d754d069 | ||
![]() |
e12b194d41 | ||
![]() |
07d7fa26fe | ||
![]() |
73b9b87ef3 | ||
![]() |
0c0091375c | ||
![]() |
21a29ed3a5 | ||
![]() |
a6312f4279 | ||
![]() |
f459abdf85 | ||
![]() |
586fa1d0f0 | ||
![]() |
bf4192a1f0 | ||
![]() |
ac31eedf65 | ||
![]() |
b05dc5141c | ||
![]() |
32c6fb14dd | ||
![]() |
982c940381 | ||
![]() |
a7a8aaa887 | ||
![]() |
bf83a9980e | ||
![]() |
11be603ed3 | ||
![]() |
a432cf8405 | ||
![]() |
9dd6b3b72d | ||
![]() |
faca62b55f | ||
![]() |
a339de89f5 | ||
![]() |
e40c90e9c0 | ||
![]() |
3f447bb8a7 | ||
![]() |
21dca3fbf8 | ||
![]() |
1078bb4287 | ||
![]() |
daeed06e70 | ||
![]() |
1206e2d75f | ||
![]() |
cc81239b9d | ||
![]() |
e797c01761 | ||
![]() |
12f7366968 | ||
![]() |
b7fd7abe85 | ||
![]() |
a5e1f3d165 | ||
![]() |
212d047ada | ||
![]() |
2e16127fde | ||
![]() |
e8b53a619d | ||
![]() |
df1ca1fd96 | ||
![]() |
8f85132d48 | ||
![]() |
349144599c | ||
![]() |
979093923b | ||
![]() |
137f8ad4cb | ||
![]() |
c1f462b8f8 | ||
![]() |
6701c4c371 | ||
![]() |
fc6e459c09 | ||
![]() |
9e28b3447e | ||
![]() |
5c737e1969 | ||
![]() |
569765e77e | ||
![]() |
bc0d63ed12 | ||
![]() |
02f9893522 | ||
![]() |
b4bbe63f0f | ||
![]() |
fabbcac99f | ||
![]() |
b1b5ab6949 | ||
![]() |
4b9487183b | ||
![]() |
de5a817953 | ||
![]() |
4970f640fa | ||
![]() |
18996535b7 | ||
![]() |
2a1e31b5e9 | ||
![]() |
8ca9a0f409 | ||
![]() |
fcc89a67ba | ||
![]() |
1f377d43c5 | ||
![]() |
30d6c68908 | ||
![]() |
dc781da93a | ||
![]() |
36c20e4348 | ||
![]() |
4466950bb8 | ||
![]() |
be29828454 | ||
![]() |
7bab245073 | ||
![]() |
f5dcf0b760 | ||
![]() |
8141f78a92 | ||
![]() |
be244b3d00 | ||
![]() |
805f5ff9b6 | ||
![]() |
76daeb7e55 | ||
![]() |
9594c8106e | ||
![]() |
ed4052365c | ||
![]() |
377ebadc10 | ||
![]() |
ed4809b71e | ||
![]() |
db37dffdbb | ||
![]() |
13cab7e301 | ||
![]() |
0a50fc66e5 | ||
![]() |
a3d1a3566d | ||
![]() |
ba0be927ed | ||
![]() |
4260606267 | ||
![]() |
4665db4f27 | ||
![]() |
43503ba085 | ||
![]() |
0a83a704f1 | ||
![]() |
08de941c90 | ||
![]() |
62228ef144 | ||
![]() |
9731257782 | ||
![]() |
4ec9c9c16e | ||
![]() |
45436731e2 | ||
![]() |
27730e65e7 | ||
![]() |
a4aba93d57 | ||
![]() |
d93db16963 | ||
![]() |
c327fe11b8 | ||
![]() |
4fbc31d0b0 | ||
![]() |
9a4a1cb4ec | ||
![]() |
202d6957bc | ||
![]() |
14fcff7774 | ||
![]() |
2c9aa1cab4 | ||
![]() |
7745c10d07 | ||
![]() |
c1d571de42 | ||
![]() |
cecb66451c | ||
![]() |
0ef3421fa2 | ||
![]() |
f88e238d41 | ||
![]() |
ce3c8f264d | ||
![]() |
9fbd594f37 | ||
![]() |
5ad95cad90 | ||
![]() |
7e507b40c4 | ||
![]() |
446a9b5c02 | ||
![]() |
e02e61384e | ||
![]() |
5deb570fdf | ||
![]() |
915c46f144 | ||
![]() |
30d6c5eaf3 | ||
![]() |
6e50d1166a | ||
![]() |
0e3eed0563 | ||
![]() |
1b1676cecc | ||
![]() |
d911fe6a0b | ||
![]() |
22253a3385 | ||
![]() |
38640c99e3 | ||
![]() |
d6df8bddea | ||
![]() |
ddfc4bd98e | ||
![]() |
3d6674325c | ||
![]() |
194829f5b1 | ||
![]() |
11a77253f4 | ||
![]() |
67be2343f8 | ||
![]() |
e9b1b3d853 | ||
![]() |
8a33d174d7 | ||
![]() |
226d6216b7 | ||
![]() |
1925bb01be | ||
![]() |
82a4806e01 | ||
![]() |
ce419fae7b | ||
![]() |
c68b76e2da | ||
![]() |
342020b420 | ||
![]() |
1e6e99e3c7 | ||
![]() |
2e9aafc377 | ||
![]() |
299c863f49 | ||
![]() |
c2792a28ba | ||
![]() |
635a027a8e | ||
![]() |
a45b8ca8e7 | ||
![]() |
1e6e945a07 | ||
![]() |
8666e6baae | ||
![]() |
f71157c24d | ||
![]() |
e87a2b36cf | ||
![]() |
5418474f64 | ||
![]() |
8836ba6ceb | ||
![]() |
509c5b497a | ||
![]() |
e00bcc9f48 | ||
![]() |
bdef9fd040 | ||
![]() |
c956491ec5 | ||
![]() |
68bc549d6a | ||
![]() |
9c64eafc21 | ||
![]() |
b05e86d442 | ||
![]() |
fe5f9576c6 | ||
![]() |
1b282b65b7 | ||
![]() |
e49664bad3 | ||
![]() |
2db9f33c41 | ||
![]() |
2a30b55a43 | ||
![]() |
9d0b20adce | ||
![]() |
acd5e1c081 | ||
![]() |
cc1c5e45b2 | ||
![]() |
038199c447 | ||
![]() |
8a1eab7ceb | ||
![]() |
bc5bd35448 | ||
![]() |
1795fd56b7 | ||
![]() |
3d788d6056 | ||
![]() |
4a7c33edad | ||
![]() |
797f60d725 | ||
![]() |
2427d68aa1 | ||
![]() |
00c6b0f8ed | ||
![]() |
7560f98d70 | ||
![]() |
7b8d4ab3d6 | ||
![]() |
07a1a805f6 | ||
![]() |
d8bab6aba9 | ||
![]() |
a930e2dc75 | ||
![]() |
2eb35668fa | ||
![]() |
07f4e5ac5c | ||
![]() |
1533c22d5c | ||
![]() |
db82a90414 | ||
![]() |
51a693badf | ||
![]() |
2aa8f5b352 | ||
![]() |
93b3b8f985 | ||
![]() |
92c8bd80b5 | ||
![]() |
528af0157d | ||
![]() |
10a77b6278 | ||
![]() |
03bbf6a582 | ||
![]() |
63fcb649d2 | ||
![]() |
4f60a92b92 | ||
![]() |
0419c1a41f | ||
![]() |
2d5ae78521 | ||
![]() |
959134df02 | ||
![]() |
cf03f103ab | ||
![]() |
a9f9fc4ce2 | ||
![]() |
cfb370a3c8 | ||
![]() |
353435c8d5 | ||
![]() |
c8c85d096b | ||
![]() |
19c9c8f227 | ||
![]() |
6ea2a29eea | ||
![]() |
59f3f819a6 | ||
![]() |
93e8f52880 | ||
![]() |
02810efcc4 | ||
![]() |
4a8a7c997e | ||
![]() |
4b9be7ce16 | ||
![]() |
f3ec09e480 | ||
![]() |
8291a84e3e | ||
![]() |
b0e1f0f73a | ||
![]() |
a66b966e7d | ||
![]() |
5f56040c64 | ||
![]() |
eaccd22267 | ||
![]() |
27845a7345 | ||
![]() |
f7ef8180e4 | ||
![]() |
5958eb9a55 | ||
![]() |
3ef2912b60 | ||
![]() |
fa9c6a765a | ||
![]() |
c4a8899780 | ||
![]() |
3cc4628d03 | ||
![]() |
b6c5223221 | ||
![]() |
cbd6d4251c | ||
![]() |
fdcbb5b432 | ||
![]() |
de09e31815 | ||
![]() |
f55e911313 | ||
![]() |
465a91dbf3 | ||
![]() |
835a7833ae | ||
![]() |
179717d40c | ||
![]() |
3d4d789f7f | ||
![]() |
d97fb19f05 | ||
![]() |
0dd3757df2 | ||
![]() |
c32a4546f3 | ||
![]() |
1bb025ccd0 | ||
![]() |
2b8033a97f | ||
![]() |
21a3a8c594 | ||
![]() |
1026e90296 | ||
![]() |
0eca602e61 | ||
![]() |
7f75ca81f1 | ||
![]() |
8af05e2726 | ||
![]() |
0a478ee1da | ||
![]() |
a4bdc5a05f | ||
![]() |
d425767dae | ||
![]() |
c78382c119 | ||
![]() |
ee15ddfbc3 | ||
![]() |
0af14eb77e | ||
![]() |
583cc4bc8a | ||
![]() |
2ee92f48e6 | ||
![]() |
d05e02ab3e | ||
![]() |
abb9f8e233 | ||
![]() |
f873ef9b59 | ||
![]() |
1255b56522 | ||
![]() |
fd9bb4d8cc | ||
![]() |
9328576b55 | ||
![]() |
70a1edd1dd | ||
![]() |
87e4c209f4 | ||
![]() |
3d0a5642cc | ||
![]() |
e211d812ad | ||
![]() |
0dcf673b87 | ||
![]() |
cb14e1f20c | ||
![]() |
52087c0e30 | ||
![]() |
1b9286db76 | ||
![]() |
bc92c0b052 | ||
![]() |
245bb639f2 | ||
![]() |
8d81ed58c8 | ||
![]() |
7890ca85a8 | ||
![]() |
07bab7b264 | ||
![]() |
5730c14dc1 | ||
![]() |
f8e8b5ad18 | ||
![]() |
fd2728c02c | ||
![]() |
7e2bf920e1 | ||
![]() |
1f65328f2d | ||
![]() |
4f731baa00 | ||
![]() |
5abb3dd8c1 | ||
![]() |
0a672c55c5 | ||
![]() |
a6b2299c74 | ||
![]() |
37cc6709d4 | ||
![]() |
f4ffbe67e2 | ||
![]() |
9f32d72a41 | ||
![]() |
64a117d8ac | ||
![]() |
ebf0bdc840 | ||
![]() |
cc0a120bf6 | ||
![]() |
fe2fe7468f | ||
![]() |
b12a10ccb5 | ||
![]() |
2ad2a4b198 | ||
![]() |
6a62f05657 | ||
![]() |
4910f60ec4 | ||
![]() |
d35168e88f | ||
![]() |
01b3d2aca9 | ||
![]() |
29e8d1cff0 | ||
![]() |
4e1d10cc08 | ||
![]() |
3575d94ca1 | ||
![]() |
d91546b532 | ||
![]() |
9f554f4917 | ||
![]() |
d4720a9244 | ||
![]() |
5c466712db | ||
![]() |
6dc7e852ae | ||
![]() |
785f614bd9 | ||
![]() |
0a8e27249d | ||
![]() |
15ee87ee67 | ||
![]() |
12612a16df | ||
![]() |
4f449e2600 | ||
![]() |
7f49f039fd | ||
![]() |
88dc65bc4e | ||
![]() |
6edebe18ad | ||
![]() |
38b3a9205d | ||
![]() |
4b796b4929 | ||
![]() |
83cabcac28 | ||
![]() |
d308c5d9b9 | ||
![]() |
9f032a61a9 | ||
![]() |
0f58214ba1 | ||
![]() |
c48a60cce6 | ||
![]() |
cd3ffceeff | ||
![]() |
9be4a00169 | ||
![]() |
a9c7a39a47 | ||
![]() |
abcdd60a21 | ||
![]() |
a94f85a100 | ||
![]() |
9755bf723f | ||
![]() |
a71ebcf47e | ||
![]() |
72695631cd | ||
![]() |
2af211b543 | ||
![]() |
6e5e2625d6 | ||
![]() |
23c1c2f5eb | ||
![]() |
da85ee5d01 | ||
![]() |
9612bc78fe | ||
![]() |
d408e8653c | ||
![]() |
cc76ccc3c9 | ||
![]() |
105a00d3e4 | ||
![]() |
2c08cba8cc | ||
![]() |
344b11a204 | ||
![]() |
1ff5bf0fd5 | ||
![]() |
2b86137388 | ||
![]() |
c29cf7f77c | ||
![]() |
193cb46d60 | ||
![]() |
9dc864d486 | ||
![]() |
cee166839a | ||
![]() |
1a60a3c728 | ||
![]() |
5d946778cb | ||
![]() |
ac5f85820f | ||
![]() |
716e100a28 | ||
![]() |
7b8cb16c12 | ||
![]() |
00d46424a3 | ||
![]() |
2a5f940744 | ||
![]() |
13cc016b36 | ||
![]() |
a8d49c27c8 | ||
![]() |
8fdbe447c1 | ||
![]() |
a8522e91b5 | ||
![]() |
5754f4463d | ||
![]() |
d4118ade0f | ||
![]() |
6d80f15a98 | ||
![]() |
f8aa472409 | ||
![]() |
df22fd00ca | ||
![]() |
ce2743a982 | ||
![]() |
92b32458ad | ||
![]() |
d57e8a45d3 | ||
![]() |
551d3ffdf3 | ||
![]() |
7add6eb736 | ||
![]() |
a28616d535 | ||
![]() |
a288fd370f | ||
![]() |
acd335e249 | ||
![]() |
764ae7e0b6 | ||
![]() |
da0bfa1945 | ||
![]() |
3c61d709b5 | ||
![]() |
ffc92a7b63 | ||
![]() |
af0c7b5a50 | ||
![]() |
1904c4d057 | ||
![]() |
542f169b36 | ||
![]() |
65a30bf60c | ||
![]() |
2e51da32f0 | ||
![]() |
0562242043 | ||
![]() |
debcdefc21 | ||
![]() |
6b7e78320d | ||
![]() |
0de3f3a332 | ||
![]() |
4fcb4d449e | ||
![]() |
408fe25abd | ||
![]() |
236e5e0b25 | ||
![]() |
ebe0caba83 | ||
![]() |
9d33c0cfaf | ||
![]() |
7962130a0c | ||
![]() |
9690434cac | ||
![]() |
7304544c37 | ||
![]() |
5a3408c242 | ||
![]() |
16996f25af | ||
![]() |
0c12586019 | ||
![]() |
93a1adaa56 | ||
![]() |
83e65e2cc6 | ||
![]() |
36586b798e | ||
![]() |
20c351949f | ||
![]() |
b63bd92d81 | ||
![]() |
9f3bb7f4d6 | ||
![]() |
73bb346c00 | ||
![]() |
33703a3b53 | ||
![]() |
b7a4f97eca | ||
![]() |
dd4efe0f51 | ||
![]() |
7e0522c3b3 | ||
![]() |
e682abfb75 | ||
![]() |
24e202a3d7 | ||
![]() |
ac9a881ab5 | ||
![]() |
4d287a1f83 | ||
![]() |
b8d6b1ebdd | ||
![]() |
8ca1b9320d | ||
![]() |
cba3992d2b | ||
![]() |
96d6e337be | ||
![]() |
959f7ae046 | ||
![]() |
9572a58764 | ||
![]() |
393ae9e5dc | ||
![]() |
63e10314bd | ||
![]() |
b599417a37 | ||
![]() |
899eab4e5c | ||
![]() |
3f21c87a3d | ||
![]() |
c296a60bab | ||
![]() |
5f78f18cb4 | ||
![]() |
0b8d356865 | ||
![]() |
e8d1318a5b | ||
![]() |
07ce07c4a5 | ||
![]() |
a07220f383 | ||
![]() |
f21ed24a49 | ||
![]() |
e3c38b93f4 | ||
![]() |
b398727413 | ||
![]() |
9bc2ab29a1 | ||
![]() |
51f1ff26f1 | ||
![]() |
97d5e6512d | ||
![]() |
b76c67fc9b | ||
![]() |
b96a70cd55 | ||
![]() |
982ab93cdb | ||
![]() |
c7f4e1152d | ||
![]() |
519988326b | ||
![]() |
b518f4b03c | ||
![]() |
5493fdfcb7 | ||
![]() |
179767e9f8 | ||
![]() |
25b3bb1285 | ||
![]() |
841c8ab1f1 | ||
![]() |
1ce17e2847 | ||
![]() |
a09b206b0e | ||
![]() |
bb4617c53b | ||
![]() |
cfd18bfb74 | ||
![]() |
e225d6f546 | ||
![]() |
60fe48d355 | ||
![]() |
2dcd0d2b0a | ||
![]() |
8e11aa9130 | ||
![]() |
f6e223c18d | ||
![]() |
9d29b55bee | ||
![]() |
92aa8580db | ||
![]() |
538028a003 | ||
![]() |
c53575a74f | ||
![]() |
193016a46a | ||
![]() |
aaa50b4d1d | ||
![]() |
a43120320e | ||
![]() |
b8bb0c038d | ||
![]() |
dc79fc2919 | ||
![]() |
30787fef60 | ||
![]() |
445ae156ef | ||
![]() |
62a0cfb0f6 | ||
![]() |
96bc3ef99a | ||
![]() |
1d3b95d24f | ||
![]() |
56fe4b07f3 | ||
![]() |
ea60f7005b | ||
![]() |
9eb59062aa | ||
![]() |
d00927c31f | ||
![]() |
c03017208d | ||
![]() |
73f945458a | ||
![]() |
db12234611 | ||
![]() |
ed1cd4632f | ||
![]() |
17d3755152 | ||
![]() |
d7c0c2ea72 | ||
![]() |
7c823c98ae | ||
![]() |
97508a6f31 | ||
![]() |
2507a41b6e | ||
![]() |
9833accc79 | ||
![]() |
d46123771a | ||
![]() |
87fe84b1ac | ||
![]() |
21140f437e | ||
![]() |
ba9e410393 | ||
![]() |
9b628546c1 | ||
![]() |
d0837fada8 | ||
![]() |
520647d72f | ||
![]() |
51c888845c | ||
![]() |
e4606219bc | ||
![]() |
716335df2c | ||
![]() |
ad4f90c502 | ||
![]() |
a1bdfa7560 | ||
![]() |
587fb2a170 | ||
![]() |
7d801ff84c | ||
![]() |
d69accd9a5 | ||
![]() |
1127750c5e | ||
![]() |
7758bd89c1 | ||
![]() |
de7264327a | ||
![]() |
c3f0932794 | ||
![]() |
367907e037 | ||
![]() |
2d15bd651e | ||
![]() |
4b1d7863f8 | ||
![]() |
e425d768dd | ||
![]() |
34ca807044 | ||
![]() |
9075146b47 | ||
![]() |
26c4591baa | ||
![]() |
2aac8c55e7 | ||
![]() |
8af55efdb3 | ||
![]() |
9d6e07ff96 | ||
![]() |
8f58eee6af | ||
![]() |
8dd3d78f21 | ||
![]() |
48161fd02f | ||
![]() |
b61410826d | ||
![]() |
2f0188b280 | ||
![]() |
3a4fffdb0b | ||
![]() |
6393072e68 | ||
![]() |
109910d18f | ||
![]() |
8874aaabe9 | ||
![]() |
cafbea9c42 | ||
![]() |
4843ee80a7 | ||
![]() |
4511c8f30c | ||
![]() |
4cf1e52ac0 | ||
![]() |
b501b7f47c | ||
![]() |
cc275f9877 | ||
![]() |
7aae55cde7 | ||
![]() |
85eaa219c6 | ||
![]() |
7d5ecb8ba4 | ||
![]() |
1fd142d337 | ||
![]() |
d75c6aecbe | ||
![]() |
dffe0f656d | ||
![]() |
890639436b | ||
![]() |
99f66d7c5d | ||
![]() |
05faa52425 | ||
![]() |
9e1a8b646b | ||
![]() |
8f6ec03446 | ||
![]() |
c56b4fade3 | ||
![]() |
61aaaabcb5 | ||
![]() |
d57cf93580 | ||
![]() |
82ad5c103d | ||
![]() |
a0b5bc5456 | ||
![]() |
c810e541ea | ||
![]() |
05ea3b8187 | ||
![]() |
8301dffb21 | ||
![]() |
01be5243de | ||
![]() |
334196799a | ||
![]() |
c11bbcf442 | ||
![]() |
8e3a7576ea | ||
![]() |
deca6f03ba | ||
![]() |
401064d3c8 | ||
![]() |
b6f59d3c98 | ||
![]() |
1fb3663398 | ||
![]() |
5c1604e959 | ||
![]() |
17b1f3e465 | ||
![]() |
9a68bdeec1 | ||
![]() |
9b947ef734 | ||
![]() |
66432608ed | ||
![]() |
d8153ac8fc | ||
![]() |
27d9f82f7d | ||
![]() |
5b55bcd879 | ||
![]() |
5cfd28881b | ||
![]() |
bc54a42e01 | ||
![]() |
03f9964c59 | ||
![]() |
f159219d2c | ||
![]() |
e714f32737 | ||
![]() |
20858db96d | ||
![]() |
89b82bb778 | ||
![]() |
2c886d739f | ||
![]() |
1ccf4e49bc | ||
![]() |
7d63e3e088 | ||
![]() |
828523f281 | ||
![]() |
afe3831f25 | ||
![]() |
3888c56f1a | ||
![]() |
6f07966ef8 | ||
![]() |
09eafe8abd | ||
![]() |
6719a42e27 | ||
![]() |
4b98a70ee8 | ||
![]() |
db3f5447ca | ||
![]() |
fed63f645d | ||
![]() |
e7315bb570 | ||
![]() |
cd2404f26a | ||
![]() |
b866166425 | ||
![]() |
46580376dd | ||
![]() |
b8bfb44aec | ||
![]() |
a153f572d0 | ||
![]() |
66c30a59e7 | ||
![]() |
10b8efc5cb | ||
![]() |
c65d414b7b | ||
![]() |
7f7d89c745 | ||
![]() |
742028b691 | ||
![]() |
62f685bac2 | ||
![]() |
0b3333e88c | ||
![]() |
c341a99b83 | ||
![]() |
f43c420d59 | ||
![]() |
0393970a80 | ||
![]() |
1865e0661f | ||
![]() |
c07b1194b3 | ||
![]() |
bf802628b9 | ||
![]() |
36020373cd | ||
![]() |
43e73d69de | ||
![]() |
47a3f649d2 | ||
![]() |
5c63f8e52a | ||
![]() |
01c553ef13 | ||
![]() |
f229e4e12a | ||
![]() |
40cf4c8d32 | ||
![]() |
ee38c419de | ||
![]() |
10baa34c18 | ||
![]() |
343b67fa7f | ||
![]() |
6de8b4e35f | ||
![]() |
57e535c2c8 | ||
![]() |
af5b22a265 | ||
![]() |
77972c961b | ||
![]() |
a3efa5676b | ||
![]() |
014dbc2a86 | ||
![]() |
226a2941d6 | ||
![]() |
c269c8fd3f | ||
![]() |
d8fc3c1ebf | ||
![]() |
a5c6ffd1b9 | ||
![]() |
9aaaaae175 | ||
![]() |
7d39b69540 | ||
![]() |
09bad14c3d | ||
![]() |
369c9dc6e2 | ||
![]() |
9676d2cee7 | ||
![]() |
5156c67226 | ||
![]() |
4d48fc3d85 | ||
![]() |
20da329a21 | ||
![]() |
4b664cc142 | ||
![]() |
c9b620fdb2 | ||
![]() |
25c886d401 | ||
![]() |
740805356f | ||
![]() |
ce5fb57577 | ||
![]() |
3e20d2b454 | ||
![]() |
a9e8186491 | ||
![]() |
3bb909b026 | ||
![]() |
b921d91aeb | ||
![]() |
05790954c6 | ||
![]() |
13014c1351 | ||
![]() |
e34c63b830 | ||
![]() |
943100d758 | ||
![]() |
55f40d66f2 | ||
![]() |
593e5ac79c | ||
![]() |
ef31bce5ee | ||
![]() |
3c75eb96f1 | ||
![]() |
f34dfde925 | ||
![]() |
e3b72fe0aa | ||
![]() |
60de74a375 | ||
![]() |
55e58f8d35 | ||
![]() |
a465254418 | ||
![]() |
5d27a138cf | ||
![]() |
22f4b036df | ||
![]() |
03f694922d | ||
![]() |
a841e287e5 | ||
![]() |
5d2afdd825 | ||
![]() |
67240e2339 | ||
![]() |
f84a8eccfa | ||
![]() |
68a058e4f1 | ||
![]() |
d678b42ece | ||
![]() |
2cf63cda08 | ||
![]() |
7bd4eeb0df | ||
![]() |
dc3ee7c779 | ||
![]() |
e8cc97a8e5 | ||
![]() |
3b837e1d54 | ||
![]() |
bb6c2050bc | ||
![]() |
082d4f9691 | ||
![]() |
153d68a9cd | ||
![]() |
0404faa856 | ||
![]() |
afbc2d6b8f | ||
![]() |
89ecc8bd2f | ||
![]() |
7f21a2b319 | ||
![]() |
e2f07f6723 | ||
![]() |
a475e143b7 | ||
![]() |
e50fd80b2e | ||
![]() |
68ea1abc05 | ||
![]() |
2e76b306c4 | ||
![]() |
ca3cac4ed3 | ||
![]() |
41852460e1 | ||
![]() |
9ec4e083d9 | ||
![]() |
9560a1c4a7 | ||
![]() |
4f5a47ace7 | ||
![]() |
01c4d662f2 | ||
![]() |
9bdda77e89 | ||
![]() |
194024edb9 | ||
![]() |
bef0d3a6a1 | ||
![]() |
47a024b795 | ||
![]() |
39847f9c9d | ||
![]() |
fa7bd28c92 | ||
![]() |
279f78e4a8 | ||
![]() |
8ec3cbdb33 | ||
![]() |
7449f7e73f | ||
![]() |
d680fde759 | ||
![]() |
f24f21ca91 | ||
![]() |
c8ea37eec0 | ||
![]() |
b71f452795 | ||
![]() |
7ea1ece169 | ||
![]() |
aece3a37c0 | ||
![]() |
705871f8dc | ||
![]() |
4a11975349 | ||
![]() |
4d3d27f2c4 | ||
![]() |
d784a30d42 | ||
![]() |
35f776284b | ||
![]() |
f659a6fe37 | ||
![]() |
ad53c99fc4 | ||
![]() |
fa0172d00c | ||
![]() |
ba77a88714 | ||
![]() |
9b39087102 | ||
![]() |
845411b48c | ||
![]() |
d715867b09 | ||
![]() |
0ca2cdfbed | ||
![]() |
a00961b9ef | ||
![]() |
701c188bab | ||
![]() |
e81002807f | ||
![]() |
0d1c72386e | ||
![]() |
c91779dffe | ||
![]() |
3853cc9214 | ||
![]() |
a66b3f6b80 | ||
![]() |
c97ec32343 | ||
![]() |
2abba7e445 | ||
![]() |
f887c27ad1 | ||
![]() |
6ee8d74899 | ||
![]() |
f196c72563 | ||
![]() |
419e564441 | ||
![]() |
de97b54c95 | ||
![]() |
07001f7b5c | ||
![]() |
bee17fce64 | ||
![]() |
718904a853 | ||
![]() |
e14d652651 | ||
![]() |
98ae5270ef | ||
![]() |
19ccf0ab40 | ||
![]() |
72af4a69d6 | ||
![]() |
fe50f4229c | ||
![]() |
6021bec5ee | ||
![]() |
7fcadc85fa | ||
![]() |
ca4de877c1 | ||
![]() |
1dfecf9618 | ||
![]() |
0a3505ed89 | ||
![]() |
33cbf7eabe | ||
![]() |
935d97ce1a | ||
![]() |
9f73f0ca8d | ||
![]() |
5d7f971a82 | ||
![]() |
d8cdbac15e | ||
![]() |
03b8c1348c | ||
![]() |
25a0be7672 | ||
![]() |
08f1ce2d54 | ||
![]() |
bea20d0495 | ||
![]() |
c42430ccf9 | ||
![]() |
5ae10e8516 | ||
![]() |
e3f4a9ce5b | ||
![]() |
cf1fb606fb | ||
![]() |
54ec81b67d | ||
![]() |
f2a9725572 | ||
![]() |
4765114e80 | ||
![]() |
5ff757ad65 | ||
![]() |
1642c68493 | ||
![]() |
f31f10cea9 | ||
![]() |
76e0bbb55d | ||
![]() |
f43af9c0a5 | ||
![]() |
f7a3d2705c | ||
![]() |
22c8af0cc5 | ||
![]() |
f263a5221d | ||
![]() |
3834ab8ede | ||
![]() |
e2e167630d | ||
![]() |
01dd44300b | ||
![]() |
b30160d671 | ||
![]() |
f44d505b41 | ||
![]() |
16fa6904d9 | ||
![]() |
b58c17e75e | ||
![]() |
ae590d42dc | ||
![]() |
d7917160c0 | ||
![]() |
01e4414d17 | ||
![]() |
0bc2eb530d | ||
![]() |
12b124e5a3 | ||
![]() |
478a4b2593 | ||
![]() |
9752e30eb4 | ||
![]() |
af6e87ba31 | ||
![]() |
64d390ad0f | ||
![]() |
c94bcb6896 | ||
![]() |
97f9df2f2d | ||
![]() |
4e7f68a86c | ||
![]() |
2f7f677549 | ||
![]() |
12a8a1531d | ||
![]() |
f44d867d3a | ||
![]() |
6f636187f7 | ||
![]() |
9414f89e50 | ||
![]() |
60bf1a5451 | ||
![]() |
32ba8f4731 | ||
![]() |
81f96de2bd | ||
![]() |
0c417755ed | ||
![]() |
93e5bde797 | ||
![]() |
c85f69c9ee | ||
![]() |
b6eaf0a7c5 | ||
![]() |
5f1851bade | ||
![]() |
5c66a02711 | ||
![]() |
bde925a0e3 | ||
![]() |
0f574a765b | ||
![]() |
782b941531 | ||
![]() |
f42c0a0717 | ||
![]() |
13ac14d449 | ||
![]() |
db9cea81db | ||
![]() |
7c1fd542da | ||
![]() |
54a2b2534a | ||
![]() |
f5fb6c1e03 | ||
![]() |
781c0701fc | ||
![]() |
742f1f85dc | ||
![]() |
a648e9be49 | ||
![]() |
fd9441dde2 | ||
![]() |
b5ec59c396 | ||
![]() |
60e4594abd | ||
![]() |
79692ef58a | ||
![]() |
ace7ee5622 | ||
![]() |
741ac679a0 | ||
![]() |
216526e391 | ||
![]() |
d76af2cb61 | ||
![]() |
b7d4c40736 | ||
![]() |
6092af8de6 | ||
![]() |
627424b8b9 | ||
![]() |
e33aff7cf3 | ||
![]() |
ef0bfb237a | ||
![]() |
c042c5568b | ||
![]() |
d84a7ee358 | ||
![]() |
311e1cfb00 |
119
.eslintrc.json
119
.eslintrc.json
@@ -4,8 +4,7 @@
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
@@ -29,63 +28,89 @@
|
||||
"__BUILD__": false,
|
||||
"__VERSION__": false,
|
||||
"__STATIC_PATH__": false,
|
||||
"Polymer": true,
|
||||
"webkitSpeechRecognition": false,
|
||||
"ResizeObserver": false
|
||||
"Polymer": true
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 2,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"no-console": 2,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"class-methods-use-this": "off",
|
||||
"new-cap": "off",
|
||||
"prefer-template": "off",
|
||||
"object-shorthand": "off",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"strict": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-bitwise": "error",
|
||||
"comma-dangle": "off",
|
||||
"vars-on-top": "off",
|
||||
"no-continue": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-multi-assign": "off",
|
||||
"no-console": "error",
|
||||
"radix": "off",
|
||||
"no-alert": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/order": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-cycle": 0,
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-cycle": "off",
|
||||
"import/extensions": [
|
||||
2,
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{ "ts": "never", "js": "never" }
|
||||
],
|
||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"wc/no-self-class": 0,
|
||||
"no-shadow": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-shadow": ["error"]
|
||||
"object-curly-newline": "off",
|
||||
"default-case": "off",
|
||||
"wc/no-self-class": "off",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"off",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase", "snake_case"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": ["variable"],
|
||||
"format": ["camelCase", "snake_case", "UPPER_CASE"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "typeLike",
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"lit/attribute-value-entities": "off"
|
||||
},
|
||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
||||
"plugins": ["disable", "unused-imports"],
|
||||
"processor": "disable/disable"
|
||||
}
|
||||
|
35
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
35
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,8 +1,6 @@
|
||||
name: Report a bug with the UI, Frontend or Lovelace
|
||||
about: Report an issue related to the Home Assistant frontend.
|
||||
description: Report an issue related to the Home Assistant frontend.
|
||||
labels: bug
|
||||
title: ""
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -97,11 +95,7 @@ body:
|
||||
If your issue is about how an entity is shown in the UI, please add the
|
||||
state and attributes for all situations. You can find this information
|
||||
at Developer Tools -> States.
|
||||
value: |
|
||||
```yaml
|
||||
# Paste your state here.
|
||||
|
||||
```
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem-relevant frontend configuration
|
||||
@@ -110,29 +104,18 @@ body:
|
||||
configuration of the used cards. Fill this out even if it seems
|
||||
unimportant to you. Please be sure to remove personal information like
|
||||
passwords, private URLs and other credentials.
|
||||
value: |
|
||||
```yaml
|
||||
# Paste your YAML here.
|
||||
|
||||
```
|
||||
render: yaml
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Javascript errors shown in your browser console/inspector
|
||||
description: >
|
||||
If you come across any Javascript or other error logs, e.g., in your
|
||||
browser console/inspector please provide them.
|
||||
value: |
|
||||
```txt
|
||||
# Paste your logs here.
|
||||
|
||||
```
|
||||
- type: markdown
|
||||
render: txt
|
||||
- type: textarea
|
||||
attributes:
|
||||
value: |
|
||||
## Additional information
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
label: Additional information
|
||||
description: >
|
||||
If you have any additional information for us, use the field below.
|
||||
Please note, you can attach screenshots or screen recordings here,
|
||||
by dragging and dropping files in the field below.
|
||||
Please note, you can attach screenshots or screen recordings here, by
|
||||
dragging and dropping files in the field below.
|
||||
|
82
.github/workflows/ci.yaml
vendored
82
.github/workflows/ci.yaml
vendored
@@ -10,26 +10,21 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -37,54 +32,40 @@ jobs:
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
||||
- name: Run eslint
|
||||
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
||||
run: yarn run lint:eslint
|
||||
- name: Run tsc
|
||||
run: ./node_modules/.bin/tsc
|
||||
run: yarn run lint:types
|
||||
- name: Run prettier
|
||||
run: yarn run lint:prettier
|
||||
- name: Check for duplicate dependencies
|
||||
run: yarn dedupe --check
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Run Mocha
|
||||
run: npm run mocha
|
||||
run: yarn run mocha
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -99,20 +80,11 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
22
.github/workflows/demo.yaml
vendored
22
.github/workflows/demo.yaml
vendored
@@ -4,26 +4,22 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
19
.github/workflows/release.yaml
vendored
19
.github/workflows/release.yaml
vendored
@@ -6,9 +6,9 @@ on:
|
||||
- published
|
||||
|
||||
env:
|
||||
WHEELS_TAG: 3.7-alpine3.11
|
||||
PYTHON_VERSION: 3.7
|
||||
NODE_VERSION: 12.1
|
||||
PYTHON_VERSION: 3.8
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -30,7 +30,15 @@ jobs:
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Download Translations
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
- name: Build and release package
|
||||
run: |
|
||||
python3 -m pip install twine
|
||||
@@ -64,6 +72,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||
tag:
|
||||
- "3.8-alpine3.12"
|
||||
- "3.9-alpine3.13"
|
||||
steps:
|
||||
- name: Download requirements.txt
|
||||
uses: actions/download-artifact@v2
|
||||
@@ -73,7 +84,7 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@master
|
||||
with:
|
||||
tag: ${{ env.WHEELS_TAG }}
|
||||
tag: ${{ matrix.tag }}
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-host: ${{ secrets.WHEELS_HOST }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
44
.github/workflows/translations.yaml
vendored
44
.github/workflows/translations.yaml
vendored
@@ -1,16 +1,14 @@
|
||||
name: Translations
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 0 * * *"
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths:
|
||||
- translations/en.json
|
||||
- src/translations/en.json
|
||||
|
||||
env:
|
||||
NODE_VERSION: 12
|
||||
NODE_VERSION: 14
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
@@ -20,46 +18,8 @@ jobs:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
./script/translations_upload_base
|
||||
|
||||
download:
|
||||
name: Download
|
||||
needs: upload
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Download Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
npm install
|
||||
./script/translations_download
|
||||
|
||||
- name: Initialize git
|
||||
uses: home-assistant/actions/helpers/git-init@master
|
||||
with:
|
||||
name: GitHub Action
|
||||
email: github-action@users.noreply.github.com
|
||||
|
||||
- name: Update translation
|
||||
run: |
|
||||
git add translations
|
||||
git commit -am "Translation update"
|
||||
git push
|
||||
|
29
.gitignore
vendored
29
.gitignore
vendored
@@ -1,10 +1,23 @@
|
||||
.DS_Store
|
||||
.reify-cache
|
||||
|
||||
# build
|
||||
build
|
||||
build-translations/*
|
||||
node_modules/*
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
.reify-cache
|
||||
dist
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
node_modules/*
|
||||
yarn-error.log
|
||||
npm-debug.log
|
||||
|
||||
# Python stuff
|
||||
*.py[cod]
|
||||
@@ -14,11 +27,8 @@ hass_frontend/*
|
||||
# venv stuff
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
venv
|
||||
venv/*
|
||||
.venv
|
||||
lib
|
||||
bin
|
||||
dist
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
@@ -31,9 +41,8 @@ src/cast/dev_const.ts
|
||||
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
||||
#asdf
|
||||
# asdf
|
||||
.tool-versions
|
||||
|
||||
# Home Assistant config
|
||||
|
4
.mocharc.cjs
Normal file
4
.mocharc.cjs
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
require: "test-mocha/testconf.js",
|
||||
timeout: 10000,
|
||||
};
|
1536
.yarn/patches/@lit-labs/virtualizer/0.7.0.patch
Normal file
1536
.yarn/patches/@lit-labs/virtualizer/0.7.0.patch
Normal file
File diff suppressed because one or more lines are too long
29
.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
Normal file
29
.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
Normal file
@@ -0,0 +1,29 @@
|
||||
diff --git a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
index d92179f7fd5315203f870a6963e871dc8ddf6c0c..362e284121b97e0fba0925225777aebc32e26b8d 100644
|
||||
--- a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
+++ b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
@@ -1,14 +1,15 @@
|
||||
-let _ET, ET;
|
||||
+let _ET;
|
||||
+let ET;
|
||||
export default async function EventTarget() {
|
||||
- return ET || init();
|
||||
+ return ET || init();
|
||||
}
|
||||
async function init() {
|
||||
- _ET = window.EventTarget;
|
||||
- try {
|
||||
- new _ET();
|
||||
- }
|
||||
- catch (_a) {
|
||||
- _ET = (await import('event-target-shim')).EventTarget;
|
||||
- }
|
||||
- return (ET = _ET);
|
||||
+ _ET = window.EventTarget;
|
||||
+ try {
|
||||
+ new _ET();
|
||||
+ } catch (_a) {
|
||||
+ _ET = (await import("event-target-shim")).default.EventTarget;
|
||||
+ }
|
||||
+ return (ET = _ET);
|
||||
}
|
34
.yarn/patches/@polymer/polymer/pr-5569.patch
Normal file
34
.yarn/patches/@polymer/polymer/pr-5569.patch
Normal file
@@ -0,0 +1,34 @@
|
||||
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
|
||||
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
|
||||
--- a/lib/legacy/class.js
|
||||
+++ b/lib/legacy/class.js
|
||||
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
|
||||
// only proceed if the generated class' prototype has not been registered.
|
||||
const generatedProto = PolymerGenerated.prototype;
|
||||
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
|
||||
- generatedProto.__hasRegisterFinished = true;
|
||||
+ // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
+ // and not the generated class prototype; if the element has been
|
||||
+ // extended, these are *not* the same.
|
||||
+ const proto = Object.getPrototypeOf(this);
|
||||
+ // Only set flag when generated prototype itself is registered,
|
||||
+ // as this element may be extended from, and needs to run `registered`
|
||||
+ // on all behaviors on the subclass as well.
|
||||
+ if (proto === generatedProto) {
|
||||
+ generatedProto.__hasRegisterFinished = true;
|
||||
+ }
|
||||
// ensure superclass is registered first.
|
||||
super._registered();
|
||||
// copy properties onto the generated class lazily if we're optimizing,
|
||||
- if (legacyOptimizations) {
|
||||
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
|
||||
+ generatedProto.__hasCopiedProperties = true;
|
||||
copyPropertiesToProto(generatedProto);
|
||||
}
|
||||
- // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
- // and not the generated class prototype; if the element has been
|
||||
- // extended, these are *not* the same.
|
||||
- const proto = Object.getPrototypeOf(this);
|
||||
let list = lifecycle.beforeRegister;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
77
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
77
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
55
.yarn/releases/yarn-2.4.2.cjs
vendored
Executable file
55
.yarn/releases/yarn-2.4.2.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
9
.yarnrc.yml
Normal file
9
.yarnrc.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
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-2.4.2.cjs
|
170
build-scripts/babel-plugins/inline-constants-plugin.js
Normal file
170
build-scripts/babel-plugins/inline-constants-plugin.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||
module.exports = function inlineConstants(babel, options, cwd) {
|
||||
const t = babel.types;
|
||||
|
||||
if (!Array.isArray(options.modules)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `modules` array to be passed"
|
||||
);
|
||||
}
|
||||
|
||||
if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected `resolveExtensions` to be an array"
|
||||
);
|
||||
}
|
||||
|
||||
const ignoreModuleNotFound = options.ignoreModuleNotFound;
|
||||
const resolveExtensions = options.resolveExtensions;
|
||||
|
||||
const hasRelativeModules = options.modules.some(
|
||||
(module) => module.startsWith(".") || module.startsWith("/")
|
||||
);
|
||||
|
||||
const modules = Object.fromEntries(
|
||||
options.modules.map((module) => {
|
||||
const absolute = module.startsWith(".")
|
||||
? require.resolve(module, { paths: [cwd] })
|
||||
: module;
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
return [absolute, require(absolute)];
|
||||
})
|
||||
);
|
||||
|
||||
const toLiteral = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return t.stringLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return t.numericLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return t.booleanLiteral(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return t.nullLiteral();
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot handle non-literal `" + value + "`"
|
||||
);
|
||||
};
|
||||
|
||||
const resolveAbsolute = (value, state, resolveExtensionIndex) => {
|
||||
if (!state.filename) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `filename` to be set for files"
|
||||
);
|
||||
}
|
||||
|
||||
if (resolveExtensions && resolveExtensionIndex !== undefined) {
|
||||
value += resolveExtensions[resolveExtensionIndex];
|
||||
}
|
||||
|
||||
try {
|
||||
return require.resolve(value, { paths: [path.dirname(state.filename)] });
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === "MODULE_NOT_FOUND" &&
|
||||
resolveExtensions &&
|
||||
(resolveExtensionIndex === undefined ||
|
||||
resolveExtensionIndex < resolveExtensions.length - 1)
|
||||
) {
|
||||
const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1;
|
||||
return resolveAbsolute(value, state, resolveExtensionIdx);
|
||||
}
|
||||
|
||||
if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const importDeclaration = (p, state) => {
|
||||
if (p.node.type !== "ImportDeclaration") {
|
||||
return;
|
||||
}
|
||||
const absolute =
|
||||
hasRelativeModules && p.node.source.value.startsWith(".")
|
||||
? resolveAbsolute(p.node.source.value, state)
|
||||
: p.node.source.value;
|
||||
|
||||
if (!absolute || !(absolute in modules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const module = modules[absolute];
|
||||
|
||||
for (const specifier of p.node.specifiers) {
|
||||
if (
|
||||
specifier.type === "ImportDefaultSpecifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!("default" in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access default export from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module.default);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else if (
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported &&
|
||||
specifier.imported.type === "Identifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!(specifier.imported.name in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access `" +
|
||||
specifier.imported.name +
|
||||
"` from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module[specifier.imported.name]);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else {
|
||||
throw new Error("Cannot handle specifier `" + specifier.type + "`");
|
||||
}
|
||||
}
|
||||
p.remove();
|
||||
};
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
ImportDeclaration: importDeclaration,
|
||||
},
|
||||
};
|
||||
};
|
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const env = require("./env.js");
|
||||
const paths = require("./paths.js");
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Bloats bundle and it's not used.
|
||||
path.resolve(require.resolve("moment"), "../locale"),
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
@@ -19,7 +18,8 @@ module.exports.emptyPackages = ({ latestBuild }) =>
|
||||
require.resolve("@polymer/paper-styles/default-theme.js"),
|
||||
// Loads stuff from a CDN
|
||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
|
||||
// Compatibility not needed for latest builds
|
||||
latestBuild &&
|
||||
// wrapped in require.resolve so it blows up if file no longer exists
|
||||
@@ -51,17 +51,29 @@ module.exports.terserOptions = (latestBuild) => ({
|
||||
|
||||
module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
babelrc: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
!latestBuild && [
|
||||
require("@babel/preset-env").default,
|
||||
"@babel/preset-env",
|
||||
{
|
||||
useBuiltIns: "entry",
|
||||
corejs: "3.6",
|
||||
corejs: "3.15",
|
||||
bugfixes: true,
|
||||
},
|
||||
],
|
||||
require("@babel/preset-typescript").default,
|
||||
"@babel/preset-typescript",
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
[
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
"build-scripts/babel-plugins/inline-constants-plugin.js"
|
||||
),
|
||||
{
|
||||
modules: ["@mdi/js"],
|
||||
ignoreModuleNotFound: true,
|
||||
},
|
||||
],
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
!latestBuild && [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
@@ -72,23 +84,18 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
[
|
||||
require("@babel/plugin-proposal-decorators").default,
|
||||
{ decoratorsBeforeExport: true },
|
||||
],
|
||||
[
|
||||
require("@babel/plugin-proposal-class-properties").default,
|
||||
{ loose: true },
|
||||
],
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||
["@babel/plugin-proposal-private-methods", { loose: true }],
|
||||
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||
["@babel/plugin-proposal-class-properties", { loose: true }],
|
||||
].filter(Boolean),
|
||||
exclude: [
|
||||
// \\ for Windows, / for Mac OS and Linux
|
||||
/node_modules[\\/]core-js/,
|
||||
/node_modules[\\/]webpack[\\/]buildin/,
|
||||
],
|
||||
});
|
||||
|
||||
// Are already ES5, cause warnings when babelified.
|
||||
module.exports.babelExclude = () => [
|
||||
require.resolve("@mdi/js/mdi.js"),
|
||||
require.resolve("hls.js"),
|
||||
];
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const paths = require("./paths.js");
|
||||
|
@@ -47,8 +47,8 @@ gulp.task(
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-app",
|
||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||
...// Don't compress running tests
|
||||
(env.isTest() ? [] : ["compress-app"]),
|
||||
// Don't compress running tests
|
||||
...(env.isTest() ? [] : ["compress-app"]),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-app-prod",
|
||||
|
@@ -302,15 +302,23 @@ gulp.task("gen-index-hassio-prod", async () => {
|
||||
|
||||
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||
`
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
function loadES5() {
|
||||
var el = document.createElement('script');
|
||||
el.src = '${es5Entrypoint}';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) {
|
||||
loadES5();
|
||||
} else {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
loadES5();
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ encoding: "utf-8" }
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const cpx = require("cpx");
|
||||
const fs = require("fs-extra");
|
||||
const paths = require("../paths");
|
||||
|
||||
@@ -13,8 +12,10 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
|
||||
const copyFileDir = (fromFile, toDir) =>
|
||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||
|
||||
const genStaticPath = (staticDir) => (...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
const genStaticPath =
|
||||
(staticDir) =>
|
||||
(...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
|
||||
function copyTranslations(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
@@ -62,9 +63,12 @@ function copyLoaderJS(staticDir) {
|
||||
function copyFonts(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
// Local fonts
|
||||
cpx.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/*.woff2"),
|
||||
staticPath("fonts/roboto")
|
||||
fs.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/"),
|
||||
staticPath("fonts/roboto/"),
|
||||
{
|
||||
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,6 +89,11 @@ gulp.task("copy-translations-app", async () => {
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-translations-supervisor", async () => {
|
||||
const staticDir = paths.hassio_output_static;
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-app", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
// Basic static files
|
||||
|
@@ -10,6 +10,8 @@ require("./gen-icons-json.js");
|
||||
require("./webpack.js");
|
||||
require("./compress.js");
|
||||
require("./rollup.js");
|
||||
require("./gather-static.js");
|
||||
require("./translations.js");
|
||||
|
||||
gulp.task(
|
||||
"develop-hassio",
|
||||
@@ -20,6 +22,8 @@ gulp.task(
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
"gen-index-hassio-dev",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||
)
|
||||
);
|
||||
@@ -32,6 +36,8 @@ gulp.task(
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||
"gen-index-hassio-prod",
|
||||
...// Don't compress running tests
|
||||
|
@@ -266,6 +266,7 @@ gulp.task(taskName, function () {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
delete data.supervisor;
|
||||
return data;
|
||||
})
|
||||
)
|
||||
@@ -342,6 +343,62 @@ gulp.task(
|
||||
}
|
||||
);
|
||||
|
||||
gulp.task("build-translation-fragment-supervisor", function () {
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(transform((data) => data.supervisor))
|
||||
.pipe(gulp.dest(workDir + "/supervisor"));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-flatten-supervisor", function () {
|
||||
return gulp
|
||||
.src(workDir + "/supervisor/*.json")
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||
return gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (value.nativeName) {
|
||||
newData[key] = value;
|
||||
} else {
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"build-translations",
|
||||
gulp.series(
|
||||
@@ -353,42 +410,20 @@ gulp.task(
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations",
|
||||
"build-translation-fingerprints",
|
||||
function writeMetadata() {
|
||||
return gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (data[key].nativeName) {
|
||||
newData[key] = data[key];
|
||||
} else {
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
}
|
||||
if (data[key]) newData[key] = value;
|
||||
});
|
||||
return newData;
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir));
|
||||
}
|
||||
"build-translation-write-metadata"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"build-supervisor-translations",
|
||||
gulp.series(
|
||||
"clean-translations",
|
||||
"ensure-translations-build-dir",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
"build-translation-fragment-supervisor",
|
||||
"build-translation-flatten-supervisor",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
)
|
||||
);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
// Tasks to run webpack.
|
||||
const fs = require("fs");
|
||||
const gulp = require("gulp");
|
||||
const webpack = require("webpack");
|
||||
const WebpackDevServer = require("webpack-dev-server");
|
||||
@@ -18,6 +19,13 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
createConfigFunc({ ...params, latestBuild: false }),
|
||||
];
|
||||
|
||||
const isWsl =
|
||||
fs.existsSync("/proc/version") &&
|
||||
fs
|
||||
.readFileSync("/proc/version", "utf-8")
|
||||
.toLocaleLowerCase()
|
||||
.includes("microsoft");
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* compiler: import("webpack").Compiler,
|
||||
@@ -78,8 +86,15 @@ const prodBuild = (conf) =>
|
||||
|
||||
gulp.task("webpack-watch-app", () => {
|
||||
// This command will run forever because we don't close compiler
|
||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
||||
{ ignored: /build-translations/ },
|
||||
webpack(
|
||||
process.env.ES5
|
||||
? bothBuilds(createAppConfig, { isProdBuild: false })
|
||||
: createAppConfig({ isProdBuild: false, latestBuild: true })
|
||||
).watch(
|
||||
{
|
||||
ignored: /build-translations/,
|
||||
poll: isWsl,
|
||||
},
|
||||
doneHandler()
|
||||
);
|
||||
gulp.watch(
|
||||
@@ -137,7 +152,12 @@ gulp.task("webpack-watch-hassio", () => {
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({}, doneHandler());
|
||||
).watch({ ignored: /build-translations/, poll: isWsl }, doneHandler());
|
||||
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task("webpack-prod-hassio", () =>
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
@@ -34,6 +35,7 @@ module.exports = {
|
||||
|
||||
hassio_dir: path.resolve(__dirname, "../hassio"),
|
||||
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
||||
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
|
||||
hassio_output_latest: path.resolve(
|
||||
__dirname,
|
||||
"../hassio/build/frontend_latest"
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
@@ -32,88 +33,77 @@ const createRollupConfig = ({
|
||||
publicPath,
|
||||
dontHash,
|
||||
isWDS,
|
||||
}) => {
|
||||
return {
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}) => ({
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}),
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
"js-yaml": ["safeDump", "safeLoad"],
|
||||
},
|
||||
}),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
exclude: bundle.babelExclude(),
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(
|
||||
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
||||
),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames:
|
||||
isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames:
|
||||
isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
};
|
||||
};
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
});
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
return createRollupConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
|
||||
createRollupConfig(
|
||||
bundle.config.app({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
@@ -121,31 +111,24 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
isWDS,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createRollupConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createRollupConfig(
|
||||
bundle.config.demo({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
};
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle");
|
||||
const bundle = require("./bundle.js");
|
||||
const log = require("fancy-log");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
@@ -46,15 +47,18 @@ const createWebpackConfig = ({
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$|\.ts$/,
|
||||
exclude: bundle.babelExclude(),
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: bundle.babelOptions({ latestBuild }),
|
||||
options: {
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
cacheDirectory: !isProdBuild,
|
||||
cacheCompression: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: "raw-loader",
|
||||
type: "asset/source",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -66,9 +70,11 @@ const createWebpackConfig = ({
|
||||
terserOptions: bundle.terserOptions(latestBuild),
|
||||
}),
|
||||
],
|
||||
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
},
|
||||
plugins: [
|
||||
new ManifestPlugin({
|
||||
new WebpackManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
}),
|
||||
@@ -94,6 +100,7 @@ const createWebpackConfig = ({
|
||||
? path.resolve(context, resource)
|
||||
: require.resolve(resource);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Error in Home Assistant ignore plugin",
|
||||
resource,
|
||||
@@ -111,31 +118,25 @@ const createWebpackConfig = ({
|
||||
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
new RegExp(
|
||||
require.resolve(
|
||||
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
|
||||
)
|
||||
),
|
||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
||||
),
|
||||
!isProdBuild && new LogStartCompilePlugin(),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
alias: {
|
||||
"lit/decorators$": "lit/decorators.js",
|
||||
"lit/directive$": "lit/directive.js",
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) {
|
||||
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) {
|
||||
return `${chunk.name}.js`;
|
||||
}
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js",
|
||||
path: outputPath,
|
||||
publicPath,
|
||||
// To silence warning in worker plugin
|
||||
@@ -144,33 +145,24 @@ const createWebpackConfig = ({
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.hassio({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -139,7 +139,7 @@
|
||||
Your authentication credentials or Home Assistant url are never sent
|
||||
to the Cloud. You can validate this behavior in
|
||||
<a
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
||||
href="https://github.com/home-assistant/frontend/tree/dev/cast"
|
||||
target="_blank"
|
||||
>the source code</a
|
||||
>.
|
||||
|
@@ -1,16 +1,9 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
castSendShowLovelaceView,
|
||||
@@ -32,7 +25,6 @@ import {
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
|
||||
@customElement("hc-cast")
|
||||
class HcCast extends LitElement {
|
||||
@@ -42,9 +34,9 @@ class HcCast extends LitElement {
|
||||
|
||||
@property() public castManager!: CastManager;
|
||||
|
||||
@internalProperty() private askWrite = false;
|
||||
@state() private askWrite = false;
|
||||
|
||||
@internalProperty() private lovelaceConfig?: LovelaceConfig | null;
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
@@ -54,9 +46,7 @@ class HcCast extends LitElement {
|
||||
const error =
|
||||
this.castManager.castState === "NO_DEVICES_AVAILABLE"
|
||||
? html`
|
||||
<p>
|
||||
There were no suitable Chromecast devices to cast to found.
|
||||
</p>
|
||||
<p>There were no suitable Chromecast devices to cast to found.</p>
|
||||
`
|
||||
: undefined;
|
||||
|
||||
@@ -206,7 +196,7 @@ class HcCast extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.center-item {
|
||||
display: flex;
|
||||
|
@@ -11,15 +11,8 @@ import {
|
||||
getAuth,
|
||||
getAuthOptions,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
@@ -60,19 +53,19 @@ const INTRO = html`
|
||||
|
||||
@customElement("hc-connect")
|
||||
export class HcConnect extends LitElement {
|
||||
@internalProperty() private loading = false;
|
||||
@state() private loading = false;
|
||||
|
||||
// If we had stored credentials but we cannot connect,
|
||||
// show a screen asking retry or logout.
|
||||
@internalProperty() private cannotConnect = false;
|
||||
@state() private cannotConnect = false;
|
||||
|
||||
@internalProperty() private error?: string | TemplateResult;
|
||||
@state() private error?: string | TemplateResult;
|
||||
|
||||
@internalProperty() private auth?: Auth;
|
||||
@state() private auth?: Auth;
|
||||
|
||||
@internalProperty() private connection?: Connection;
|
||||
@state() private connection?: Connection;
|
||||
|
||||
@internalProperty() private castManager?: CastManager | null;
|
||||
@state() private castManager?: CastManager | null;
|
||||
|
||||
private openDemo = false;
|
||||
|
||||
@@ -86,9 +79,7 @@ export class HcConnect extends LitElement {
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="/">
|
||||
<mwc-button>
|
||||
Retry
|
||||
</mwc-button>
|
||||
<mwc-button> Retry </mwc-button>
|
||||
</a>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
|
||||
@@ -299,7 +290,7 @@ export class HcConnect extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.card-content a {
|
||||
color: var(--primary-color);
|
||||
|
@@ -4,15 +4,8 @@ import {
|
||||
getUser,
|
||||
HassUser,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
|
||||
@customElement("hc-layout")
|
||||
@@ -69,7 +62,7 @@ class HcLayout extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
@@ -100,7 +93,7 @@ class HcLayout extends LitElement {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.hero {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@ import {
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
const touchSupported = castContext.getDeviceCapabilities()
|
||||
.touch_input_supported;
|
||||
const touchSupported =
|
||||
castContext.getDeviceCapabilities().touch_input_supported;
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
|
@@ -1,10 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import {
|
||||
@@ -21,7 +16,7 @@ import "./hc-lovelace";
|
||||
class HcDemo extends HassElement {
|
||||
@property({ attribute: false }) public lovelacePath!: string;
|
||||
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._lovelaceConfig) {
|
||||
@@ -38,10 +33,10 @@ class HcDemo extends HassElement {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._initialize();
|
||||
this._initializeHass();
|
||||
}
|
||||
|
||||
private async _initialize() {
|
||||
private async _initializeHass() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
|
@@ -1,12 +1,5 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
@customElement("hc-launch-screen")
|
||||
@@ -29,7 +22,7 @@ class HcLaunchScreen extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
|
@@ -1,12 +1,5 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
@@ -35,11 +28,12 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
const lovelace: Lovelace = {
|
||||
config: this.lovelaceConfig,
|
||||
rawConfig: this.lovelaceConfig,
|
||||
editMode: false,
|
||||
urlPath: this.urlPath!,
|
||||
enableFullEditMode: () => undefined,
|
||||
mode: "storage",
|
||||
language: "en",
|
||||
locale: this.hass.locale,
|
||||
saveConfig: async () => undefined,
|
||||
deleteConfig: async () => undefined,
|
||||
setEditMode: () => undefined,
|
||||
@@ -90,10 +84,11 @@ class HcLovelace extends LitElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
min-height: 100vh;
|
||||
height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
|
@@ -3,12 +3,8 @@ import {
|
||||
getAuth,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { CAST_NS } from "../../../../src/cast/const";
|
||||
import {
|
||||
ConnectMessage,
|
||||
@@ -36,13 +32,13 @@ let resourcesLoaded = false;
|
||||
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@internalProperty() private _showDemo = false;
|
||||
@state() private _showDemo = false;
|
||||
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
@internalProperty() private _lovelacePath: string | number | null = null;
|
||||
@state() private _lovelacePath: string | number | null = null;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@state() private _error?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
|
||||
@@ -221,11 +217,17 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig() {
|
||||
const { generateLovelaceConfigFromHass } = await import(
|
||||
"../../../../src/panels/lovelace/common/generate-lovelace-config"
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceConfigFromHass(this.hass!)
|
||||
await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
hass: this.hass!,
|
||||
narrow: false,
|
||||
},
|
||||
"original-states"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -246,11 +246,15 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
|
||||
"light.living_room_lights": {
|
||||
entity_id: "light.living_room_lights",
|
||||
state: "off",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 175,
|
||||
color_temp: 300,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
friendly_name: "Living Room Lights",
|
||||
color_mode: "color_temp",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
@@ -263,13 +267,27 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
"light.kitchen_lights": {
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 200,
|
||||
rgb_color: [255, 175, 96],
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "rgb",
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Garage Lights",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
|
||||
"sensor.plexspy": {
|
||||
entity_id: "sensor.plexspy",
|
||||
state: "0",
|
||||
@@ -482,16 +500,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
icon: "hademo:history",
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
friendly_name: "Garage Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"sensor.alok_to_home": {
|
||||
entity_id: "sensor.alok_to_home",
|
||||
state: "41",
|
||||
|
@@ -29,6 +29,11 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Energy distribution today",
|
||||
type: "energy-distribution",
|
||||
link_dashboard: true,
|
||||
},
|
||||
{
|
||||
type: "thermostat",
|
||||
entity: "climate.upstairs",
|
||||
@@ -113,8 +118,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -196,8 +200,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -277,8 +280,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -315,8 +317,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { energyEntities } from "../stubs/entities";
|
||||
import { DemoConfig } from "./types";
|
||||
|
||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
@@ -12,9 +13,8 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
||||
selectedDemoConfigIndex
|
||||
]();
|
||||
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||
demoConfigs[selectedDemoConfigIndex]();
|
||||
|
||||
export const setDemoConfig = async (
|
||||
hass: MockHomeAssistant,
|
||||
@@ -28,6 +28,7 @@ export const setDemoConfig = async (
|
||||
selectedDemoConfig = confProm;
|
||||
|
||||
hass.addEntities(config.entities(hass.localize), true);
|
||||
hass.addEntities(energyEntities());
|
||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
hass.mockTheme(config.theme());
|
||||
};
|
||||
|
@@ -980,8 +980,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-off",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1005,8 +1004,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-multiple-minus",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1114,6 +1112,9 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
brightness: 63,
|
||||
color_temp: 200,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
friendly_name: "Upstairs lights",
|
||||
supported_features: 63,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1125,6 +1126,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Walk in closet lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1136,6 +1138,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
brightness: 254,
|
||||
friendly_name: "Outdoor lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1148,6 +1152,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
friendly_name: "Downstairs lights",
|
||||
supported_features: 63,
|
||||
@@ -1307,6 +1313,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Bedside Lamp",
|
||||
supported_features: 63,
|
||||
@@ -1320,6 +1327,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp Reading Light",
|
||||
supported_features: 43,
|
||||
@@ -1335,6 +1343,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Hallway window light",
|
||||
@@ -1349,6 +1359,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
brightness: 77,
|
||||
is_deconz_group: false,
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Isa Ceiling Light",
|
||||
supported_features: 41,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1363,6 +1374,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 150,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp",
|
||||
@@ -1377,6 +1390,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Bedroom Ceiling Light",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1387,6 +1401,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Nightlight",
|
||||
supported_features: 17,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:lamp",
|
||||
},
|
||||
@@ -1753,6 +1768,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.2,
|
||||
friendly_name: "Upstairs Hallway Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1768,6 +1784,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Dining Room Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1783,6 +1800,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Living room Spotlights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1799,6 +1817,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.5,
|
||||
friendly_name: "Passage Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1843,6 +1862,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 37.4,
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
|
@@ -440,57 +440,43 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
type: "grid",
|
||||
columns: 2,
|
||||
cards: [
|
||||
{
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
},
|
||||
{
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
},
|
||||
{
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
},
|
||||
],
|
||||
type: "vertical-stack",
|
||||
},
|
||||
{
|
||||
entities: [
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { LitElement } from "lit-element";
|
||||
import { LitElement } from "lit";
|
||||
import "./card-tools";
|
||||
|
||||
class CardModder extends LitElement {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { html, LitElement } from "lit-element";
|
||||
import { html, LitElement } from "lit";
|
||||
|
||||
if (!window.cardTools) {
|
||||
const version = 0.2;
|
||||
|
@@ -1,12 +1,5 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../src/cast/receiver_messages";
|
||||
import "../../../src/components/ha-icon";
|
||||
@@ -20,7 +13,7 @@ import { HomeAssistant } from "../../../src/types";
|
||||
class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _castManager?: CastManager | null;
|
||||
@state() private _castManager?: CastManager | null;
|
||||
|
||||
public setConfig(_config: CastConfig): void {
|
||||
// No config possible.
|
||||
@@ -73,7 +66,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
this.style.display = this._castManager ? "" : "none";
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
|
@@ -1,14 +1,7 @@
|
||||
import "@material/mwc-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { 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";
|
||||
@@ -26,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||
|
||||
@internalProperty() private _switching?: boolean;
|
||||
@state() private _switching = false;
|
||||
|
||||
private _hidden = localStorage.hide_demo_card;
|
||||
|
||||
@@ -34,12 +27,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
return this._hidden ? 0 : 2;
|
||||
}
|
||||
|
||||
public setConfig(
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
config: LovelaceCardConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
) {}
|
||||
public setConfig(_config: LovelaceCardConfig) {}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._hidden) {
|
||||
@@ -113,7 +101,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
a {
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import "@polymer/polymer/lib/elements/dom-if";
|
||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
|
@@ -20,11 +20,15 @@ import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
import { mockEnergy } from "./stubs/energy";
|
||||
import { mockConfig } from "./stubs/config";
|
||||
import { energyEntities } from "./stubs/entities";
|
||||
import { mockForecastSolar } from "./stubs/forecast_solar";
|
||||
|
||||
class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initialize() {
|
||||
protected async _initializeHass() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
panelUrl: (this as any).panelUrl,
|
||||
panelUrl: (this as any)._panelUrl,
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
this._updateHass(hassUpdate),
|
||||
@@ -47,8 +51,13 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
mockEvents(hass);
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockEnergy(hass);
|
||||
mockForecastSolar(hass);
|
||||
mockConfig(hass);
|
||||
mockPersistentNotification(hass);
|
||||
|
||||
hass.addEntities(energyEntities());
|
||||
|
||||
// Once config is loaded AND localize, set entities and apply theme.
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([conf, localize]) => {
|
||||
@@ -70,7 +79,7 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
navigate(this, href);
|
||||
navigate(href);
|
||||
},
|
||||
{ capture: true }
|
||||
);
|
||||
|
41
demo/src/stubs/config.ts
Normal file
41
demo/src/stubs/config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockConfig = (hass: MockHomeAssistant) => {
|
||||
hass.mockAPI("config/config_entries/entry", () => [
|
||||
{
|
||||
entry_id: "co2signal",
|
||||
domain: "co2signal",
|
||||
title: "CO2 Signal",
|
||||
source: "user",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
disabled_by: null,
|
||||
reason: null,
|
||||
},
|
||||
]);
|
||||
hass.mockWS("config/entity_registry/list", () => [
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.co2_intensity",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
]);
|
||||
};
|
83
demo/src/stubs/energy.ts
Normal file
83
demo/src/stubs/energy.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS("energy/get_prefs", () => ({
|
||||
energy_sources: [
|
||||
{
|
||||
type: "grid",
|
||||
flow_from: [
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
flow_to: [
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
cost_adjustment_day: 0,
|
||||
},
|
||||
{
|
||||
type: "solar",
|
||||
stat_energy_from: "sensor.solar_production",
|
||||
config_entry_solar_forecast: ["solar_forecast"],
|
||||
},
|
||||
/* {
|
||||
type: "battery",
|
||||
stat_energy_from: "sensor.battery_output",
|
||||
stat_energy_to: "sensor.battery_input",
|
||||
}, */
|
||||
{
|
||||
type: "gas",
|
||||
stat_energy_from: "sensor.energy_gas",
|
||||
stat_cost: "sensor.energy_gas_cost",
|
||||
entity_energy_from: "sensor.energy_gas",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
device_consumption: [
|
||||
{
|
||||
stat_consumption: "sensor.energy_car",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_ac",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_washing_machine",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_dryer",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_heat_pump",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_boiler",
|
||||
},
|
||||
],
|
||||
}));
|
||||
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
||||
};
|
178
demo/src/stubs/entities.ts
Normal file
178
demo/src/stubs/entities.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
"sensor.solar_production": {
|
||||
entity_id: "sensor.solar_production",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Solar",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_input": {
|
||||
entity_id: "sensor.battery_input",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Input",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_output": {
|
||||
entity_id: "sensor.battery_output",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Output",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1": {
|
||||
entity_id: "sensor.energy_production_tarif_1",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2": {
|
||||
entity_id: "sensor.energy_production_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_1_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_2_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas_cost": {
|
||||
entity_id: "sensor.energy_gas_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas": {
|
||||
entity_id: "sensor.energy_gas",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Gas",
|
||||
unit_of_measurement: "m³",
|
||||
},
|
||||
},
|
||||
"sensor.energy_car": {
|
||||
entity_id: "sensor.energy_car",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Electric car",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_ac": {
|
||||
entity_id: "sensor.energy_ac",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Air conditioning",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_washing_machine": {
|
||||
entity_id: "sensor.energy_washing_machine",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Washing machine",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_dryer": {
|
||||
entity_id: "sensor.energy_dryer",
|
||||
state: "5.5",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Dryer",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_heat_pump": {
|
||||
entity_id: "sensor.energy_heat_pump",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Heat pump",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_boiler": {
|
||||
entity_id: "sensor.energy_boiler",
|
||||
state: "7",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Boiler",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
});
|
55
demo/src/stubs/forecast_solar.ts
Normal file
55
demo/src/stubs/forecast_solar.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||
import { ForecastSolarForecast } from "../../../src/data/forecast_solar";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockForecastSolar = (hass: MockHomeAssistant) => {
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"forecast_solar/forecasts",
|
||||
(): Record<string, ForecastSolarForecast> => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
@@ -1,4 +1,6 @@
|
||||
import { addHours, differenceInHours, endOfDay } from "date-fns";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { StatisticValue } from "../../../src/data/history";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
interface HistoryQueryParams {
|
||||
@@ -64,17 +66,219 @@ const generateHistory = (state, deltas) => {
|
||||
|
||||
const incrementalUnits = ["clients", "queries", "ads"];
|
||||
|
||||
const generateMeanStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let lastVal = initValue;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const delta = Math.random() * maxDiff;
|
||||
const mean = lastVal + delta;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean,
|
||||
min: mean - Math.random() * maxDiff,
|
||||
max: mean + Math.random() * maxDiff,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: mean,
|
||||
sum: null,
|
||||
});
|
||||
lastVal = mean;
|
||||
currentDate = addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const generateSumStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let sum = initValue;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const add = Math.random() * maxDiff;
|
||||
sum += add;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean: null,
|
||||
min: null,
|
||||
max: null,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: initValue + sum,
|
||||
sum,
|
||||
});
|
||||
currentDate = addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const generateCurvedStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number,
|
||||
metered: boolean
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let sum = initValue;
|
||||
const hours = differenceInHours(end, start) - 1;
|
||||
let i = 0;
|
||||
let half = false;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const add = Math.random() * maxDiff;
|
||||
sum += i * add;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean: null,
|
||||
min: null,
|
||||
max: null,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: initValue + sum,
|
||||
sum: metered ? sum : null,
|
||||
});
|
||||
currentDate = addHours(currentDate, 1);
|
||||
if (!half && i > hours / 2) {
|
||||
half = true;
|
||||
}
|
||||
i += half ? -1 : 1;
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const statisticsFunctions: Record<
|
||||
string,
|
||||
(id: string, start: Date, end: Date) => StatisticValue[]
|
||||
> = {
|
||||
"sensor.energy_consumption_tarif_1": (id: string, start: Date, end: Date) => {
|
||||
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
|
||||
const morningLow = generateSumStatistics(id, start, morningEnd, 0, 0.7);
|
||||
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||
const morningFinalVal = morningLow.length
|
||||
? morningLow[morningLow.length - 1].sum!
|
||||
: 0;
|
||||
const empty = generateSumStatistics(
|
||||
id,
|
||||
morningEnd,
|
||||
eveningStart,
|
||||
morningFinalVal,
|
||||
0
|
||||
);
|
||||
const eveningLow = generateSumStatistics(
|
||||
id,
|
||||
eveningStart,
|
||||
end,
|
||||
morningFinalVal,
|
||||
0.7
|
||||
);
|
||||
return [...morningLow, ...empty, ...eveningLow];
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": (id: string, start: Date, end: Date) => {
|
||||
const morningEnd = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||
const highTarif = generateSumStatistics(
|
||||
id,
|
||||
morningEnd,
|
||||
eveningStart,
|
||||
0,
|
||||
0.3
|
||||
);
|
||||
const highTarifFinalVal = highTarif.length
|
||||
? highTarif[highTarif.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, morningEnd, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
eveningStart,
|
||||
end,
|
||||
highTarifFinalVal,
|
||||
0
|
||||
);
|
||||
return [...morning, ...highTarif, ...evening];
|
||||
},
|
||||
"sensor.energy_production_tarif_1": (id, start, end) =>
|
||||
generateSumStatistics(id, start, end, 0, 0),
|
||||
"sensor.energy_production_tarif_1_compensation": (id, start, end) =>
|
||||
generateSumStatistics(id, start, end, 0, 0),
|
||||
"sensor.energy_production_tarif_2": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
productionEnd,
|
||||
0,
|
||||
0.15,
|
||||
true
|
||||
);
|
||||
const productionFinalVal = production.length
|
||||
? production[production.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 1);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.solar_production": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
productionEnd,
|
||||
0,
|
||||
0.3,
|
||||
true
|
||||
);
|
||||
const productionFinalVal = production.length
|
||||
? production[production.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 2);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
|
||||
generateMeanStatistics(id, start, end, 35, 1.3),
|
||||
};
|
||||
|
||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
mockHass.mockAPI(
|
||||
new RegExp("history/period/.+"),
|
||||
(
|
||||
hass,
|
||||
// @ts-ignore
|
||||
method,
|
||||
path,
|
||||
// @ts-ignore
|
||||
parameters
|
||||
) => {
|
||||
(hass, _method, path, _parameters) => {
|
||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||
const entities = params.filter_entity_id.split(",");
|
||||
|
||||
@@ -95,7 +299,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
const numberState = Number(state.state);
|
||||
|
||||
if (isNaN(numberState)) {
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
"Ignoring state with unparsable state but with a unit",
|
||||
entityId,
|
||||
@@ -140,4 +344,40 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
return results;
|
||||
}
|
||||
);
|
||||
mockHass.mockWS("history/list_statistic_ids", () => []);
|
||||
mockHass.mockWS(
|
||||
"history/statistics_during_period",
|
||||
({ statistic_ids, start_time, end_time }, hass) => {
|
||||
const start = new Date(start_time);
|
||||
const end = end_time ? new Date(end_time) : new Date();
|
||||
|
||||
const statistics: Record<string, StatisticValue[]> = {};
|
||||
|
||||
statistic_ids.forEach((id: string) => {
|
||||
if (id in statisticsFunctions) {
|
||||
statistics[id] = statisticsFunctions[id](id, start, end);
|
||||
} else {
|
||||
const entityState = hass.states[id];
|
||||
const state = entityState ? Number(entityState.state) : 1;
|
||||
statistics[id] =
|
||||
entityState && "last_reset" in entityState.attributes
|
||||
? generateSumStatistics(
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
state,
|
||||
state * (state > 80 ? 0.01 : 0.05)
|
||||
)
|
||||
: generateMeanStatistics(
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
state,
|
||||
state * (state > 80 ? 0.05 : 0.1)
|
||||
);
|
||||
}
|
||||
});
|
||||
return statistics;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@@ -10,10 +10,9 @@ export const mockLovelace = (
|
||||
localizePromise: Promise<LocalizeFunc>
|
||||
) => {
|
||||
hass.mockWS("lovelace/config", () =>
|
||||
Promise.all([
|
||||
selectedDemoConfig,
|
||||
localizePromise,
|
||||
]).then(([config, localize]) => config.lovelace(localize))
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([config, localize]) => config.lovelace(localize)
|
||||
)
|
||||
);
|
||||
|
||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||
|
@@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
|
||||
body: { message: "Template dev tool does not work in the demo." },
|
||||
})
|
||||
);
|
||||
hass.mockWS("render_template", (msg, onChange) => {
|
||||
hass.mockWS("render_template", (msg, _hass, onChange) => {
|
||||
onChange!({
|
||||
result: msg.template,
|
||||
listeners: { all: false, domains: [], entities: [], time: false },
|
||||
|
@@ -3,8 +3,6 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
export const mockTranslations = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS(
|
||||
"frontend/get_translations",
|
||||
(/* msg: {language: string, category: string} */) => {
|
||||
return { resources: {} };
|
||||
}
|
||||
(/* msg: {language: string, category: string} */) => ({ resources: {} })
|
||||
);
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { safeLoad } from "js-yaml";
|
||||
import { load } from "js-yaml";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
|
||||
|
||||
class DemoCard extends PolymerElement {
|
||||
@@ -15,6 +15,10 @@ class DemoCard extends PolymerElement {
|
||||
margin: 0 0 20px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
h2 small {
|
||||
font-size: 0.5em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
@@ -34,7 +38,12 @@ class DemoCard extends PolymerElement {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h2>[[config.heading]]</h2>
|
||||
<h2>
|
||||
[[config.heading]]
|
||||
<template is="dom-if" if="[[_size]]">
|
||||
<small>(size [[_size]])</small>
|
||||
</template>
|
||||
</h2>
|
||||
<div class="root">
|
||||
<div id="card"></div>
|
||||
<template is="dom-if" if="[[showConfig]]">
|
||||
@@ -55,6 +64,9 @@ class DemoCard extends PolymerElement {
|
||||
observer: "_configChanged",
|
||||
},
|
||||
showConfig: Boolean,
|
||||
_size: {
|
||||
type: Number,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,8 +80,19 @@ class DemoCard extends PolymerElement {
|
||||
card.removeChild(card.lastChild);
|
||||
}
|
||||
|
||||
const el = this._createCardElement(safeLoad(config.config)[0]);
|
||||
const el = this._createCardElement(load(config.config)[0]);
|
||||
card.appendChild(el);
|
||||
this._getSize(el);
|
||||
}
|
||||
|
||||
async _getSize(el) {
|
||||
await customElements.whenDefined(el.localName);
|
||||
|
||||
if (!("getCardSize" in el)) {
|
||||
this._size = undefined;
|
||||
return;
|
||||
}
|
||||
this._size = await el.getCardSize();
|
||||
}
|
||||
|
||||
_createCardElement(cardConfig) {
|
||||
|
349
gallery/src/data/traces/basic_trace.ts
Normal file
349
gallery/src/data/traces/basic_trace.ts
Normal file
@@ -0,0 +1,349 @@
|
||||
import { DemoTrace } from "./types";
|
||||
|
||||
export const basicTrace: DemoTrace = {
|
||||
trace: {
|
||||
last_step: "action/2",
|
||||
run_id: "0",
|
||||
state: "stopped",
|
||||
timestamp: {
|
||||
start: "2021-03-25T04:36:51.223693+00:00",
|
||||
finish: "2021-03-25T04:36:51.266132+00:00",
|
||||
},
|
||||
trigger: "state of input_boolean.toggle_1",
|
||||
domain: "automation",
|
||||
item_id: "1615419646544",
|
||||
trace: {
|
||||
"trigger/0": [
|
||||
{
|
||||
path: "trigger/0",
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
},
|
||||
],
|
||||
"condition/0": [
|
||||
{
|
||||
path: "condition/0",
|
||||
timestamp: "2021-03-25T04:36:51.228243+00:00",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
from_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-24T19:03:59.141440+00:00",
|
||||
last_updated: "2021-03-24T19:03:59.141440+00:00",
|
||||
context: {
|
||||
id: "5d0918eb379214d07554bdab6a08bcff",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "off",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-25T04:36:51.220696+00:00",
|
||||
last_updated: "2021-03-25T04:36:51.220696+00:00",
|
||||
context: {
|
||||
id: "664d6d261450a9ecea6738e97269a149",
|
||||
parent_id: null,
|
||||
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description: "state of input_boolean.toggle_1",
|
||||
},
|
||||
},
|
||||
result: {
|
||||
result: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/0": [
|
||||
{
|
||||
path: "action/0",
|
||||
timestamp: "2021-03-25T04:36:51.243018+00:00",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
from_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-24T19:03:59.141440+00:00",
|
||||
last_updated: "2021-03-24T19:03:59.141440+00:00",
|
||||
context: {
|
||||
id: "5d0918eb379214d07554bdab6a08bcff",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
state: "off",
|
||||
attributes: {
|
||||
editable: true,
|
||||
friendly_name: "Toggle 1",
|
||||
},
|
||||
last_changed: "2021-03-25T04:36:51.220696+00:00",
|
||||
last_updated: "2021-03-25T04:36:51.220696+00:00",
|
||||
context: {
|
||||
id: "664d6d261450a9ecea6738e97269a149",
|
||||
parent_id: null,
|
||||
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description: "state of input_boolean.toggle_1",
|
||||
},
|
||||
context: {
|
||||
id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||
parent_id: "664d6d261450a9ecea6738e97269a149",
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_4"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1": [
|
||||
{
|
||||
path: "action/1",
|
||||
timestamp: "2021-03-25T04:36:51.252406+00:00",
|
||||
result: {
|
||||
choice: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0": [
|
||||
{
|
||||
path: "action/1/choose/0",
|
||||
timestamp: "2021-03-25T04:36:51.254569+00:00",
|
||||
result: {
|
||||
result: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0/conditions/0": [
|
||||
{
|
||||
path: "action/1/choose/0/conditions/0",
|
||||
timestamp: "2021-03-25T04:36:51.254697+00:00",
|
||||
result: {
|
||||
result: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0/sequence/0": [
|
||||
{
|
||||
path: "action/1/choose/0/sequence/0",
|
||||
timestamp: "2021-03-25T04:36:51.257360+00:00",
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_2"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1/choose/0/sequence/1": [
|
||||
{
|
||||
path: "action/1/choose/0/sequence/1",
|
||||
timestamp: "2021-03-25T04:36:51.260658+00:00",
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_3"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/2": [
|
||||
{
|
||||
path: "action/2",
|
||||
timestamp: "2021-03-25T04:36:51.264159+00:00",
|
||||
result: {
|
||||
params: {
|
||||
domain: "input_boolean",
|
||||
service: "toggle",
|
||||
service_data: {},
|
||||
target: {
|
||||
entity_id: ["input_boolean.toggle_4"],
|
||||
},
|
||||
},
|
||||
running_script: false,
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
config: {
|
||||
id: "1615419646544",
|
||||
alias: "Ensure Party mode",
|
||||
description: "",
|
||||
trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
},
|
||||
],
|
||||
condition: [
|
||||
{
|
||||
condition: "template",
|
||||
alias: "Test if Paulus is home",
|
||||
value_template: "{{ true }}",
|
||||
},
|
||||
],
|
||||
action: [
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
},
|
||||
},
|
||||
{
|
||||
choose: [
|
||||
{
|
||||
alias: "If toggle 3 is on",
|
||||
conditions: [
|
||||
{
|
||||
condition: "template",
|
||||
value_template:
|
||||
"{{ is_state('input_boolean.toggle_3', 'on') }}",
|
||||
},
|
||||
],
|
||||
sequence: [
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
alias: "Toggle 2 while 3 is on",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_2",
|
||||
},
|
||||
},
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
alias: "Toggle 3",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_3",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
default: [
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
alias: "Toggle 2",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_2",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
},
|
||||
},
|
||||
],
|
||||
mode: "single",
|
||||
},
|
||||
context: {
|
||||
id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||
parent_id: "664d6d261450a9ecea6738e97269a149",
|
||||
user_id: null,
|
||||
},
|
||||
script_execution: "finished",
|
||||
},
|
||||
logbookEntries: [
|
||||
{
|
||||
name: "Ensure Party mode",
|
||||
message: "has been triggered by state of input_boolean.toggle_1",
|
||||
source: "state of input_boolean.toggle_1",
|
||||
entity_id: "automation.toggle_toggles",
|
||||
context_id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||
when: "2021-03-25T04:36:51.240832+00:00",
|
||||
domain: "automation",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.249828+00:00",
|
||||
name: "Toggle 4",
|
||||
state: "on",
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.258947+00:00",
|
||||
name: "Toggle 2",
|
||||
state: "on",
|
||||
entity_id: "input_boolean.toggle_2",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.261806+00:00",
|
||||
name: "Toggle 3",
|
||||
state: "off",
|
||||
entity_id: "input_boolean.toggle_3",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
{
|
||||
when: "2021-03-25T04:36:51.265246+00:00",
|
||||
name: "Toggle 4",
|
||||
state: "off",
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
context_entity_id: "automation.toggle_toggles",
|
||||
context_entity_id_name: "Ensure Party mode",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Ensure Party mode",
|
||||
},
|
||||
],
|
||||
};
|
44
gallery/src/data/traces/mock-demo-trace.ts
Normal file
44
gallery/src/data/traces/mock-demo-trace.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { LogbookEntry } from "../../../../src/data/logbook";
|
||||
import { AutomationTraceExtended } from "../../../../src/data/trace";
|
||||
import { DemoTrace } from "./types";
|
||||
|
||||
export const mockDemoTrace = (
|
||||
tracePartial: Partial<AutomationTraceExtended>,
|
||||
logbookEntries?: LogbookEntry[]
|
||||
): DemoTrace => ({
|
||||
trace: {
|
||||
last_step: "",
|
||||
run_id: "0",
|
||||
state: "stopped",
|
||||
timestamp: {
|
||||
start: "2021-03-25T04:36:51.223693+00:00",
|
||||
finish: "2021-03-25T04:36:51.266132+00:00",
|
||||
},
|
||||
trigger: "mocked trigger",
|
||||
domain: "automation",
|
||||
item_id: "1615419646544",
|
||||
trace: {
|
||||
"trigger/0": [
|
||||
{
|
||||
path: "trigger/0",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
description: "mocked trigger",
|
||||
},
|
||||
},
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
config: {
|
||||
trigger: [],
|
||||
action: [],
|
||||
},
|
||||
context: {
|
||||
id: "abcd",
|
||||
},
|
||||
script_execution: "finished",
|
||||
...tracePartial,
|
||||
},
|
||||
logbookEntries: logbookEntries || [],
|
||||
});
|
214
gallery/src/data/traces/motion-light-trace.ts
Normal file
214
gallery/src/data/traces/motion-light-trace.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { DemoTrace } from "./types";
|
||||
|
||||
export const motionLightTrace: DemoTrace = {
|
||||
trace: {
|
||||
last_step: "action/3",
|
||||
run_id: "1",
|
||||
state: "stopped",
|
||||
timestamp: {
|
||||
start: "2021-03-14T06:07:01.768006+00:00",
|
||||
finish: "2021-03-14T06:07:53.287525+00:00",
|
||||
},
|
||||
trigger: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
domain: "automation",
|
||||
item_id: "1614732497392",
|
||||
trace: {
|
||||
"trigger/0": [
|
||||
{
|
||||
path: "trigger/0",
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
},
|
||||
],
|
||||
"action/0": [
|
||||
{
|
||||
path: "action/0",
|
||||
timestamp: "2021-03-14T06:07:01.771038+00:00",
|
||||
changed_variables: {
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera-off",
|
||||
},
|
||||
last_changed: "2021-03-14T06:06:29.235325+00:00",
|
||||
last_updated: "2021-03-14T06:06:29.235325+00:00",
|
||||
context: {
|
||||
id: "ad4864c5ce957c38a07b50378eeb245d",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera",
|
||||
},
|
||||
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||
context: {
|
||||
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description:
|
||||
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
},
|
||||
context: {
|
||||
id: "43b6ee9293a551c5cc14e8eb60af54ba",
|
||||
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/1": [
|
||||
{ path: "action/1", timestamp: "2021-03-14T06:07:01.875316+00:00" },
|
||||
],
|
||||
"action/2": [
|
||||
{
|
||||
path: "action/2",
|
||||
timestamp: "2021-03-14T06:07:53.195013+00:00",
|
||||
changed_variables: {
|
||||
wait: {
|
||||
remaining: null,
|
||||
trigger: {
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera",
|
||||
},
|
||||
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||
context: {
|
||||
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
to_state: {
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||
icon: "mdi:camera-off",
|
||||
},
|
||||
last_changed: "2021-03-14T06:07:53.186755+00:00",
|
||||
last_updated: "2021-03-14T06:07:53.186755+00:00",
|
||||
context: {
|
||||
id: "b2308cc91d509ea8e0c623331ab178d6",
|
||||
parent_id: null,
|
||||
user_id: null,
|
||||
},
|
||||
},
|
||||
for: null,
|
||||
attribute: null,
|
||||
description:
|
||||
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"action/3": [
|
||||
{
|
||||
path: "action/3",
|
||||
timestamp: "2021-03-14T06:07:53.196014+00:00",
|
||||
},
|
||||
],
|
||||
},
|
||||
config: {
|
||||
mode: "restart",
|
||||
max_exceeded: "silent",
|
||||
trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from: "off",
|
||||
to: "on",
|
||||
},
|
||||
],
|
||||
action: [
|
||||
{
|
||||
service: "light.turn_on",
|
||||
target: {
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
},
|
||||
},
|
||||
{
|
||||
wait_for_trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
from: "on",
|
||||
to: "off",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
delay: 0,
|
||||
},
|
||||
{
|
||||
service: "light.turn_off",
|
||||
target: {
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
},
|
||||
},
|
||||
],
|
||||
id: "1614732497392",
|
||||
alias: "Auto Elgato",
|
||||
description: "",
|
||||
},
|
||||
context: {
|
||||
id: "43b6ee9293a551c5cc14e8eb60af54ba",
|
||||
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||
user_id: null,
|
||||
},
|
||||
script_execution: "finished",
|
||||
},
|
||||
logbookEntries: [
|
||||
{
|
||||
name: "Auto Elgato",
|
||||
message:
|
||||
"has been triggered by state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
source: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||
entity_id: "automation.auto_elgato",
|
||||
when: "2021-03-14T06:07:01.768492+00:00",
|
||||
domain: "automation",
|
||||
},
|
||||
{
|
||||
when: "2021-03-14T06:07:01.872187+00:00",
|
||||
name: "Elgato Key Light Air",
|
||||
state: "on",
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
context_entity_id: "automation.auto_elgato",
|
||||
context_entity_id_name: "Auto Elgato",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Auto Elgato",
|
||||
},
|
||||
{
|
||||
when: "2021-03-14T06:07:53.284505+00:00",
|
||||
name: "Elgato Key Light Air",
|
||||
state: "off",
|
||||
entity_id: "light.elgato_key_light_air",
|
||||
context_entity_id: "automation.auto_elgato",
|
||||
context_entity_id_name: "Auto Elgato",
|
||||
context_event_type: "automation_triggered",
|
||||
context_domain: "automation",
|
||||
context_name: "Auto Elgato",
|
||||
},
|
||||
],
|
||||
};
|
7
gallery/src/data/traces/types.ts
Normal file
7
gallery/src/data/traces/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { AutomationTraceExtended } from "../../../../src/data/trace";
|
||||
import { LogbookEntry } from "../../../../src/data/logbook";
|
||||
|
||||
export interface DemoTrace {
|
||||
trace: AutomationTraceExtended;
|
||||
logbookEntries: LogbookEntry[];
|
||||
}
|
96
gallery/src/demos/demo-automation-describe-action.ts
Normal file
96
gallery/src/demos/demo-automation-describe-action.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeAction } from "../../../src/data/script_i18n";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
|
||||
const actions = [
|
||||
{ wait_template: "{{ true }}", alias: "Something with an alias" },
|
||||
{ delay: "0:05" },
|
||||
{ wait_template: "{{ true }}" },
|
||||
{
|
||||
condition: "template",
|
||||
value_template: "{{ true }}",
|
||||
},
|
||||
{ event: "happy_event" },
|
||||
{
|
||||
device_id: "abcdefgh",
|
||||
domain: "plex",
|
||||
entity_id: "media_player.kitchen",
|
||||
},
|
||||
{ scene: "scene.kitchen_morning" },
|
||||
{
|
||||
wait_for_trigger: [
|
||||
{
|
||||
platform: "state",
|
||||
entity_id: "input_boolean.toggle_1",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
variables: {
|
||||
hello: "world",
|
||||
},
|
||||
},
|
||||
{
|
||||
service: "input_boolean.toggle",
|
||||
target: {
|
||||
entity_id: "input_boolean.toggle_4",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-automation-describe-action")
|
||||
export class DemoAutomationDescribeAction extends LitElement {
|
||||
@property({ attribute: false }) hass!: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card header="Actions">
|
||||
${actions.map(
|
||||
(conf) => html`
|
||||
<div class="action">
|
||||
<span>${describeAction(this.hass, conf as any)}</span>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.action {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-describe-action": DemoAutomationDescribeAction;
|
||||
}
|
||||
}
|
60
gallery/src/demos/demo-automation-describe-condition.ts
Normal file
60
gallery/src/demos/demo-automation-describe-condition.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeCondition } from "../../../src/data/automation_i18n";
|
||||
|
||||
const conditions = [
|
||||
{ condition: "and" },
|
||||
{ condition: "not" },
|
||||
{ condition: "or" },
|
||||
{ condition: "state" },
|
||||
{ condition: "numeric_state" },
|
||||
{ condition: "sun", after: "sunset" },
|
||||
{ condition: "sun", after: "sunrise" },
|
||||
{ condition: "zone" },
|
||||
{ condition: "time" },
|
||||
{ condition: "template" },
|
||||
];
|
||||
|
||||
@customElement("demo-automation-describe-condition")
|
||||
export class DemoAutomationDescribeCondition extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="Conditions">
|
||||
${conditions.map(
|
||||
(conf) => html`
|
||||
<div class="condition">
|
||||
<span>${describeCondition(conf as any)}</span>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.condition {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-describe-condition": DemoAutomationDescribeCondition;
|
||||
}
|
||||
}
|
63
gallery/src/demos/demo-automation-describe-trigger.ts
Normal file
63
gallery/src/demos/demo-automation-describe-trigger.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeTrigger } from "../../../src/data/automation_i18n";
|
||||
|
||||
const triggers = [
|
||||
{ platform: "state" },
|
||||
{ platform: "mqtt" },
|
||||
{ platform: "geo_location" },
|
||||
{ platform: "homeassistant" },
|
||||
{ platform: "numeric_state" },
|
||||
{ platform: "sun" },
|
||||
{ platform: "time_pattern" },
|
||||
{ platform: "webhook" },
|
||||
{ platform: "zone" },
|
||||
{ platform: "tag" },
|
||||
{ platform: "time" },
|
||||
{ platform: "template" },
|
||||
{ platform: "event" },
|
||||
];
|
||||
|
||||
@customElement("demo-automation-describe-trigger")
|
||||
export class DemoAutomationDescribeTrigger extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="Triggers">
|
||||
${triggers.map(
|
||||
(conf) => html`
|
||||
<div class="trigger">
|
||||
<span>${describeTrigger(conf as any)}</span>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.trigger {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-describe-trigger": DemoAutomationDescribeTrigger;
|
||||
}
|
||||
}
|
81
gallery/src/demos/demo-automation-trace-timeline.ts
Normal file
81
gallery/src/demos/demo-automation-trace-timeline.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
|
||||
const traces: DemoTrace[] = [
|
||||
mockDemoTrace({ state: "running" }),
|
||||
mockDemoTrace({ state: "debugged" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "failed_conditions" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "failed_single" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "failed_max_runs" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "finished" }),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "aborted" }),
|
||||
mockDemoTrace({
|
||||
state: "stopped",
|
||||
script_execution: "error",
|
||||
error: 'Variable "beer" cannot be None',
|
||||
}),
|
||||
mockDemoTrace({ state: "stopped", script_execution: "cancelled" }),
|
||||
];
|
||||
|
||||
@customElement("demo-automation-trace-timeline")
|
||||
export class DemoAutomationTraceTimeline extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
${traces.map(
|
||||
(trace) => html`
|
||||
<ha-card .header=${trace.trace.config.alias}>
|
||||
<div class="card-content">
|
||||
<hat-trace-timeline
|
||||
.hass=${this.hass}
|
||||
.trace=${trace.trace}
|
||||
.logbookEntries=${trace.logbookEntries}
|
||||
></hat-trace-timeline>
|
||||
<button @click=${() => console.log(trace)}>Log trace</button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-trace-timeline": DemoAutomationTraceTimeline;
|
||||
}
|
||||
}
|
91
gallery/src/demos/demo-automation-trace.ts
Normal file
91
gallery/src/demos/demo-automation-trace.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
import { basicTrace } from "../data/traces/basic_trace";
|
||||
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
||||
|
||||
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||
|
||||
@customElement("demo-automation-trace")
|
||||
export class DemoAutomationTrace extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
@state() private _selected = {};
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
${traces.map(
|
||||
(trace, idx) => html`
|
||||
<ha-card .header=${trace.trace.config.alias}>
|
||||
<div class="card-content">
|
||||
<hat-script-graph
|
||||
.trace=${trace.trace}
|
||||
.selected=${this._selected[idx]}
|
||||
@graph-node-selected=${(ev) => {
|
||||
this._selected = { ...this._selected, [idx]: ev.detail.path };
|
||||
}}
|
||||
></hat-script-graph>
|
||||
<hat-trace-timeline
|
||||
allowPick
|
||||
.hass=${this.hass}
|
||||
.trace=${trace.trace}
|
||||
.logbookEntries=${trace.logbookEntries}
|
||||
.selectedPath=${this._selected[idx]}
|
||||
@value-changed=${(ev) => {
|
||||
this._selected = {
|
||||
...this._selected,
|
||||
[idx]: ev.detail.value,
|
||||
};
|
||||
}}
|
||||
></hat-trace-timeline>
|
||||
<button @click=${() => console.log(trace)}>Log trace</button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
}
|
||||
.card-content > * {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.card-content > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-automation-trace": DemoAutomationTrace;
|
||||
}
|
||||
}
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -93,4 +87,8 @@ class DemoAlarmPanelEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-alarm-panel-card", DemoAlarmPanelEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -75,4 +69,8 @@ class DemoConditional extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-conditional-card", DemoConditional);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-conditional-card": DemoConditional;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -239,4 +233,8 @@ class DemoEntities extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-entities-card", DemoEntities);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-entities-card": DemoEntities;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -91,4 +85,8 @@ class DemoButtonEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-entity-button-card", DemoButtonEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-entity-button-card": DemoButtonEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -132,4 +126,8 @@ class DemoEntityFilter extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-entity-filter-card": DemoEntityFilter;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -129,4 +123,8 @@ class DemoGaugeEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-gauge-card", DemoGaugeEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-gauge-card": DemoGaugeEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -186,7 +180,7 @@ const CONFIGS = [
|
||||
name:
|
||||
- light.kitchen_lights
|
||||
- entity: lock.kitchen_door
|
||||
name:
|
||||
name:
|
||||
- light.ceiling_lights
|
||||
`,
|
||||
},
|
||||
@@ -194,7 +188,7 @@ const CONFIGS = [
|
||||
heading: "Custom tap action",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
columns: 4
|
||||
entities:
|
||||
- entity: lock.kitchen_door
|
||||
name: Custom
|
||||
@@ -232,4 +226,8 @@ class DemoGlanceEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-glance-card": DemoGlanceEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { mockHistory } from "../../../demo/src/stubs/history";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
@@ -49,6 +43,110 @@ const ENTITIES = [
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "Default Grid",
|
||||
config: `
|
||||
- type: grid
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
- type: entity
|
||||
entity: device_tracker.demo_anne_therese
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Non-square Grid with 2 columns",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Default Grid with title",
|
||||
config: `
|
||||
- type: grid
|
||||
title: Kitchen
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
- type: entity
|
||||
entity: device_tracker.demo_anne_therese
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Columns 4",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 4
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Columns 2",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 2
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Columns 1",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 1
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Size for single card",
|
||||
config: `
|
||||
- type: grid
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
heading: "Vertical Stack",
|
||||
config: `
|
||||
@@ -99,45 +197,9 @@ const CONFIGS = [
|
||||
entity: light.bed_light
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Default Grid",
|
||||
config: `
|
||||
- type: grid
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
- type: entity
|
||||
entity: device_tracker.demo_anne_therese
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Non-square Grid with 2 columns",
|
||||
config: `
|
||||
- type: grid
|
||||
columns: 2
|
||||
square: false
|
||||
cards:
|
||||
- type: entity
|
||||
entity: light.kitchen_lights
|
||||
- type: entity
|
||||
entity: light.bed_light
|
||||
- type: entity
|
||||
entity: device_tracker.demo_paulus
|
||||
- type: sensor
|
||||
entity: sensor.illumination
|
||||
graph: line
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-hui-stack-card")
|
||||
@customElement("demo-hui-grid-and-stack-card")
|
||||
class DemoStack extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
@@ -155,4 +217,8 @@ class DemoStack extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-stack-card", DemoStack);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-grid-and-stack-card": DemoStack;
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../components/demo-cards";
|
||||
|
||||
const CONFIGS = [
|
||||
@@ -42,4 +43,8 @@ class DemoIframe extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-iframe-card", DemoIframe);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-iframe-card": DemoIframe;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -85,4 +79,8 @@ class DemoLightEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-light-card", DemoLightEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-light-card": DemoLightEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -183,4 +177,8 @@ class DemoMap extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-map-card", DemoMap);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-map-card": DemoMap;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { mockTemplate } from "../../../demo/src/stubs/template";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -276,4 +270,8 @@ class DemoMarkdown extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-markdown-card", DemoMarkdown);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-markdown-card": DemoMarkdown;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
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 { createMediaPlayerEntities } from "../data/media_players";
|
||||
@@ -180,4 +174,8 @@ class DemoHuiMediaControlCard extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-media-control-card": DemoHuiMediaControlCard;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
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 { createMediaPlayerEntities } from "../data/media_players";
|
||||
@@ -77,4 +71,8 @@ class DemoHuiMediaPlayerRow extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -147,4 +141,8 @@ class DemoPictureElements extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-picture-elements-card": DemoPictureElements;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -102,4 +96,8 @@ class DemoPictureEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-picture-entity-card": DemoPictureEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -143,4 +137,8 @@ class DemoPictureGlance extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-picture-glance-card": DemoPictureGlance;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
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 { createPlantEntities } from "../data/plants";
|
||||
@@ -52,4 +46,8 @@ export class DemoPlantEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-plant-card", DemoPlantEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-plant-card": DemoPlantEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
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";
|
||||
|
||||
@@ -48,4 +42,8 @@ class DemoShoppingListEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-shopping-list-card": DemoShoppingListEntity;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -96,4 +90,8 @@ class DemoThermostatEntity extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-hui-thermostat-card", DemoThermostatEntity);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-hui-thermostat-card": DemoThermostatEntity;
|
||||
}
|
||||
}
|
||||
|
354
gallery/src/demos/demo-integration-card.ts
Normal file
354
gallery/src/demos/demo-integration-card.ts
Normal file
@@ -0,0 +1,354 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { IntegrationManifest } from "../../../src/data/integration";
|
||||
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import "../../../src/panels/config/integrations/ha-integration-card";
|
||||
import "../../../src/panels/config/integrations/ha-ignored-config-entry-card";
|
||||
import "../../../src/panels/config/integrations/ha-config-flow-card";
|
||||
import type {
|
||||
ConfigEntryExtended,
|
||||
DataEntryFlowProgressExtended,
|
||||
} from "../../../src/panels/config/integrations/ha-config-integrations";
|
||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||
|
||||
const createConfigEntry = (
|
||||
title: string,
|
||||
override: Partial<ConfigEntryExtended> = {}
|
||||
): ConfigEntryExtended => ({
|
||||
entry_id: title,
|
||||
domain: "esphome",
|
||||
localized_domain_name: "ESPHome",
|
||||
title,
|
||||
source: "zeroconf",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
disabled_by: null,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
reason: null,
|
||||
...override,
|
||||
});
|
||||
|
||||
const createManifest = (
|
||||
isCustom: boolean,
|
||||
isCloud: boolean,
|
||||
name = "ESPHome"
|
||||
): IntegrationManifest => ({
|
||||
name,
|
||||
domain: "esphome",
|
||||
is_built_in: !isCustom,
|
||||
config_flow: false,
|
||||
documentation: "https://www.home-assistant.io/integrations/esphome/",
|
||||
iot_class: isCloud ? "cloud_polling" : "local_polling",
|
||||
});
|
||||
|
||||
const loadedEntry = createConfigEntry("Loaded");
|
||||
const nameAsDomainEntry = createConfigEntry("ESPHome");
|
||||
const longNameEntry = createConfigEntry(
|
||||
"Entry with a super long name that is going to the next line"
|
||||
);
|
||||
const longNonBreakingNameEntry = createConfigEntry(
|
||||
"EntryWithASuperLongNameThatDoesNotBreak"
|
||||
);
|
||||
const configPanelEntry = createConfigEntry("Config Panel", {
|
||||
domain: "mqtt",
|
||||
localized_domain_name: "MQTT",
|
||||
});
|
||||
const optionsFlowEntry = createConfigEntry("Options Flow", {
|
||||
supports_options: true,
|
||||
});
|
||||
const disabledPollingEntry = createConfigEntry("Disabled Polling", {
|
||||
pref_disable_polling: true,
|
||||
});
|
||||
const setupErrorEntry = createConfigEntry("Setup Error", {
|
||||
state: "setup_error",
|
||||
});
|
||||
const migrationErrorEntry = createConfigEntry("Migration Error", {
|
||||
state: "migration_error",
|
||||
});
|
||||
const setupRetryEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
});
|
||||
const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
reason: "connection_error",
|
||||
});
|
||||
const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", {
|
||||
state: "setup_retry",
|
||||
reason:
|
||||
"HTTPSConnectionpool: Max retries exceeded with NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x9eedfc10>: Failed to establish a new connection: [Errno 113] Host is unreachable')",
|
||||
});
|
||||
const failedUnloadEntry = createConfigEntry("Failed Unload", {
|
||||
state: "failed_unload",
|
||||
});
|
||||
const notLoadedEntry = createConfigEntry("Not Loaded", { state: "not_loaded" });
|
||||
const disabledEntry = createConfigEntry("Disabled", {
|
||||
state: "not_loaded",
|
||||
disabled_by: "user",
|
||||
});
|
||||
const disabledFailedUnloadEntry = createConfigEntry(
|
||||
"Disabled - Failed Unload",
|
||||
{
|
||||
state: "failed_unload",
|
||||
disabled_by: "user",
|
||||
}
|
||||
);
|
||||
|
||||
const configFlows: DataEntryFlowProgressExtended[] = [
|
||||
{
|
||||
flow_id: "adbb401329d8439ebb78ef29837826a8",
|
||||
handler: "roku",
|
||||
context: {
|
||||
source: "ssdp",
|
||||
unique_id: "YF008D862864",
|
||||
title_placeholders: {
|
||||
name: "Living room Roku",
|
||||
},
|
||||
},
|
||||
step_id: "discovery_confirm",
|
||||
localized_title: "Living room Roku",
|
||||
},
|
||||
{
|
||||
flow_id: "adbb401329d8439ebb78ef29837826a8",
|
||||
handler: "hue",
|
||||
context: {
|
||||
source: "reauth",
|
||||
unique_id: "YF008D862864",
|
||||
title_placeholders: {
|
||||
name: "Living room Roku",
|
||||
},
|
||||
},
|
||||
step_id: "discovery_confirm",
|
||||
localized_title: "Philips Hue",
|
||||
},
|
||||
];
|
||||
|
||||
const configEntries: Array<{
|
||||
items: ConfigEntryExtended[];
|
||||
is_custom?: boolean;
|
||||
disabled?: boolean;
|
||||
highlight?: string;
|
||||
}> = [
|
||||
{ items: [loadedEntry] },
|
||||
{ items: [configPanelEntry] },
|
||||
{ items: [optionsFlowEntry] },
|
||||
{ items: [disabledPollingEntry] },
|
||||
{ items: [nameAsDomainEntry] },
|
||||
{ items: [longNameEntry] },
|
||||
{ items: [longNonBreakingNameEntry] },
|
||||
{ items: [setupErrorEntry] },
|
||||
{ items: [migrationErrorEntry] },
|
||||
{ items: [setupRetryEntry] },
|
||||
{ items: [setupRetryReasonEntry] },
|
||||
{ items: [setupRetryReasonMissingKeyEntry] },
|
||||
{ items: [failedUnloadEntry] },
|
||||
{ items: [notLoadedEntry] },
|
||||
{
|
||||
items: [
|
||||
loadedEntry,
|
||||
setupErrorEntry,
|
||||
migrationErrorEntry,
|
||||
longNameEntry,
|
||||
longNonBreakingNameEntry,
|
||||
setupRetryEntry,
|
||||
failedUnloadEntry,
|
||||
notLoadedEntry,
|
||||
disabledEntry,
|
||||
nameAsDomainEntry,
|
||||
configPanelEntry,
|
||||
optionsFlowEntry,
|
||||
],
|
||||
},
|
||||
{ disabled: true, items: [disabledEntry] },
|
||||
{ disabled: true, items: [disabledFailedUnloadEntry] },
|
||||
{
|
||||
disabled: true,
|
||||
items: [disabledEntry, disabledFailedUnloadEntry],
|
||||
},
|
||||
{
|
||||
items: [loadedEntry, configPanelEntry],
|
||||
highlight: "Loaded",
|
||||
},
|
||||
];
|
||||
|
||||
const createEntityRegistryEntries = (
|
||||
item: ConfigEntryExtended
|
||||
): EntityRegistryEntry[] => [
|
||||
{
|
||||
config_entry_id: item.entry_id,
|
||||
device_id: "mock-device-id",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "binary_sensor.updater",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "updater",
|
||||
},
|
||||
];
|
||||
|
||||
const createDeviceRegistryEntries = (
|
||||
item: ConfigEntryExtended
|
||||
): DeviceRegistryEntry[] => [
|
||||
{
|
||||
entry_type: null,
|
||||
config_entries: [item.entry_id],
|
||||
connections: [],
|
||||
manufacturer: "ESPHome",
|
||||
model: "Mock Device",
|
||||
name: "Tag Reader",
|
||||
sw_version: null,
|
||||
id: "mock-device-id",
|
||||
identifiers: [],
|
||||
via_device_id: null,
|
||||
area_id: null,
|
||||
name_by_user: null,
|
||||
disabled_by: null,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-integration-card")
|
||||
export class DemoIntegrationCard extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
@state() isCustomIntegration = false;
|
||||
|
||||
@state() isCloud = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="filters">
|
||||
<ha-formfield label="Custom Integration">
|
||||
<ha-switch @change=${this._toggleCustomIntegration}></ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Relies on cloud">
|
||||
<ha-switch @change=${this._toggleCloud}></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
|
||||
<ha-ignored-config-entry-card
|
||||
.hass=${this.hass}
|
||||
.entry=${createConfigEntry("Ignored Entry")}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
></ha-ignored-config-entry-card>
|
||||
|
||||
${configFlows.map(
|
||||
(flow) => html`
|
||||
<ha-config-flow-card
|
||||
.hass=${this.hass}
|
||||
.flow=${flow}
|
||||
.manifest=${createManifest(
|
||||
this.isCustomIntegration,
|
||||
this.isCloud,
|
||||
flow.handler === "roku" ? "Roku" : "Philips Hue"
|
||||
)}
|
||||
></ha-config-flow-card>
|
||||
`
|
||||
)}
|
||||
${configEntries.map(
|
||||
(info) => html`
|
||||
<ha-integration-card
|
||||
class=${classMap({
|
||||
highlight: info.highlight !== undefined,
|
||||
})}
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${info.items}
|
||||
.manifest=${createManifest(
|
||||
this.isCustomIntegration,
|
||||
this.isCloud
|
||||
)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(
|
||||
info.items[0]
|
||||
)}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(
|
||||
info.items[0]
|
||||
)}
|
||||
?disabled=${info.disabled}
|
||||
.selectedConfigEntryId=${info.highlight}
|
||||
></ha-integration-card>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<div class="container">
|
||||
<!-- One that is standalone to see how it increases height if height
|
||||
not defined by other cards. -->
|
||||
<ha-integration-card
|
||||
.hass=${this.hass}
|
||||
domain="esphome"
|
||||
.items=${[
|
||||
loadedEntry,
|
||||
setupErrorEntry,
|
||||
migrationErrorEntry,
|
||||
setupRetryEntry,
|
||||
failedUnloadEntry,
|
||||
]}
|
||||
.manifest=${createManifest(this.isCustomIntegration, this.isCloud)}
|
||||
.entityRegistryEntries=${createEntityRegistryEntries(loadedEntry)}
|
||||
.deviceRegistryEntries=${createDeviceRegistryEntries(loadedEntry)}
|
||||
></ha-integration-card>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
// Normally this string is loaded from backend
|
||||
hass.addTranslations(
|
||||
{
|
||||
"component.esphome.config.error.connection_error":
|
||||
"Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
|
||||
},
|
||||
"en"
|
||||
);
|
||||
}
|
||||
|
||||
private _toggleCustomIntegration() {
|
||||
this.isCustomIntegration = !this.isCustomIntegration;
|
||||
}
|
||||
|
||||
private _toggleCloud() {
|
||||
this.isCloud = !this.isCloud;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-gap: 16px 16px;
|
||||
padding: 8px 16px 16px;
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
|
||||
.container > * {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
ha-formfield {
|
||||
margin: 8px 0;
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-integration-card": DemoIntegrationCard;
|
||||
}
|
||||
}
|
@@ -1,21 +1,11 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import {
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
LightColorModes,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_FLASH,
|
||||
SUPPORT_TRANSITION,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../src/data/light";
|
||||
import "../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
@@ -32,7 +22,8 @@ const ENTITIES = [
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_features: SUPPORT_BRIGHTNESS,
|
||||
supported_color_modes: [LightColorModes.BRIGHTNESS],
|
||||
color_mode: LightColorModes.BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
@@ -40,20 +31,96 @@ const ENTITIES = [
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorModes.COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_effectslight", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
getEntity("light", "color_hs_light", "on", {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
white_value: 36,
|
||||
supported_features:
|
||||
SUPPORT_BRIGHTNESS +
|
||||
SUPPORT_EFFECT +
|
||||
SUPPORT_FLASH +
|
||||
SUPPORT_COLOR +
|
||||
SUPPORT_TRANSITION +
|
||||
SUPPORT_WHITE_VALUE,
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.HS,
|
||||
],
|
||||
color_mode: LightColorModes.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgb_ct_light", "on", {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGB,
|
||||
],
|
||||
color_mode: LightColorModes.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_RGB_light", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
|
||||
color_mode: LightColorModes.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbw_light", "on", {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGBW,
|
||||
],
|
||||
color_mode: LightColorModes.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbww_light", "on", {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.RGBWW,
|
||||
],
|
||||
color_mode: LightColorModes.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_xy_light", "on", {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorModes.BRIGHTNESS,
|
||||
LightColorModes.COLOR_TEMP,
|
||||
LightColorModes.XY,
|
||||
],
|
||||
color_mode: LightColorModes.XY,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
];
|
||||
@@ -81,4 +148,8 @@ class DemoMoreInfoLight extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-more-info-light", DemoMoreInfoLight);
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-more-info-light": DemoMoreInfoLight;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import "@material/mwc-button";
|
||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
||||
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||
|
@@ -111,29 +111,9 @@ class HaGallery extends PolymerElement {
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="More Info Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
More info screens show up when an entity is clicked.
|
||||
</p>
|
||||
</div>
|
||||
<template is="dom-repeat" items="[[_moreInfoDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
</paper-item>
|
||||
</a>
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Util Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
Test pages for our utility functions.
|
||||
</p>
|
||||
</div>
|
||||
<template is="dom-repeat" items="[[_utilDemos]]">
|
||||
<ha-card header="Other Demos">
|
||||
<div class="card-content intro"></div>
|
||||
<template is="dom-repeat" items="[[_restDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
@@ -178,13 +158,9 @@ class HaGallery extends PolymerElement {
|
||||
type: Array,
|
||||
computed: "_computeLovelace(_demos)",
|
||||
},
|
||||
_moreInfoDemos: {
|
||||
_restDemos: {
|
||||
type: Array,
|
||||
computed: "_computeMoreInfos(_demos)",
|
||||
},
|
||||
_utilDemos: {
|
||||
type: Array,
|
||||
computed: "_computeUtil(_demos)",
|
||||
computed: "_computeRest(_demos)",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -237,12 +213,8 @@ class HaGallery extends PolymerElement {
|
||||
return demos.filter((demo) => demo.includes("hui"));
|
||||
}
|
||||
|
||||
_computeMoreInfos(demos) {
|
||||
return demos.filter((demo) => demo.includes("more-info"));
|
||||
}
|
||||
|
||||
_computeUtil(demos) {
|
||||
return demos.filter((demo) => demo.includes("util"));
|
||||
_computeRest(demos) {
|
||||
return demos.filter((demo) => !demo.includes("hui"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,12 +1,6 @@
|
||||
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
@@ -15,6 +9,7 @@ import {
|
||||
HassioAddonInfo,
|
||||
HassioAddonRepository,
|
||||
} from "../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import "../components/hassio-card-content";
|
||||
import { filterAndSort } from "../components/hassio-filter-addons";
|
||||
@@ -23,6 +18,8 @@ import { hassioStyle } from "../resources/hassio-style";
|
||||
class HassioAddonRepositoryEl extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
||||
|
||||
@property({ attribute: false }) public addons!: HassioAddonInfo[];
|
||||
@@ -44,9 +41,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
const repo = this.repo;
|
||||
let _addons = this.addons;
|
||||
if (!this.hass.userData?.showAdvanced) {
|
||||
_addons = _addons.filter((addon) => {
|
||||
return !addon.advanced;
|
||||
});
|
||||
_addons = _addons.filter((addon) => !addon.advanced);
|
||||
}
|
||||
const addons = this._getAddons(_addons, this.filter);
|
||||
|
||||
@@ -54,16 +49,18 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p class="description">
|
||||
No results found in "${repo.name}."
|
||||
${this.supervisor.localize(
|
||||
"store.no_results_found",
|
||||
"repository",
|
||||
repo.name
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html`
|
||||
<div class="content">
|
||||
<h1>
|
||||
${repo.name}
|
||||
</h1>
|
||||
<h1>${repo.name}</h1>
|
||||
<div class="card-group">
|
||||
${addons.map(
|
||||
(addon) => html`
|
||||
@@ -83,11 +80,13 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
: mdiPuzzle}
|
||||
.iconTitle=${addon.installed
|
||||
? addon.update_available
|
||||
? "New version available"
|
||||
: "Add-on is installed"
|
||||
? this.supervisor.localize(
|
||||
"common.new_version_available"
|
||||
)
|
||||
: this.supervisor.localize("addon.installed")
|
||||
: addon.available
|
||||
? "Add-on is not installed"
|
||||
: "Add-on is not available on your system"}
|
||||
? this.supervisor.localize("addon.not_installed")
|
||||
: this.supervisor.localize("addon.not_available")}
|
||||
.iconClass=${addon.installed
|
||||
? addon.update_available
|
||||
? "update"
|
||||
@@ -121,10 +120,10 @@ class HassioAddonRepositoryEl extends LitElement {
|
||||
}
|
||||
|
||||
private _addonTapped(ev) {
|
||||
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);
|
||||
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}`);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
hassioStyle,
|
||||
css`
|
||||
|
@@ -4,17 +4,19 @@ import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
internalProperty,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
import "../../../src/common/search/search-input";
|
||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||
import "../../../src/components/ha-button-menu";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
@@ -56,7 +58,7 @@ class HassioAddonStore extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@internalProperty() private _filter?: string;
|
||||
@state() private _filter?: string;
|
||||
|
||||
public async refreshData() {
|
||||
await reloadHassioAddons(this.hass);
|
||||
@@ -69,20 +71,22 @@ class HassioAddonStore extends LitElement {
|
||||
if (this.supervisor.addon.repositories) {
|
||||
repos = this.addonRepositories(
|
||||
this.supervisor.addon.repositories,
|
||||
this.supervisor.addon.addons
|
||||
this.supervisor.addon.addons,
|
||||
this._filter
|
||||
);
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.localizeFunc=${this.supervisor.localize}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
hassio
|
||||
main-page
|
||||
.tabs=${supervisorTabs}
|
||||
main-page
|
||||
supervisor
|
||||
>
|
||||
<span slot="header">Add-on Store</span>
|
||||
<span slot="header"> ${this.supervisor.localize("panel.store")} </span>
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
slot="toolbar-icon"
|
||||
@@ -92,15 +96,15 @@ class HassioAddonStore extends LitElement {
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item>
|
||||
Repositories
|
||||
${this.supervisor.localize("store.repositories")}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item>
|
||||
Reload
|
||||
${this.supervisor.localize("common.reload")}
|
||||
</mwc-list-item>
|
||||
${this.hass.userData?.showAdvanced &&
|
||||
atLeastVersion(this.hass.config.version, 0, 117)
|
||||
? html`<mwc-list-item>
|
||||
Registries
|
||||
${this.supervisor.localize("store.registries")}
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
</ha-button-menu>
|
||||
@@ -121,11 +125,9 @@ class HassioAddonStore extends LitElement {
|
||||
${!this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<div class="advanced">
|
||||
Missing add-ons? Enable advanced mode on
|
||||
<a href="/profile" target="_top">
|
||||
your profile page
|
||||
${this.supervisor.localize("store.missing_addons")}
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
@@ -135,13 +137,23 @@ class HassioAddonStore extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
const repositoryUrl = extractSearchParam("repository_url");
|
||||
navigate("/hassio/store", { replace: true });
|
||||
if (repositoryUrl) {
|
||||
this._manageRepositories(repositoryUrl);
|
||||
}
|
||||
|
||||
this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev));
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
private addonRepositories = memoizeOne(
|
||||
(repositories: HassioAddonRepository[], addons: HassioAddonInfo[]) => {
|
||||
return repositories.sort(sortRepos).map((repo) => {
|
||||
(
|
||||
repositories: HassioAddonRepository[],
|
||||
addons: HassioAddonInfo[],
|
||||
filter?: string
|
||||
) =>
|
||||
repositories.sort(sortRepos).map((repo) => {
|
||||
const filteredAddons = addons.filter(
|
||||
(addon) => addon.repository === repo.slug
|
||||
);
|
||||
@@ -152,18 +164,18 @@ class HassioAddonStore extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.repo=${repo}
|
||||
.addons=${filteredAddons}
|
||||
.filter=${this._filter!}
|
||||
.filter=${filter!}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-repository>
|
||||
`
|
||||
: html``;
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._manageRepositories();
|
||||
this._manageRepositoriesClicked();
|
||||
break;
|
||||
case 1:
|
||||
this.refreshData();
|
||||
@@ -180,27 +192,33 @@ class HassioAddonStore extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _manageRepositories() {
|
||||
private _manageRepositoriesClicked() {
|
||||
this._manageRepositories();
|
||||
}
|
||||
|
||||
private async _manageRepositories(url?: string) {
|
||||
showRepositoriesDialog(this, {
|
||||
repos: this.supervisor.addon.repositories,
|
||||
loadData: () => this._loadData(),
|
||||
supervisor: this.supervisor,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
private async _manageRegistries() {
|
||||
showRegistriesDialog(this);
|
||||
showRegistriesDialog(this, { supervisor: this.supervisor });
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
fireEvent(this, "supervisor-store-refresh", { store: "addon" });
|
||||
fireEvent(this, "supervisor-store-refresh", { store: "supervisor" });
|
||||
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: "supervisor",
|
||||
});
|
||||
}
|
||||
|
||||
private async _filterChanged(e) {
|
||||
this._filter = e.detail.value;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
hassio-addon-repository {
|
||||
margin-top: 24px;
|
||||
|
@@ -4,15 +4,13 @@ import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-card";
|
||||
@@ -25,6 +23,7 @@ import {
|
||||
fetchHassioHardwareAudio,
|
||||
HassioHardwareAudioDevice,
|
||||
} from "../../../../src/data/hassio/hardware";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||
@@ -34,26 +33,32 @@ import { hassioStyle } from "../../resources/hassio-style";
|
||||
class HassioAddonAudio extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@state() private _error?: string;
|
||||
|
||||
@internalProperty() private _inputDevices?: HassioHardwareAudioDevice[];
|
||||
@state() private _inputDevices?: HassioHardwareAudioDevice[];
|
||||
|
||||
@internalProperty() private _outputDevices?: HassioHardwareAudioDevice[];
|
||||
@state() private _outputDevices?: HassioHardwareAudioDevice[];
|
||||
|
||||
@internalProperty() private _selectedInput!: null | string;
|
||||
@state() private _selectedInput!: null | string;
|
||||
|
||||
@internalProperty() private _selectedOutput!: null | string;
|
||||
@state() private _selectedOutput!: null | string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="Audio">
|
||||
<ha-card
|
||||
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
||||
>
|
||||
<div class="card-content">
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
|
||||
<paper-dropdown-menu
|
||||
label="Input"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.configuration.audio.input"
|
||||
)}
|
||||
@iron-select=${this._setInputDevice}
|
||||
>
|
||||
<paper-listbox
|
||||
@@ -62,17 +67,19 @@ class HassioAddonAudio extends LitElement {
|
||||
.selected=${this._selectedInput!}
|
||||
>
|
||||
${this._inputDevices &&
|
||||
this._inputDevices.map((item) => {
|
||||
return html`
|
||||
<paper-item device=${item.device || ""}
|
||||
>${item.name}</paper-item
|
||||
>
|
||||
`;
|
||||
})}
|
||||
this._inputDevices.map(
|
||||
(item) => html`
|
||||
<paper-item device=${item.device || ""}>
|
||||
${item.name}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
<paper-dropdown-menu
|
||||
label="Output"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.configuration.audio.output"
|
||||
)}
|
||||
@iron-select=${this._setOutputDevice}
|
||||
>
|
||||
<paper-listbox
|
||||
@@ -81,26 +88,26 @@ class HassioAddonAudio extends LitElement {
|
||||
.selected=${this._selectedOutput!}
|
||||
>
|
||||
${this._outputDevices &&
|
||||
this._outputDevices.map((item) => {
|
||||
return html`
|
||||
this._outputDevices.map(
|
||||
(item) => html`
|
||||
<paper-item device=${item.device || ""}
|
||||
>${item.name}</paper-item
|
||||
>
|
||||
`;
|
||||
})}
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button @click=${this._saveSettings}>
|
||||
Save
|
||||
${this.supervisor.localize("common.save")}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
@@ -152,7 +159,7 @@ class HassioAddonAudio extends LitElement {
|
||||
|
||||
const noDevice: HassioHardwareAudioDevice = {
|
||||
device: "default",
|
||||
name: "Default",
|
||||
name: this.supervisor.localize("addon.configuration.audio.default"),
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -189,7 +196,7 @@ class HassioAddonAudio extends LitElement {
|
||||
try {
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.addon);
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch {
|
||||
this._error = "Failed to set addon audio device";
|
||||
|
@@ -1,14 +1,8 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
@@ -20,26 +14,28 @@ import "./hassio-addon-network";
|
||||
class HassioAddonConfigDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
}
|
||||
const hasOptions =
|
||||
this.addon.options && Object.keys(this.addon.options).length;
|
||||
const hasSchema =
|
||||
hasOptions && this.addon.schema && Object.keys(this.addon.schema).length;
|
||||
const hasConfiguration =
|
||||
(this.addon.options && Object.keys(this.addon.options).length) ||
|
||||
(this.addon.schema && Object.keys(this.addon.schema).length);
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
${hasOptions || hasSchema || this.addon.network || this.addon.audio
|
||||
${hasConfiguration || this.addon.network || this.addon.audio
|
||||
? html`
|
||||
${hasOptions || hasSchema
|
||||
${hasConfiguration
|
||||
? html`
|
||||
<hassio-addon-config
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-config>
|
||||
`
|
||||
: ""}
|
||||
@@ -48,6 +44,7 @@ class HassioAddonConfigDashboard extends LitElement {
|
||||
<hassio-addon-network
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-network>
|
||||
`
|
||||
: ""}
|
||||
@@ -56,16 +53,17 @@ class HassioAddonConfigDashboard extends LitElement {
|
||||
<hassio-addon-audio
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addon-audio>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: "This add-on does not expose configuration for you to mess with.... 👋"}
|
||||
: this.supervisor.localize("addon.configuration.no_configuration")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
|
@@ -2,32 +2,35 @@ import "@material/mwc-button";
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
||||
import { DEFAULT_SCHEMA, Type } from "js-yaml";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-button-menu";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-switch";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
HassioAddonSetOptionParams,
|
||||
setHassioAddonOption,
|
||||
validateHassioAddonOption,
|
||||
} from "../../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
@@ -36,70 +39,135 @@ import { hassioStyle } from "../../resources/hassio-style";
|
||||
|
||||
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
|
||||
|
||||
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
|
||||
new Type("!secret", {
|
||||
kind: "scalar",
|
||||
construct: (data) => `!secret ${data}`,
|
||||
}),
|
||||
]);
|
||||
|
||||
@customElement("hassio-addon-config")
|
||||
class HassioAddonConfig extends LitElement {
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ type: Boolean }) private _configHasChanged = false;
|
||||
|
||||
@property({ type: Boolean }) private _valid = true;
|
||||
|
||||
@internalProperty() private _canShowSchema = false;
|
||||
@state() private _canShowSchema = false;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@state() private _showOptional = false;
|
||||
|
||||
@internalProperty() private _options?: Record<string, unknown>;
|
||||
@state() private _error?: string;
|
||||
|
||||
@internalProperty() private _yamlMode = false;
|
||||
@state() private _options?: Record<string, unknown>;
|
||||
|
||||
@state() private _yamlMode = false;
|
||||
|
||||
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
||||
|
||||
public computeLabel = (entry: HaFormSchema): string =>
|
||||
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||
?.name ||
|
||||
this.addon.translations.en?.configuration?.[entry.name].name ||
|
||||
entry.name;
|
||||
|
||||
private _filteredShchema = memoizeOne(
|
||||
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
|
||||
schema.filter((entry) => entry.name in options || entry.required)
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const showForm =
|
||||
!this._yamlMode && this._canShowSchema && this.addon.schema;
|
||||
const hasHiddenOptions =
|
||||
showForm &&
|
||||
JSON.stringify(this.addon.schema) !==
|
||||
JSON.stringify(
|
||||
this._filteredShchema(this.addon.options, this.addon.schema!)
|
||||
);
|
||||
return html`
|
||||
<h1>${this.addon.name}</h1>
|
||||
<ha-card>
|
||||
<div class="header">
|
||||
<h2>Configuration</h2>
|
||||
<h2>
|
||||
${this.supervisor.localize("addon.configuration.options.header")}
|
||||
</h2>
|
||||
<div class="card-menu">
|
||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||
<mwc-icon-button slot="trigger">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item .disabled=${!this._canShowSchema}>
|
||||
${this._yamlMode ? "Edit in UI" : "Edit in YAML"}
|
||||
${this._yamlMode
|
||||
? this.supervisor.localize(
|
||||
"addon.configuration.options.edit_in_ui"
|
||||
)
|
||||
: this.supervisor.localize(
|
||||
"addon.configuration.options.edit_in_yaml"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning">
|
||||
Reset to defaults
|
||||
${this.supervisor.localize("common.reset_defaults")}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
${!this._yamlMode && this._canShowSchema && this.addon.schema
|
||||
${showForm
|
||||
? html`<ha-form
|
||||
.data=${this._options!}
|
||||
@value-changed=${this._configChanged}
|
||||
.schema=${this.addon.schema}
|
||||
.computeLabel=${this.computeLabel}
|
||||
.schema=${this._showOptional
|
||||
? this.addon.schema!
|
||||
: this._filteredShchema(
|
||||
this.addon.options,
|
||||
this.addon.schema!
|
||||
)}
|
||||
></ha-form>`
|
||||
: html` <ha-yaml-editor
|
||||
@value-changed=${this._configChanged}
|
||||
.yamlSchema=${ADDON_YAML_SCHEMA}
|
||||
></ha-yaml-editor>`}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
${!this._yamlMode ||
|
||||
(this._canShowSchema && this.addon.schema) ||
|
||||
this._valid
|
||||
? ""
|
||||
: html` <div class="errors">Invalid YAML</div> `}
|
||||
: html`
|
||||
<div class="errors">
|
||||
${this.supervisor.localize(
|
||||
"addon.configuration.options.invalid_yaml"
|
||||
)}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
${hasHiddenOptions
|
||||
? html`<ha-formfield
|
||||
class="show-additional"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.configuration.options.show_unused_optional"
|
||||
)}
|
||||
>
|
||||
<ha-switch
|
||||
@change=${this._toggleOptional}
|
||||
.checked=${this._showOptional}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>`
|
||||
: ""}
|
||||
<div class="card-actions right">
|
||||
<ha-progress-button
|
||||
@click=${this._saveTapped}
|
||||
.disabled=${!this._configHasChanged || !this._valid}
|
||||
>
|
||||
Save
|
||||
${this.supervisor.localize("common.save")}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
@@ -108,12 +176,10 @@ class HassioAddonConfig extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._canShowSchema =
|
||||
Object.keys(this.addon.options).length !== 0 &&
|
||||
!this.addon.schema!.find(
|
||||
// @ts-ignore
|
||||
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
|
||||
);
|
||||
this._canShowSchema = !this.addon.schema!.find(
|
||||
// @ts-ignore
|
||||
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
|
||||
);
|
||||
this._yamlMode = !this._canShowSchema;
|
||||
}
|
||||
|
||||
@@ -146,6 +212,10 @@ class HassioAddonConfig extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleOptional() {
|
||||
this._showOptional = !this._showOptional;
|
||||
}
|
||||
|
||||
private _configChanged(ev): void {
|
||||
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
|
||||
this._valid = true;
|
||||
@@ -162,10 +232,10 @@ class HassioAddonConfig extends LitElement {
|
||||
button.progress = true;
|
||||
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: this.addon.name,
|
||||
text: "Are you sure you want to reset all your options?",
|
||||
confirmText: "reset options",
|
||||
dismissText: "no",
|
||||
title: this.supervisor.localize("confirm.reset_options.title"),
|
||||
text: this.supervisor.localize("confirm.reset_options.text"),
|
||||
confirmText: this.supervisor.localize("common.reset_options"),
|
||||
dismissText: this.supervisor.localize("common.cancel"),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
@@ -187,43 +257,59 @@ class HassioAddonConfig extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err) {
|
||||
this._error = `Failed to reset addon configuration, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.common.update_available",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
}
|
||||
button.progress = false;
|
||||
}
|
||||
|
||||
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
const options: Record<string, unknown> = this._yamlMode
|
||||
? this._editor?.value
|
||||
: this._options;
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "options",
|
||||
};
|
||||
button.progress = true;
|
||||
|
||||
this._error = undefined;
|
||||
|
||||
try {
|
||||
const validation = await validateHassioAddonOption(
|
||||
this.hass,
|
||||
this.addon.slug,
|
||||
options
|
||||
);
|
||||
if (!validation.valid) {
|
||||
throw Error(validation.message);
|
||||
}
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, {
|
||||
options: this._yamlMode ? this._editor?.value : this._options,
|
||||
options,
|
||||
});
|
||||
|
||||
this._configHasChanged = false;
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "options",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.addon);
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err) {
|
||||
this._error = `Failed to save addon configuration, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
eventdata.success = false;
|
||||
}
|
||||
button.progress = false;
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
@@ -242,10 +328,6 @@ class HassioAddonConfig extends LitElement {
|
||||
color: var(--error-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
iron-autogrow-textarea {
|
||||
width: 100%;
|
||||
font-family: var(--code-font-family, monospace);
|
||||
}
|
||||
.syntaxerror {
|
||||
color: var(--error-color);
|
||||
}
|
||||
@@ -275,6 +357,10 @@ class HassioAddonConfig extends LitElement {
|
||||
.card-actions.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.show-additional {
|
||||
padding: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -1,15 +1,13 @@
|
||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-card";
|
||||
@@ -19,6 +17,7 @@ import {
|
||||
setHassioAddonOption,
|
||||
} from "../../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||
@@ -38,11 +37,13 @@ interface NetworkItemInput extends PaperInputElement {
|
||||
class HassioAddonNetwork extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@state() private _error?: string;
|
||||
|
||||
@internalProperty() private _config?: NetworkItem[];
|
||||
@state() private _config?: NetworkItem[];
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
@@ -55,43 +56,57 @@ class HassioAddonNetwork extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card header="Network">
|
||||
<ha-card
|
||||
.header=${this.supervisor.localize(
|
||||
"addon.configuration.network.header"
|
||||
)}
|
||||
>
|
||||
<div class="card-content">
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Container</th>
|
||||
<th>Host</th>
|
||||
<th>Description</th>
|
||||
<th>
|
||||
${this.supervisor.localize(
|
||||
"addon.configuration.network.container"
|
||||
)}
|
||||
</th>
|
||||
<th>
|
||||
${this.supervisor.localize(
|
||||
"addon.configuration.network.host"
|
||||
)}
|
||||
</th>
|
||||
<th>${this.supervisor.localize("common.description")}</th>
|
||||
</tr>
|
||||
${this._config!.map((item) => {
|
||||
return html`
|
||||
${this._config!.map(
|
||||
(item) => html`
|
||||
<tr>
|
||||
<td>${item.container}</td>
|
||||
<td>
|
||||
<paper-input
|
||||
@value-changed=${this._configChanged}
|
||||
placeholder="disabled"
|
||||
placeholder="${this.supervisor.localize(
|
||||
"addon.configuration.network.disabled"
|
||||
)}"
|
||||
.value=${item.host ? String(item.host) : ""}
|
||||
.container=${item.container}
|
||||
no-label-float
|
||||
></paper-input>
|
||||
</td>
|
||||
<td>${item.description}</td>
|
||||
<td>${this._computeDescription(item)}</td>
|
||||
</tr>
|
||||
`;
|
||||
})}
|
||||
`
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
||||
Reset to defaults
|
||||
${this.supervisor.localize("common.reset_defaults")}
|
||||
</ha-progress-button>
|
||||
<ha-progress-button @click=${this._saveTapped}>
|
||||
Save
|
||||
${this.supervisor.localize("common.save")}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
@@ -105,16 +120,20 @@ class HassioAddonNetwork extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _computeDescription = (item: NetworkItem): string =>
|
||||
this.addon.translations[this.hass.language]?.network?.[item.container]
|
||||
?.description ||
|
||||
this.addon.translations.en?.network?.[item.container]?.description ||
|
||||
item.description;
|
||||
|
||||
private _setNetworkConfig(): void {
|
||||
const network = this.addon.network || {};
|
||||
const description = this.addon.network_description || {};
|
||||
const items: NetworkItem[] = Object.keys(network).map((key) => {
|
||||
return {
|
||||
container: key,
|
||||
host: network[key],
|
||||
description: description[key],
|
||||
};
|
||||
});
|
||||
const items: NetworkItem[] = Object.keys(network).map((key) => ({
|
||||
container: key,
|
||||
host: network[key],
|
||||
description: description[key],
|
||||
}));
|
||||
this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1));
|
||||
}
|
||||
|
||||
@@ -147,12 +166,14 @@ class HassioAddonNetwork extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.addon);
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err) {
|
||||
this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_reset",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
}
|
||||
|
||||
button.progress = false;
|
||||
@@ -181,17 +202,19 @@ class HassioAddonNetwork extends LitElement {
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.addon);
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err) {
|
||||
this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
}
|
||||
button.progress = false;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
|
@@ -1,15 +1,8 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
fetchHassioAddonDocumentation,
|
||||
HassioAddonDetails,
|
||||
@@ -19,16 +12,19 @@ import "../../../../src/layouts/hass-loading-screen";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
|
||||
@customElement("hassio-addon-documentation-tab")
|
||||
class HassioAddonDocumentationDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@state() private _error?: string;
|
||||
|
||||
@internalProperty() private _content?: string;
|
||||
@state() private _content?: string;
|
||||
|
||||
public async connectedCallback(): Promise<void> {
|
||||
super.connectedCallback();
|
||||
@@ -53,7 +49,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
@@ -81,9 +77,11 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
||||
this.addon!.slug
|
||||
);
|
||||
} catch (err) {
|
||||
this._error = `Failed to get addon documentation, ${extractApiErrorMessage(
|
||||
err
|
||||
)}`;
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.documentation.get_logs",
|
||||
"error",
|
||||
extractApiErrorMessage(err)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,16 +4,8 @@ import {
|
||||
mdiInformationVariant,
|
||||
mdiMathLog,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
@@ -21,6 +13,7 @@ import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import {
|
||||
fetchHassioAddonInfo,
|
||||
fetchHassioAddonsInfo,
|
||||
HassioAddonDetails,
|
||||
} from "../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
@@ -51,7 +44,7 @@ class HassioAddonDashboard extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@internalProperty() _error?: string;
|
||||
@state() _error?: string;
|
||||
|
||||
private _computeTail = memoizeOne((route: Route) => {
|
||||
const dividerPos = route.path.indexOf("/", 1);
|
||||
@@ -79,7 +72,7 @@ class HassioAddonDashboard extends LitElement {
|
||||
|
||||
const addonTabs: PageNavigation[] = [
|
||||
{
|
||||
name: "Info",
|
||||
translationKey: "addon.panel.info",
|
||||
path: `/hassio/addon/${this.addon.slug}/info`,
|
||||
iconPath: mdiInformationVariant,
|
||||
},
|
||||
@@ -87,7 +80,7 @@ class HassioAddonDashboard extends LitElement {
|
||||
|
||||
if (this.addon.documentation) {
|
||||
addonTabs.push({
|
||||
name: "Documentation",
|
||||
translationKey: "addon.panel.documentation",
|
||||
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
||||
iconPath: mdiFileDocument,
|
||||
});
|
||||
@@ -96,12 +89,12 @@ class HassioAddonDashboard extends LitElement {
|
||||
if (this.addon.version) {
|
||||
addonTabs.push(
|
||||
{
|
||||
name: "Configuration",
|
||||
translationKey: "addon.panel.configuration",
|
||||
path: `/hassio/addon/${this.addon.slug}/config`,
|
||||
iconPath: mdiCogs,
|
||||
},
|
||||
{
|
||||
name: "Log",
|
||||
translationKey: "addon.panel.log",
|
||||
path: `/hassio/addon/${this.addon.slug}/logs`,
|
||||
iconPath: mdiMathLog,
|
||||
}
|
||||
@@ -113,11 +106,12 @@ class HassioAddonDashboard extends LitElement {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.localizeFunc=${this.supervisor.localize}
|
||||
.narrow=${this.narrow}
|
||||
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
|
||||
.route=${route}
|
||||
hassio
|
||||
.tabs=${addonTabs}
|
||||
supervisor
|
||||
>
|
||||
<span slot="header">${this.addon.name}</span>
|
||||
<hassio-addon-router
|
||||
@@ -131,7 +125,7 @@ class HassioAddonDashboard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
@@ -172,15 +166,27 @@ class HassioAddonDashboard extends LitElement {
|
||||
|
||||
protected async firstUpdated(): Promise<void> {
|
||||
if (this.route.path === "") {
|
||||
const addon = extractSearchParam("addon");
|
||||
if (addon) {
|
||||
navigate(this, `/hassio/addon/${addon}`, true);
|
||||
const requestedAddon = extractSearchParam("addon");
|
||||
if (requestedAddon) {
|
||||
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
||||
const validAddon = addonsInfo.addons.some(
|
||||
(addon) => addon.slug === requestedAddon
|
||||
);
|
||||
if (!validAddon) {
|
||||
this._error = this.supervisor.localize("my.error_addon_not_found");
|
||||
} else {
|
||||
navigate(`/hassio/addon/${requestedAddon}`, { replace: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
||||
}
|
||||
|
||||
private async _apiCalled(ev): Promise<void> {
|
||||
if (!ev.detail.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pathSplit: string[] = ev.detail.path?.split("/");
|
||||
|
||||
if (!pathSplit || pathSplit.length === 0) {
|
||||
@@ -190,7 +196,9 @@ class HassioAddonDashboard extends LitElement {
|
||||
const path: string = pathSplit[pathSplit.length - 1];
|
||||
|
||||
if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
|
||||
fireEvent(this, "supervisor-store-refresh", { store: "supervisor" });
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: "supervisor",
|
||||
});
|
||||
}
|
||||
|
||||
if (path === "uninstall") {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { customElement, property } from "lit-element";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user