Compare commits
917 Commits
fix-button
...
20221205.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bbdb84482a | ||
![]() |
08ffe375b9 | ||
![]() |
8699f3e3a8 | ||
![]() |
ecdd07ff4d | ||
![]() |
f0f699a37e | ||
![]() |
75b1b1c9a0 | ||
![]() |
0ae8246d8a | ||
![]() |
b644407260 | ||
![]() |
b389127f78 | ||
![]() |
20dff9d25d | ||
![]() |
076ddb71b6 | ||
![]() |
f0127511b0 | ||
![]() |
07ad429f8c | ||
![]() |
ef3caf91f1 | ||
![]() |
4fcfcbeefb | ||
![]() |
f18997c7c3 | ||
![]() |
2ed8a4053b | ||
![]() |
1105c92569 | ||
![]() |
76a682fa28 | ||
![]() |
d059b97a2f | ||
![]() |
96080f3c78 | ||
![]() |
00274ebf66 | ||
![]() |
7e58bd59c3 | ||
![]() |
04ef783f5b | ||
![]() |
340449d064 | ||
![]() |
24f2ad8be9 | ||
![]() |
3eac53e209 | ||
![]() |
65cef9d996 | ||
![]() |
4c5f4508b2 | ||
![]() |
1aa6bd5577 | ||
![]() |
3dfd401036 | ||
![]() |
71a5d8c6f9 | ||
![]() |
a0bf582cc9 | ||
![]() |
77a53ffc6c | ||
![]() |
b6f1d78b7f | ||
![]() |
1fda303b23 | ||
![]() |
241645fe8d | ||
![]() |
faa57e4c02 | ||
![]() |
915563ce6c | ||
![]() |
44502d2c8d | ||
![]() |
b97a9ef311 | ||
![]() |
1a66b8a374 | ||
![]() |
4190ff5a2b | ||
![]() |
dfc461ce05 | ||
![]() |
dff7f653b1 | ||
![]() |
eccc6a8cdb | ||
![]() |
52235c6187 | ||
![]() |
594b402bd5 | ||
![]() |
684f6db4df | ||
![]() |
883e5e3a6c | ||
![]() |
29452841c2 | ||
![]() |
9b6e33cfec | ||
![]() |
e43f3b193e | ||
![]() |
40d0455936 | ||
![]() |
90a7c2d2ff | ||
![]() |
d4cda0c106 | ||
![]() |
92d022747b | ||
![]() |
ee6f97b802 | ||
![]() |
cb97918005 | ||
![]() |
f1f0baf787 | ||
![]() |
c0240eed67 | ||
![]() |
aeeacc6cad | ||
![]() |
fcfdad3d94 | ||
![]() |
566b93ec1f | ||
![]() |
5c452cb9e0 | ||
![]() |
06c1d9f6ef | ||
![]() |
bfd96944f9 | ||
![]() |
c119163422 | ||
![]() |
4846fa1a74 | ||
![]() |
aec0eb3c78 | ||
![]() |
92e7254c54 | ||
![]() |
bafe581562 | ||
![]() |
e6a153a802 | ||
![]() |
fce87ff0fe | ||
![]() |
e6b3475b5b | ||
![]() |
f969299567 | ||
![]() |
7a87dc4d8a | ||
![]() |
d6fa1427f1 | ||
![]() |
aa1e9cedca | ||
![]() |
147b1f34ac | ||
![]() |
0cfba81eae | ||
![]() |
a9d44fcb61 | ||
![]() |
0aa2c9044a | ||
![]() |
9bae4a646d | ||
![]() |
15a0847db8 | ||
![]() |
8b817b35b0 | ||
![]() |
f95a3c75f6 | ||
![]() |
2223ffd7ee | ||
![]() |
590ad5b8a0 | ||
![]() |
2bb9961fc2 | ||
![]() |
3e2fb09251 | ||
![]() |
fc80daa3e0 | ||
![]() |
2aa7b95a5a | ||
![]() |
c2436fb157 | ||
![]() |
a958c6296b | ||
![]() |
fe4191aea9 | ||
![]() |
49d9cf41fe | ||
![]() |
0bfb2b4a56 | ||
![]() |
1a68a2f4d7 | ||
![]() |
1197e5a35b | ||
![]() |
c6386284d1 | ||
![]() |
185d2f1d52 | ||
![]() |
c5ec1797f6 | ||
![]() |
048b345c75 | ||
![]() |
70868da305 | ||
![]() |
cef3dbfdf0 | ||
![]() |
bda5b97c91 | ||
![]() |
ae15dd678b | ||
![]() |
681c745e84 | ||
![]() |
23b59978ab | ||
![]() |
90b9eaeb19 | ||
![]() |
65426bd2d0 | ||
![]() |
1cb44ce857 | ||
![]() |
1c03dc9b77 | ||
![]() |
238e844068 | ||
![]() |
ec6a4b4e7a | ||
![]() |
ac65882fdd | ||
![]() |
c92e6423e8 | ||
![]() |
db0d24c807 | ||
![]() |
9e56ddcc69 | ||
![]() |
dd4c3c28ee | ||
![]() |
245202c125 | ||
![]() |
8b8a85b4b8 | ||
![]() |
0aae285236 | ||
![]() |
31ac274a51 | ||
![]() |
6f07e7ca59 | ||
![]() |
fa506202ac | ||
![]() |
c810c67a53 | ||
![]() |
663c58512d | ||
![]() |
3cd64675df | ||
![]() |
79c8b7dc27 | ||
![]() |
98a32041d4 | ||
![]() |
ffbcb0a343 | ||
![]() |
ab4dd47e51 | ||
![]() |
c7cb8cf762 | ||
![]() |
a5ab4eaf0e | ||
![]() |
d52dbde909 | ||
![]() |
1cde9e882e | ||
![]() |
8cb0d38d78 | ||
![]() |
9cb168c439 | ||
![]() |
2add29c4eb | ||
![]() |
e2104c1591 | ||
![]() |
17ac81a708 | ||
![]() |
42386c7dee | ||
![]() |
2e988bf5c3 | ||
![]() |
3356d559c9 | ||
![]() |
43755deb39 | ||
![]() |
9778c0731c | ||
![]() |
ebc0edac10 | ||
![]() |
effc9467c2 | ||
![]() |
68e94d7222 | ||
![]() |
c4992c477b | ||
![]() |
449c1f2469 | ||
![]() |
d52e521ef8 | ||
![]() |
03d03f9903 | ||
![]() |
1122698351 | ||
![]() |
9d730919d5 | ||
![]() |
6326bb010f | ||
![]() |
2ab5da6d84 | ||
![]() |
a56b2e3270 | ||
![]() |
523d936010 | ||
![]() |
b3e2beac5a | ||
![]() |
4c8e863c0e | ||
![]() |
69074df1ab | ||
![]() |
16848d03ae | ||
![]() |
dd9683674d | ||
![]() |
822917d060 | ||
![]() |
7cc6809f53 | ||
![]() |
57291183ca | ||
![]() |
504e8dd946 | ||
![]() |
5c4517517d | ||
![]() |
1b917a5b04 | ||
![]() |
527c4f71c2 | ||
![]() |
3ac6e6f307 | ||
![]() |
9e955dbaaa | ||
![]() |
e0a56956e0 | ||
![]() |
66ed1b18be | ||
![]() |
d445bf2505 | ||
![]() |
16bd1f5883 | ||
![]() |
c12e6662dd | ||
![]() |
0b18875d70 | ||
![]() |
57fb8f9f01 | ||
![]() |
f1139e09f9 | ||
![]() |
51febc2218 | ||
![]() |
c8d16af1b5 | ||
![]() |
66a75c4714 | ||
![]() |
cb8e602340 | ||
![]() |
de008f65a3 | ||
![]() |
ab1b778439 | ||
![]() |
62ac9155fc | ||
![]() |
68302d0896 | ||
![]() |
a76f456ebc | ||
![]() |
9d3eaba46b | ||
![]() |
5bb9538861 | ||
![]() |
fe1beb0d59 | ||
![]() |
153161d2cb | ||
![]() |
370864e0ed | ||
![]() |
9b6fca2c0e | ||
![]() |
55467666f7 | ||
![]() |
928f20ada5 | ||
![]() |
b53e86ad03 | ||
![]() |
112ec10b30 | ||
![]() |
1b4989a7dc | ||
![]() |
1f9763d6c8 | ||
![]() |
b495667e8d | ||
![]() |
a46e72ffbd | ||
![]() |
0a2eb05062 | ||
![]() |
c9b5fe9a85 | ||
![]() |
58d5a07a43 | ||
![]() |
c44de09a7c | ||
![]() |
a0b645d1b9 | ||
![]() |
0b6c6b2b98 | ||
![]() |
bad3edc340 | ||
![]() |
d3015c362d | ||
![]() |
fbb8ff4362 | ||
![]() |
6393d59035 | ||
![]() |
62de708b2b | ||
![]() |
6c4c65730c | ||
![]() |
23f8373b16 | ||
![]() |
dec8883f2a | ||
![]() |
a475b06d49 | ||
![]() |
0972cb4583 | ||
![]() |
dad7c43fd2 | ||
![]() |
7e6a9f1653 | ||
![]() |
f627e98902 | ||
![]() |
0d623794ed | ||
![]() |
0a3fa3e218 | ||
![]() |
19887fbd54 | ||
![]() |
fe9967550b | ||
![]() |
9b19b6f203 | ||
![]() |
797718f478 | ||
![]() |
9ea0e3a75f | ||
![]() |
0b76b60f6e | ||
![]() |
d8be662bd6 | ||
![]() |
c478a15846 | ||
![]() |
811208363b | ||
![]() |
969772663b | ||
![]() |
c3b9438b3b | ||
![]() |
9b51df02d6 | ||
![]() |
8a4b0b081a | ||
![]() |
1ecc88291d | ||
![]() |
fb80da013e | ||
![]() |
a4fcb743fa | ||
![]() |
8444fe0a07 | ||
![]() |
1442f6d546 | ||
![]() |
c468fba36f | ||
![]() |
2afbfb01bd | ||
![]() |
907466d060 | ||
![]() |
4deee46864 | ||
![]() |
391cc95883 | ||
![]() |
0c800344d2 | ||
![]() |
08279f35cf | ||
![]() |
05f2ef8a37 | ||
![]() |
3a41b4e65b | ||
![]() |
e08c12c4dd | ||
![]() |
bb0884c4bb | ||
![]() |
45646eaf0b | ||
![]() |
d735b2b722 | ||
![]() |
310b110b8b | ||
![]() |
06557709ae | ||
![]() |
1b5571557c | ||
![]() |
712ecbd13c | ||
![]() |
ea286b6fac | ||
![]() |
a2953138f4 | ||
![]() |
b327d6e0bc | ||
![]() |
88983806ae | ||
![]() |
1ac701d1c8 | ||
![]() |
aaeca323d4 | ||
![]() |
83a4fd6c1b | ||
![]() |
ef9643ddaf | ||
![]() |
af93ec1b92 | ||
![]() |
4dce9404a4 | ||
![]() |
ed54c70e75 | ||
![]() |
114507b5cb | ||
![]() |
8eddaa1914 | ||
![]() |
37394f7bc5 | ||
![]() |
d5cdd53fab | ||
![]() |
0ac2393ecb | ||
![]() |
c38892a162 | ||
![]() |
c77e5dee84 | ||
![]() |
3752336a9a | ||
![]() |
70d4fe1285 | ||
![]() |
d3738adf11 | ||
![]() |
7ededd2766 | ||
![]() |
ed8b07b7e2 | ||
![]() |
c12189b27f | ||
![]() |
9c923e45c5 | ||
![]() |
66bfdb6d12 | ||
![]() |
aa673774a8 | ||
![]() |
8c7974e466 | ||
![]() |
a70e2342a2 | ||
![]() |
b5c9aae1aa | ||
![]() |
93893d0237 | ||
![]() |
ad3dcb355f | ||
![]() |
952b433b2c | ||
![]() |
200fff506c | ||
![]() |
e84b9b7c6f | ||
![]() |
e2a89b1157 | ||
![]() |
cb0310593c | ||
![]() |
c14d9ab957 | ||
![]() |
dd695545d3 | ||
![]() |
176d8567f4 | ||
![]() |
4f4a95c04e | ||
![]() |
6393944a1b | ||
![]() |
f768c5ef7f | ||
![]() |
650d579d05 | ||
![]() |
9811f2681c | ||
![]() |
6a3ac9116e | ||
![]() |
2a6ef9b955 | ||
![]() |
b9395e1c97 | ||
![]() |
cd8c1f42ca | ||
![]() |
0ec887ad50 | ||
![]() |
f8a7737eb9 | ||
![]() |
3e01597a38 | ||
![]() |
6d230ebd65 | ||
![]() |
9035e8e9dc | ||
![]() |
7ff138534f | ||
![]() |
0b76183acd | ||
![]() |
9f658c10c3 | ||
![]() |
72ed6fdd6b | ||
![]() |
3bdc5ad420 | ||
![]() |
8d2f7d99af | ||
![]() |
b88317f1d3 | ||
![]() |
8ccd0426dd | ||
![]() |
c17e8ba65a | ||
![]() |
d9f1540115 | ||
![]() |
1b480248d1 | ||
![]() |
e88bb1114b | ||
![]() |
80e868e281 | ||
![]() |
d8a49c6eec | ||
![]() |
594ee85bbe | ||
![]() |
182b8f809c | ||
![]() |
8e4bebb694 | ||
![]() |
dddb922593 | ||
![]() |
da38cbccf1 | ||
![]() |
71c43058ea | ||
![]() |
f9d119d33d | ||
![]() |
d3b97ae91c | ||
![]() |
5146fa1d9e | ||
![]() |
fc86a66c33 | ||
![]() |
3959a7475c | ||
![]() |
61d09072a7 | ||
![]() |
4a07d3d39b | ||
![]() |
be30cdb51f | ||
![]() |
894258d7b8 | ||
![]() |
296d5f8ffe | ||
![]() |
d1c2020ee4 | ||
![]() |
3c1b2aa4f3 | ||
![]() |
17a11809de | ||
![]() |
3083d5b04c | ||
![]() |
0848c096b9 | ||
![]() |
8d5c36a96a | ||
![]() |
cc76a6c5ed | ||
![]() |
01fd2787be | ||
![]() |
c79955e76a | ||
![]() |
51874329d1 | ||
![]() |
6252955bb5 | ||
![]() |
5422fda990 | ||
![]() |
db8bc9d34a | ||
![]() |
9f19bdde65 | ||
![]() |
7336c1280f | ||
![]() |
8e245c8a83 | ||
![]() |
5a150ac80d | ||
![]() |
eac13980ff | ||
![]() |
977fdd9fbb | ||
![]() |
cedde3d6a2 | ||
![]() |
56c78ae108 | ||
![]() |
82a641a200 | ||
![]() |
04181e9c28 | ||
![]() |
4b8960c236 | ||
![]() |
fc104e7280 | ||
![]() |
8c125f4dee | ||
![]() |
b93f457d53 | ||
![]() |
e8ce6ad919 | ||
![]() |
0ce695577c | ||
![]() |
50b67751d9 | ||
![]() |
05515f21c3 | ||
![]() |
063c377797 | ||
![]() |
aee11da671 | ||
![]() |
5fcb219fcd | ||
![]() |
a97dfbb51f | ||
![]() |
c5f4e8ffdd | ||
![]() |
7ffd30643a | ||
![]() |
dcfcd54f10 | ||
![]() |
3b103619ec | ||
![]() |
614c1574ca | ||
![]() |
e1e3f9d925 | ||
![]() |
0ba4a07b92 | ||
![]() |
5a5f31b32c | ||
![]() |
ff92768973 | ||
![]() |
bb0529ecd2 | ||
![]() |
bc62e9372b | ||
![]() |
087a897cbe | ||
![]() |
589efa8cc5 | ||
![]() |
c93179c307 | ||
![]() |
9e416e829c | ||
![]() |
e13c632afa | ||
![]() |
544c8fe3bb | ||
![]() |
81b21f874b | ||
![]() |
ea319d55ef | ||
![]() |
8c03bbdccc | ||
![]() |
321914d53a | ||
![]() |
fe46f759c9 | ||
![]() |
490d46396e | ||
![]() |
7ec28c4314 | ||
![]() |
e9f4307d15 | ||
![]() |
3c62bc9b18 | ||
![]() |
28bae5071c | ||
![]() |
e9281ad9f1 | ||
![]() |
78187c5b0b | ||
![]() |
f164ad0b89 | ||
![]() |
48b10005e3 | ||
![]() |
b3d64fc52a | ||
![]() |
23e5a47b3b | ||
![]() |
55d84973c6 | ||
![]() |
de90a62de7 | ||
![]() |
3a17f2d73e | ||
![]() |
8f6a09f44c | ||
![]() |
d78191efa6 | ||
![]() |
749d869e03 | ||
![]() |
5d6f446c30 | ||
![]() |
3cf14bb2e1 | ||
![]() |
f7253a73a5 | ||
![]() |
cfabaa8716 | ||
![]() |
032f497687 | ||
![]() |
432483b3d2 | ||
![]() |
e95d5b1afb | ||
![]() |
330f3e5ce4 | ||
![]() |
fee6ae3045 | ||
![]() |
5e431a07ad | ||
![]() |
5d4c090b26 | ||
![]() |
b84240edbc | ||
![]() |
f4dc74b2e8 | ||
![]() |
c95d19299b | ||
![]() |
7696df56ac | ||
![]() |
771733d326 | ||
![]() |
4f3c708109 | ||
![]() |
d2078a7e50 | ||
![]() |
d70cb24722 | ||
![]() |
6902537666 | ||
![]() |
9ea1f61971 | ||
![]() |
782c95cf04 | ||
![]() |
d5d6216cfe | ||
![]() |
1086c85964 | ||
![]() |
47c0901df2 | ||
![]() |
d323ab6726 | ||
![]() |
07b5856190 | ||
![]() |
462dee0351 | ||
![]() |
f181a085de | ||
![]() |
bf5589b88d | ||
![]() |
c5428d8581 | ||
![]() |
fd431f36f7 | ||
![]() |
7acf3a049e | ||
![]() |
cfb0e8b39e | ||
![]() |
a3abbf3812 | ||
![]() |
980156d23a | ||
![]() |
37c2a3636e | ||
![]() |
b682d13486 | ||
![]() |
7f82b90c25 | ||
![]() |
3e9d6ea2c5 | ||
![]() |
57c5c1c191 | ||
![]() |
b553a3fd92 | ||
![]() |
d1964e92ea | ||
![]() |
a889969bb8 | ||
![]() |
f80b2c578b | ||
![]() |
579f73e08f | ||
![]() |
d13c6d3e7b | ||
![]() |
e78c875e8e | ||
![]() |
5e8c54b00f | ||
![]() |
71bc74893f | ||
![]() |
df72e5099e | ||
![]() |
e6862daa38 | ||
![]() |
66db8c999f | ||
![]() |
00bc315fc1 | ||
![]() |
f461825a59 | ||
![]() |
8c98326e31 | ||
![]() |
7bb619b0c3 | ||
![]() |
07bc9081b8 | ||
![]() |
91fa5972d1 | ||
![]() |
abbfde19a2 | ||
![]() |
ccd617d68e | ||
![]() |
8a7f35cee6 | ||
![]() |
e510e5b371 | ||
![]() |
aa6ee0f6d2 | ||
![]() |
f3449c4d9b | ||
![]() |
3c289deb21 | ||
![]() |
847d163cc8 | ||
![]() |
28d11703fc | ||
![]() |
1f003ae3be | ||
![]() |
a57d7813e7 | ||
![]() |
5842b10a10 | ||
![]() |
8ee9655bd5 | ||
![]() |
3ef567dcd5 | ||
![]() |
37f6b4f6be | ||
![]() |
a817faae54 | ||
![]() |
ab745f6e8e | ||
![]() |
02d608b704 | ||
![]() |
310df387e7 | ||
![]() |
fe8e79a67f | ||
![]() |
8ffe676827 | ||
![]() |
f032d0dbcf | ||
![]() |
81cc745c0a | ||
![]() |
43f9c9ebc9 | ||
![]() |
a9d1feb196 | ||
![]() |
8ec2c38f72 | ||
![]() |
72aea57105 | ||
![]() |
031ecf5be8 | ||
![]() |
93e7927686 | ||
![]() |
320d8e6190 | ||
![]() |
efa4f65686 | ||
![]() |
ec257710ff | ||
![]() |
ffad6f340f | ||
![]() |
0b637fc9bd | ||
![]() |
9f9b0b6457 | ||
![]() |
a4227680de | ||
![]() |
5cfd263617 | ||
![]() |
430e671901 | ||
![]() |
8fcd396445 | ||
![]() |
e273b6b659 | ||
![]() |
2751adf440 | ||
![]() |
d661450121 | ||
![]() |
4511ded205 | ||
![]() |
2bf0c5d72d | ||
![]() |
604e5d5e09 | ||
![]() |
5466705d97 | ||
![]() |
774aee406c | ||
![]() |
775837b60f | ||
![]() |
030b2b921a | ||
![]() |
229bc26327 | ||
![]() |
99e85173eb | ||
![]() |
be0c22d7ae | ||
![]() |
fee1092a08 | ||
![]() |
d041bd9fd3 | ||
![]() |
93debac19a | ||
![]() |
3fc94106b8 | ||
![]() |
e976f9c119 | ||
![]() |
be4dcbe405 | ||
![]() |
c116ad67ed | ||
![]() |
1e19799da9 | ||
![]() |
9b2dcbdb59 | ||
![]() |
ae4a37f23a | ||
![]() |
e463a997c1 | ||
![]() |
92c8de307d | ||
![]() |
c751b0b759 | ||
![]() |
cb5621032d | ||
![]() |
e861460318 | ||
![]() |
8fd99fcb15 | ||
![]() |
260855abbb | ||
![]() |
3648c8c07a | ||
![]() |
e74fd5fcdc | ||
![]() |
2e46b04204 | ||
![]() |
989a0b9173 | ||
![]() |
80a2a7b989 | ||
![]() |
2547a975f6 | ||
![]() |
35fa763086 | ||
![]() |
44e38cd24e | ||
![]() |
ad58d16dfa | ||
![]() |
98352ae7b7 | ||
![]() |
b9fbad663d | ||
![]() |
fd166fa89e | ||
![]() |
88decba851 | ||
![]() |
8db1881a93 | ||
![]() |
0038f54cea | ||
![]() |
a3d80f1280 | ||
![]() |
1c05bc6380 | ||
![]() |
5d1536030a | ||
![]() |
a8833a5ec1 | ||
![]() |
6446534e0b | ||
![]() |
d64c81a123 | ||
![]() |
80e7993923 | ||
![]() |
700af72303 | ||
![]() |
255cb23c7d | ||
![]() |
669f7efa97 | ||
![]() |
166d6f1c88 | ||
![]() |
d64ade3848 | ||
![]() |
c2542a3baa | ||
![]() |
6b7c00edbc | ||
![]() |
89c6fa7383 | ||
![]() |
ca91f71d2e | ||
![]() |
25e0c05723 | ||
![]() |
8fd5273fae | ||
![]() |
807bb10199 | ||
![]() |
5ce81232b5 | ||
![]() |
a1bc748bc1 | ||
![]() |
be169f9c83 | ||
![]() |
ed82ae9f68 | ||
![]() |
8bcbeb299b | ||
![]() |
fc1481d365 | ||
![]() |
2475f6bd41 | ||
![]() |
dff3ffe935 | ||
![]() |
1616911ba9 | ||
![]() |
8d18fb79fb | ||
![]() |
f5b44656cf | ||
![]() |
c82782fa1b | ||
![]() |
ab14cf9e9b | ||
![]() |
c0051aeb68 | ||
![]() |
44422086d7 | ||
![]() |
738367a7c7 | ||
![]() |
5fb2e3316a | ||
![]() |
82f48d106f | ||
![]() |
bbc5b02a22 | ||
![]() |
dfface6904 | ||
![]() |
9ed0cb3011 | ||
![]() |
4b54cb4a35 | ||
![]() |
1b5c30712e | ||
![]() |
7d3d800d4c | ||
![]() |
d4262ecb09 | ||
![]() |
ec7dea93a0 | ||
![]() |
aa2641d5c9 | ||
![]() |
5ecde44243 | ||
![]() |
209ba79823 | ||
![]() |
52a1594969 | ||
![]() |
24509425ca | ||
![]() |
7e5cd9a1c8 | ||
![]() |
8b13a9ff2e | ||
![]() |
b33c546610 | ||
![]() |
38fd6108b4 | ||
![]() |
57fdea19fd | ||
![]() |
d2a19e04ef | ||
![]() |
196456d0c4 | ||
![]() |
5c16447eed | ||
![]() |
f3d92ba0e0 | ||
![]() |
088b3587e0 | ||
![]() |
ede9d8a073 | ||
![]() |
8c71885b4c | ||
![]() |
47b820d28f | ||
![]() |
d7b888f761 | ||
![]() |
12e57dfcae | ||
![]() |
9a1fc02755 | ||
![]() |
eb4dbef610 | ||
![]() |
33ce27de02 | ||
![]() |
089f531492 | ||
![]() |
3aa813e391 | ||
![]() |
a989eb1c66 | ||
![]() |
e0448be24d | ||
![]() |
651cafc464 | ||
![]() |
38607a6410 | ||
![]() |
9046c0d0bf | ||
![]() |
589cec10f6 | ||
![]() |
dba9658658 | ||
![]() |
d21bdf2807 | ||
![]() |
3fe5075ad4 | ||
![]() |
f76a3ea2ce | ||
![]() |
e95a5ebbbf | ||
![]() |
ae28eb3813 | ||
![]() |
b0807cb80c | ||
![]() |
95231554d5 | ||
![]() |
f3b543f46c | ||
![]() |
9eb81e2211 | ||
![]() |
1322ff9295 | ||
![]() |
5f169b48d9 | ||
![]() |
75d05cdb0e | ||
![]() |
b444d0030f | ||
![]() |
e241b20378 | ||
![]() |
ca28feca80 | ||
![]() |
f5d9a7d662 | ||
![]() |
3d236a8f49 | ||
![]() |
0ebeec0db6 | ||
![]() |
d23d774ec1 | ||
![]() |
0d5b86e2b6 | ||
![]() |
825558c8db | ||
![]() |
12239d7fe3 | ||
![]() |
6eac6aef18 | ||
![]() |
a79d6b6a4d | ||
![]() |
1d47303127 | ||
![]() |
150bc00c31 | ||
![]() |
df4b83349e | ||
![]() |
d4232a2256 | ||
![]() |
f7e348c19b | ||
![]() |
f44fd35b90 | ||
![]() |
dac1d76bd2 | ||
![]() |
0ab823bcf5 | ||
![]() |
65e952aaeb | ||
![]() |
cfdf043444 | ||
![]() |
ecc1bf5206 | ||
![]() |
57d664d87d | ||
![]() |
cf2cd4043d | ||
![]() |
98761cab3f | ||
![]() |
4a622f9424 | ||
![]() |
61b42249ec | ||
![]() |
c27e3325d9 | ||
![]() |
08efc2fdd1 | ||
![]() |
53519ae8ab | ||
![]() |
9baeabed19 | ||
![]() |
f3229bb8a7 | ||
![]() |
86c971b76a | ||
![]() |
afc69cb270 | ||
![]() |
a379d29a6c | ||
![]() |
0769b14566 | ||
![]() |
1dc68b72da | ||
![]() |
40616b6af2 | ||
![]() |
c08be957ce | ||
![]() |
140e269697 | ||
![]() |
7c18d5aa0e | ||
![]() |
1acdc9cd6c | ||
![]() |
7501849044 | ||
![]() |
26ed13e548 | ||
![]() |
086c33d8b3 | ||
![]() |
c73677f15d | ||
![]() |
f7090583ac | ||
![]() |
68517018cc | ||
![]() |
62a0a64554 | ||
![]() |
adf3fa6a0e | ||
![]() |
b443ec0af5 | ||
![]() |
b9ae0e72b1 | ||
![]() |
63ea8e6568 | ||
![]() |
5be624f45d | ||
![]() |
38f19b6180 | ||
![]() |
c99f00ba50 | ||
![]() |
ce5776f59d | ||
![]() |
12ff70020a | ||
![]() |
5d605447a5 | ||
![]() |
6d88d46ce4 | ||
![]() |
cbe2643146 | ||
![]() |
d332b8ab14 | ||
![]() |
adfef05110 | ||
![]() |
ca6a7bfbe2 | ||
![]() |
a22f96a481 | ||
![]() |
688109524d | ||
![]() |
62dd7111ce | ||
![]() |
1267575f62 | ||
![]() |
b7da4dc68f | ||
![]() |
826474518f | ||
![]() |
a4b92fef3a | ||
![]() |
d41159591c | ||
![]() |
cb256bc386 | ||
![]() |
bd50d6a6a3 | ||
![]() |
05418fc83b | ||
![]() |
8b675cdbba | ||
![]() |
36b4909950 | ||
![]() |
157b3ba5f2 | ||
![]() |
72443b4f24 | ||
![]() |
1c7d3fe610 | ||
![]() |
6ac4560b36 | ||
![]() |
9309a4c7bc | ||
![]() |
b582a4d014 | ||
![]() |
e4d233afa8 | ||
![]() |
b131b255ec | ||
![]() |
20bdb9ff35 | ||
![]() |
666ef7a978 | ||
![]() |
24a97347df | ||
![]() |
0825d5c64e | ||
![]() |
535e752ec7 | ||
![]() |
b611a58fce | ||
![]() |
24e54554ad | ||
![]() |
d23fca4dd1 | ||
![]() |
729e2f5248 | ||
![]() |
437723c6a6 | ||
![]() |
a30c8205b1 | ||
![]() |
c50cf78bb4 | ||
![]() |
be52ba0ea9 | ||
![]() |
55e9ebc4d2 | ||
![]() |
29c3fb0f92 | ||
![]() |
7c3cd9d88d | ||
![]() |
4881d699e3 | ||
![]() |
414db83359 | ||
![]() |
cd4f6e19f4 | ||
![]() |
da709cbbd1 | ||
![]() |
ee9ca16eb5 | ||
![]() |
399efca411 | ||
![]() |
f8bccf9e79 | ||
![]() |
87aab72b63 | ||
![]() |
24688ba18e | ||
![]() |
e8086b6a6f | ||
![]() |
4358437278 | ||
![]() |
e0a9c57a54 | ||
![]() |
e63953ecbc | ||
![]() |
72af200190 | ||
![]() |
2094ae534b | ||
![]() |
f6d6fd179f | ||
![]() |
153ebb2a20 | ||
![]() |
5d58e52eea | ||
![]() |
5038f9c3c6 | ||
![]() |
b285fda61b | ||
![]() |
6cd38472cd | ||
![]() |
8fd5f53f96 | ||
![]() |
b70eee77ef | ||
![]() |
4148b8c7aa | ||
![]() |
e22dd0c49d | ||
![]() |
30a254f98f | ||
![]() |
8fcb3a017b | ||
![]() |
5e29c7efa9 | ||
![]() |
b6cc3e3ef0 | ||
![]() |
184bdc0c85 | ||
![]() |
f7fb731dc8 | ||
![]() |
77977f64a3 | ||
![]() |
e8da573ba2 | ||
![]() |
07332bf155 | ||
![]() |
f3c7583bf7 | ||
![]() |
1cc02415d3 | ||
![]() |
6ca3f06ea0 | ||
![]() |
198e2b7bdf | ||
![]() |
5a68e2c977 | ||
![]() |
d9d29db560 | ||
![]() |
124c6dc2b8 | ||
![]() |
0f3886e053 | ||
![]() |
68bb3558b4 | ||
![]() |
b8bd15aa33 | ||
![]() |
ed39aa6a7c | ||
![]() |
405cae9b5f | ||
![]() |
3ca2cbb3f9 | ||
![]() |
c295ae56ab | ||
![]() |
830364721b | ||
![]() |
19089213e3 | ||
![]() |
b633067e5c | ||
![]() |
1b8874cbd4 | ||
![]() |
a5f8ce85ba | ||
![]() |
56eacf5733 | ||
![]() |
9324061d05 | ||
![]() |
eafcbdc65b | ||
![]() |
0175522c17 | ||
![]() |
cff3f51d34 | ||
![]() |
0f580a91c9 | ||
![]() |
389f50b29a | ||
![]() |
b689bb8fcf | ||
![]() |
36f067ede4 | ||
![]() |
c2178622dd | ||
![]() |
014448e7ea | ||
![]() |
08eff0509a | ||
![]() |
62d0882e82 | ||
![]() |
86a574dbbd | ||
![]() |
71ac4620c5 | ||
![]() |
f611049517 | ||
![]() |
45fa8c272f | ||
![]() |
28a1c97571 | ||
![]() |
d9a5ae0cf1 | ||
![]() |
c03849d30b | ||
![]() |
535fe2686b | ||
![]() |
709bc87a36 | ||
![]() |
2812b467ec | ||
![]() |
7d118a5715 | ||
![]() |
8bd7370a02 | ||
![]() |
9fa8a96d09 | ||
![]() |
508d1fffef | ||
![]() |
3633daa814 | ||
![]() |
05346ae9fc | ||
![]() |
ea667cf0b9 | ||
![]() |
048ac3965e | ||
![]() |
276b6f4d1f | ||
![]() |
e765d7749c | ||
![]() |
9a3b4d6df2 | ||
![]() |
529e27992e | ||
![]() |
6c5cf2a0ec | ||
![]() |
a4cb270f09 | ||
![]() |
5160a1f55c | ||
![]() |
6a3a0db338 | ||
![]() |
765d4eb3b4 | ||
![]() |
cc09e24d66 | ||
![]() |
e7848262ea | ||
![]() |
0926202eca | ||
![]() |
e83af02410 | ||
![]() |
74d6a52fa9 | ||
![]() |
5baa975632 | ||
![]() |
4ad49ef07f | ||
![]() |
bc47ecaa57 | ||
![]() |
2bd617ce6e | ||
![]() |
dbaf955525 | ||
![]() |
578ff5b53f | ||
![]() |
e386942ea7 | ||
![]() |
2fdd50f45f | ||
![]() |
4b36770adf | ||
![]() |
54377225ec | ||
![]() |
f020add6be | ||
![]() |
b1a3996cf1 | ||
![]() |
a47a0ed716 | ||
![]() |
91cd584b4b | ||
![]() |
75562efb79 | ||
![]() |
f464bcfc14 | ||
![]() |
f8af66d310 | ||
![]() |
4922e575f8 | ||
![]() |
ac08daa64e | ||
![]() |
97f082a384 | ||
![]() |
ced37aab4c | ||
![]() |
1938fb89e6 | ||
![]() |
6842c479d6 | ||
![]() |
881f6b0531 | ||
![]() |
a564ceb9e3 | ||
![]() |
077fa3f6b2 | ||
![]() |
ceda911670 | ||
![]() |
afd41e79f0 | ||
![]() |
10f63180eb | ||
![]() |
e54802bd87 | ||
![]() |
c1d6b51065 | ||
![]() |
ab65ce819f | ||
![]() |
1e011bfe34 | ||
![]() |
5951f5c5c4 | ||
![]() |
0183e32267 | ||
![]() |
588fd87654 | ||
![]() |
e2944b098d | ||
![]() |
cbb962f084 | ||
![]() |
93f4ae1bea | ||
![]() |
d810cae194 | ||
![]() |
6797e17fc8 | ||
![]() |
6e58cd5d12 | ||
![]() |
a72fd19b73 | ||
![]() |
41c61a2895 | ||
![]() |
f35af9ed98 | ||
![]() |
abf7cb7a74 | ||
![]() |
6ec2e32241 | ||
![]() |
b7cdd9a22f | ||
![]() |
6278eefc5d | ||
![]() |
73cf0b54c9 | ||
![]() |
00dcecabb7 | ||
![]() |
c9df93bc54 | ||
![]() |
3550a8c263 | ||
![]() |
c0d30c56d6 | ||
![]() |
10813d06b6 | ||
![]() |
d65e45ecfd | ||
![]() |
1b158d8310 | ||
![]() |
9d2fcec458 | ||
![]() |
60cd6c65f0 | ||
![]() |
a39af9c307 | ||
![]() |
02af4c2156 | ||
![]() |
d02cd122a9 | ||
![]() |
8e962fdecb | ||
![]() |
1f65193a97 | ||
![]() |
24484d0e74 |
8
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
time: "06:00"
|
||||||
|
open-pull-requests-limit: 10
|
19
.github/workflows/ci.yaml
vendored
@@ -11,17 +11,18 @@ on:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
env:
|
env:
|
||||||
NODE_VERSION: 14
|
NODE_VERSION: 16
|
||||||
NODE_OPTIONS: --max_old_space_size=6144
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -43,9 +44,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -62,9 +63,9 @@ jobs:
|
|||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -81,9 +82,9 @@ jobs:
|
|||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
8
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
@@ -36,14 +36,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -57,4 +57,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
12
.github/workflows/demo.yaml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
|
|
||||||
env:
|
env:
|
||||||
NODE_VERSION: 14
|
NODE_VERSION: 16
|
||||||
NODE_OPTIONS: --max_old_space_size=6144
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -14,9 +14,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -26,10 +26,10 @@ jobs:
|
|||||||
CI: true
|
CI: true
|
||||||
- name: Build Demo
|
- name: Build Demo
|
||||||
run: ./node_modules/.bin/gulp build-demo
|
run: ./node_modules/.bin/gulp build-demo
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
uses: netlify/actions/cli@master
|
run: npx netlify-cli deploy --dir=demo/dist --prod
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
|
||||||
with:
|
|
||||||
args: deploy --dir=demo/dist --prod
|
|
||||||
|
2
.github/workflows/lock.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v2.0.1
|
- uses: dessant/lock-threads@v4.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-lock-inactive-days: "30"
|
issue-lock-inactive-days: "30"
|
||||||
|
72
.github/workflows/nightly.yaml
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
name: Nightly
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 1 * * *"
|
||||||
|
|
||||||
|
env:
|
||||||
|
PYTHON_VERSION: "3.10"
|
||||||
|
NODE_VERSION: 16
|
||||||
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
actions: none
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
nightly:
|
||||||
|
name: Nightly
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
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: Bump version
|
||||||
|
run: script/version_bump.js nightly
|
||||||
|
|
||||||
|
- name: Build nightly Python wheels
|
||||||
|
run: |
|
||||||
|
pip install build
|
||||||
|
yarn install
|
||||||
|
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
||||||
|
script/build_frontend
|
||||||
|
rm -rf dist home_assistant_frontend.egg-info
|
||||||
|
python3 -m build
|
||||||
|
|
||||||
|
- name: Archive translations
|
||||||
|
run: tar -czvf translations.tar.gz translations
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: wheels
|
||||||
|
path: dist/home_assistant_frontend*.whl
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Upload translations
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: translations
|
||||||
|
path: translations.tar.gz
|
||||||
|
if-no-files-found: error
|
46
.github/workflows/release.yaml
vendored
@@ -6,8 +6,8 @@ on:
|
|||||||
- published
|
- published
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: 3.8
|
PYTHON_VERSION: "3.10"
|
||||||
NODE_VERSION: 14
|
NODE_VERSION: 16
|
||||||
NODE_OPTIONS: --max_old_space_size=6144
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
|
||||||
# Set default workflow permissions
|
# Set default workflow permissions
|
||||||
@@ -21,21 +21,21 @@ jobs:
|
|||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Verify version
|
- name: Verify version
|
||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -52,11 +52,11 @@ jobs:
|
|||||||
python3 -m pip install twine build
|
python3 -m pip install twine build
|
||||||
export TWINE_USERNAME="__token__"
|
export TWINE_USERNAME="__token__"
|
||||||
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
|
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
|
||||||
|
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
||||||
script/release
|
script/release
|
||||||
|
|
||||||
- name: Upload release assets
|
- name: Upload release assets
|
||||||
uses: softprops/action-gh-release@v0.1.14
|
uses: softprops/action-gh-release@v0.1.15
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
dist/*.whl
|
dist/*.whl
|
||||||
@@ -74,33 +74,11 @@ jobs:
|
|||||||
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
||||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||||
|
|
||||||
- name: Upload requirements.txt
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: requirements
|
|
||||||
path: ./requirements.txt
|
|
||||||
|
|
||||||
build-wheels:
|
|
||||||
name: Build wheels for ${{ matrix.arch }}
|
|
||||||
needs: wheels-init
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
|
||||||
tag:
|
|
||||||
- "3.9-alpine3.14"
|
|
||||||
steps:
|
|
||||||
- name: Download requirements.txt
|
|
||||||
uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: requirements
|
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: home-assistant/wheels@master
|
uses: home-assistant/wheels@2022.10.1
|
||||||
with:
|
with:
|
||||||
tag: ${{ matrix.tag }}
|
abi: cp310
|
||||||
arch: ${{ matrix.arch }}
|
tag: musllinux_1_2
|
||||||
wheels-host: ${{ secrets.WHEELS_HOST }}
|
arch: amd64
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
wheels-user: wheels
|
|
||||||
requirements: "requirements.txt"
|
requirements: "requirements.txt"
|
||||||
|
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 90 days stale policy
|
- name: 90 days stale policy
|
||||||
uses: actions/stale@v3.0.13
|
uses: actions/stale@v6.0.1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: 90
|
days-before-stale: 90
|
||||||
|
4
.github/workflows/translations.yaml
vendored
@@ -8,7 +8,7 @@ on:
|
|||||||
- src/translations/en.json
|
- src/translations/en.json
|
||||||
|
|
||||||
env:
|
env:
|
||||||
NODE_VERSION: 14
|
NODE_VERSION: 16
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload:
|
upload:
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
1
.gitignore
vendored
@@ -5,6 +5,7 @@
|
|||||||
build
|
build
|
||||||
hass_frontend/*
|
hass_frontend/*
|
||||||
dist
|
dist
|
||||||
|
translations
|
||||||
|
|
||||||
# yarn
|
# yarn
|
||||||
.yarn/*
|
.yarn/*
|
||||||
|
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
yarn run lint-staged --relative --shell "/bin/bash"
|
10
.vscode/tasks.json
vendored
@@ -181,7 +181,7 @@
|
|||||||
{
|
{
|
||||||
"label": "Run HA Core for Supervisor in devcontainer",
|
"label": "Run HA Core for Supervisor in devcontainer",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "HASSIO=${input:supervisorHost} HASSIO_TOKEN=${input:supervisorToken} script/core",
|
"command": "SUPERVISOR=${input:supervisorHost} SUPERVISOR_TOKEN=${input:supervisorToken} script/core",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
@@ -191,7 +191,13 @@
|
|||||||
"runOptions": {
|
"runOptions": {
|
||||||
"instanceLimit": 1
|
"instanceLimit": 1
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"label": "Setup and fetch nightly translations",
|
||||||
|
"type": "gulp",
|
||||||
|
"task": "setup-and-fetch-nightly-translations",
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
|
785
.yarn/releases/yarn-3.2.0.cjs
vendored
783
.yarn/releases/yarn-3.2.3.cjs
vendored
Executable file
@@ -6,4 +6,4 @@ plugins:
|
|||||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||||
spec: "@yarnpkg/plugin-interactive-tools"
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
yarnPath: .yarn/releases/yarn-3.2.3.cjs
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
"import/no-extraneous-dependencies": 0,
|
|
||||||
"no-restricted-syntax": 0,
|
|
||||||
"no-console": 0
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +1,12 @@
|
|||||||
{
|
{
|
||||||
"extends": "../.eslintrc.json",
|
"extends": "../.eslintrc.json",
|
||||||
"rules": {
|
"rules": {
|
||||||
"import/no-extraneous-dependencies": 0,
|
"no-console": "off",
|
||||||
"global-require": 0
|
"import/no-extraneous-dependencies": "off",
|
||||||
|
"import/extensions": "off",
|
||||||
|
"import/no-dynamic-require": "off",
|
||||||
|
"global-require": "off",
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"prefer-arrow-callback": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||||
@@ -29,7 +28,6 @@ module.exports = function inlineConstants(babel, options, cwd) {
|
|||||||
const absolute = module.startsWith(".")
|
const absolute = module.startsWith(".")
|
||||||
? require.resolve(module, { paths: [cwd] })
|
? require.resolve(module, { paths: [cwd] })
|
||||||
: module;
|
: module;
|
||||||
// eslint-disable-next-line import/no-dynamic-require
|
|
||||||
return [absolute, require(absolute)];
|
return [absolute, require(absolute)];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const env = require("./env.js");
|
const env = require("./env.js");
|
||||||
const paths = require("./paths.js");
|
const paths = require("./paths.js");
|
||||||
|
|
||||||
// Files from NPM Packages that should not be imported
|
// Files from NPM Packages that should not be imported
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||||
// Part of yaml.js and only used for !!js functions that we don't use
|
// Part of yaml.js and only used for !!js functions that we don't use
|
||||||
require.resolve("esprima"),
|
require.resolve("esprima"),
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const paths = require("./paths.js");
|
const paths = require("./paths.js");
|
||||||
@@ -27,7 +26,7 @@ module.exports = {
|
|||||||
version() {
|
version() {
|
||||||
const version = fs
|
const version = fs
|
||||||
.readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8")
|
.readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8")
|
||||||
.match(/version\W+=\W"(\d{8}\.\d)"/);
|
.match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/);
|
||||||
if (!version) {
|
if (!version) {
|
||||||
throw Error("Version not found");
|
throw Error("Version not found");
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,12 @@
|
|||||||
const del = require("del");
|
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const fs = require("fs");
|
const fs = require("fs/promises");
|
||||||
const mapStream = require("map-stream");
|
const mapStream = require("map-stream");
|
||||||
|
|
||||||
const inDirFrontend = "translations/frontend";
|
const inDirFrontend = "translations/frontend";
|
||||||
const inDirBackend = "translations/backend";
|
const inDirBackend = "translations/backend";
|
||||||
const downloadDir = "translations/downloads";
|
|
||||||
const srcMeta = "src/translations/translationMetadata.json";
|
const srcMeta = "src/translations/translationMetadata.json";
|
||||||
|
|
||||||
const encoding = "utf8";
|
const encoding = "utf8";
|
||||||
|
|
||||||
const tasks = [];
|
|
||||||
|
|
||||||
function hasHtml(data) {
|
function hasHtml(data) {
|
||||||
return /<[a-z][\s\S]*>/i.test(data);
|
return /<[a-z][\s\S]*>/i.test(data);
|
||||||
}
|
}
|
||||||
@@ -46,50 +41,29 @@ function checkHtml() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let taskName = "clean-downloaded-translations";
|
// Backend translations do not currently pass HTML check so are excluded here for now
|
||||||
gulp.task(taskName, function () {
|
gulp.task("check-translations-html", function () {
|
||||||
return del([`${downloadDir}/**`]);
|
return gulp.src([`${inDirFrontend}/*.json`]).pipe(checkHtml());
|
||||||
});
|
});
|
||||||
tasks.push(taskName);
|
|
||||||
|
|
||||||
taskName = "check-translations-html";
|
gulp.task("check-all-files-exist", async function () {
|
||||||
gulp.task(taskName, function () {
|
const file = await fs.readFile(srcMeta, { encoding });
|
||||||
return gulp.src(`${downloadDir}/*.json`).pipe(checkHtml());
|
|
||||||
});
|
|
||||||
tasks.push(taskName);
|
|
||||||
|
|
||||||
taskName = "check-all-files-exist";
|
|
||||||
gulp.task(taskName, function () {
|
|
||||||
const file = fs.readFileSync(srcMeta, { encoding });
|
|
||||||
const meta = JSON.parse(file);
|
const meta = JSON.parse(file);
|
||||||
|
const writings = [];
|
||||||
Object.keys(meta).forEach((lang) => {
|
Object.keys(meta).forEach((lang) => {
|
||||||
if (!fs.existsSync(`${inDirFrontend}/${lang}.json`)) {
|
writings.push(
|
||||||
fs.writeFileSync(`${inDirFrontend}/${lang}.json`, JSON.stringify({}));
|
fs.writeFile(`${inDirFrontend}/${lang}.json`, JSON.stringify({}), {
|
||||||
}
|
flag: "wx",
|
||||||
if (!fs.existsSync(`${inDirBackend}/${lang}.json`)) {
|
}),
|
||||||
fs.writeFileSync(`${inDirBackend}/${lang}.json`, JSON.stringify({}));
|
fs.writeFile(`${inDirBackend}/${lang}.json`, JSON.stringify({}), {
|
||||||
}
|
flag: "wx",
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return Promise.resolve();
|
await Promise.allSettled(writings);
|
||||||
});
|
});
|
||||||
tasks.push(taskName);
|
|
||||||
|
|
||||||
taskName = "move-downloaded-translations";
|
|
||||||
gulp.task(taskName, function () {
|
|
||||||
return gulp.src(`${downloadDir}/*.json`).pipe(gulp.dest(inDirFrontend));
|
|
||||||
});
|
|
||||||
tasks.push(taskName);
|
|
||||||
|
|
||||||
taskName = "check-downloaded-translations";
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
taskName,
|
"check-downloaded-translations",
|
||||||
gulp.series(
|
gulp.series("check-translations-html", "check-all-files-exist")
|
||||||
"check-translations-html",
|
|
||||||
"move-downloaded-translations",
|
|
||||||
"check-all-files-exist",
|
|
||||||
"clean-downloaded-translations"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
tasks.push(taskName);
|
|
||||||
|
|
||||||
module.exports = tasks;
|
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
// Tasks to generate entry HTML
|
// Tasks to generate entry HTML
|
||||||
/* eslint-disable import/no-dynamic-require */
|
|
||||||
/* eslint-disable global-require */
|
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
@@ -91,7 +89,9 @@ gulp.task("gen-pages-prod", (done) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("gen-index-app-dev", (done) => {
|
gulp.task("gen-index-app-dev", (done) => {
|
||||||
let latestAppJS, latestCoreJS, latestCustomPanelJS;
|
let latestAppJS;
|
||||||
|
let latestCoreJS;
|
||||||
|
let latestCustomPanelJS;
|
||||||
|
|
||||||
if (env.useWDS()) {
|
if (env.useWDS()) {
|
||||||
latestAppJS = "http://localhost:8000/src/entrypoints/app.ts";
|
latestAppJS = "http://localhost:8000/src/entrypoints/app.ts";
|
||||||
|
170
build-scripts/gulp/fetch-nightly_translations.js
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// Task to download the latest Lokalise translations from the nightly workflow artifacts
|
||||||
|
|
||||||
|
const fs = require("fs/promises");
|
||||||
|
const path = require("path");
|
||||||
|
const process = require("process");
|
||||||
|
const del = require("del");
|
||||||
|
const gulp = require("gulp");
|
||||||
|
const jszip = require("jszip");
|
||||||
|
const tar = require("tar");
|
||||||
|
const { Octokit } = require("@octokit/rest");
|
||||||
|
const { createOAuthDeviceAuth } = require("@octokit/auth-oauth-device");
|
||||||
|
|
||||||
|
const MAX_AGE = 24; // hours
|
||||||
|
const OWNER = "home-assistant";
|
||||||
|
const REPO = "frontend";
|
||||||
|
const WORKFLOW_NAME = "nightly.yaml";
|
||||||
|
const ARTIFACT_NAME = "translations";
|
||||||
|
const CLIENT_ID = "Iv1.3914e28cb27834d1";
|
||||||
|
const EXTRACT_DIR = "translations";
|
||||||
|
const TOKEN_FILE = path.join(EXTRACT_DIR, "token.json");
|
||||||
|
const ARTIFACT_FILE = path.join(EXTRACT_DIR, "artifact.json");
|
||||||
|
|
||||||
|
let allowTokenSetup = false;
|
||||||
|
gulp.task("allow-setup-fetch-nightly-translations", (done) => {
|
||||||
|
allowTokenSetup = true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("fetch-nightly-translations", async function () {
|
||||||
|
// Skip all when environment flag is set (assumes translations are already in place)
|
||||||
|
if (process.env?.SKIP_FETCH_NIGHTLY_TRANSLATIONS) {
|
||||||
|
console.log("Skipping fetch due to environment signal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current translations artifact info if it exists,
|
||||||
|
// and stop if they are not old enough
|
||||||
|
let currentArtifact;
|
||||||
|
try {
|
||||||
|
currentArtifact = JSON.parse(await fs.readFile(ARTIFACT_FILE, "utf-8"));
|
||||||
|
const currentAge =
|
||||||
|
(Date.now() - Date.parse(currentArtifact.created_at)) / 3600000;
|
||||||
|
if (currentAge < MAX_AGE) {
|
||||||
|
console.log(
|
||||||
|
"Keeping current translations (only %s hours old)",
|
||||||
|
currentAge.toFixed(1)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
currentArtifact = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To store file writing promises
|
||||||
|
const createExtractDir = fs.mkdir(EXTRACT_DIR, { recursive: true });
|
||||||
|
const writings = [];
|
||||||
|
|
||||||
|
// Authenticate to GitHub using GitHub action token if it exists,
|
||||||
|
// otherwise look for a saved user token or generate a new one if none
|
||||||
|
let tokenAuth;
|
||||||
|
if (process.env.GITHUB_TOKEN) {
|
||||||
|
tokenAuth = { token: process.env.GITHUB_TOKEN };
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
tokenAuth = JSON.parse(await fs.readFile(TOKEN_FILE, "utf-8"));
|
||||||
|
} catch {
|
||||||
|
if (!allowTokenSetup) {
|
||||||
|
console.log("No token found so build wil continue with English only");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auth = createOAuthDeviceAuth({
|
||||||
|
clientType: "github-app",
|
||||||
|
clientId: CLIENT_ID,
|
||||||
|
onVerification: (verification) => {
|
||||||
|
console.log(
|
||||||
|
"Task needs to authenticate to GitHub to fetch the translations from nightly workflow\n" +
|
||||||
|
"Please go to %s to authorize this task\n" +
|
||||||
|
"\nEnter user code: %s\n\n" +
|
||||||
|
"This code will expire in %s minutes\n" +
|
||||||
|
"Task will automatically continue after authorization and token will be saved for future use",
|
||||||
|
verification.verification_uri,
|
||||||
|
verification.user_code,
|
||||||
|
(verification.expires_in / 60).toFixed(0)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
tokenAuth = await auth({ type: "oauth" });
|
||||||
|
writings.push(
|
||||||
|
createExtractDir.then(
|
||||||
|
fs.writeFile(TOKEN_FILE, JSON.stringify(tokenAuth, null, 2))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate with token and request workflow runs from GitHub
|
||||||
|
console.log("Fetching new translations...");
|
||||||
|
const octokit = new Octokit({
|
||||||
|
userAgent: "Fetch Nightly Translations",
|
||||||
|
auth: tokenAuth.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
const workflowRunsResponse = await octokit.rest.actions.listWorkflowRuns({
|
||||||
|
owner: OWNER,
|
||||||
|
repo: REPO,
|
||||||
|
workflow_id: WORKFLOW_NAME,
|
||||||
|
status: "success",
|
||||||
|
event: "schedule",
|
||||||
|
per_page: 1,
|
||||||
|
exclude_pull_requests: true,
|
||||||
|
});
|
||||||
|
if (workflowRunsResponse.data.total_count === 0) {
|
||||||
|
throw Error("No successful nightly workflow runs found");
|
||||||
|
}
|
||||||
|
const latestNightlyRun = workflowRunsResponse.data.workflow_runs[0];
|
||||||
|
|
||||||
|
// Stop if current is already the latest, otherwise Find the translations artifact
|
||||||
|
if (currentArtifact?.workflow_run.id === latestNightlyRun.id) {
|
||||||
|
console.log("Stopping because current translations are still the latest");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const latestArtifact = (
|
||||||
|
await octokit.actions.listWorkflowRunArtifacts({
|
||||||
|
owner: OWNER,
|
||||||
|
repo: REPO,
|
||||||
|
run_id: latestNightlyRun.id,
|
||||||
|
})
|
||||||
|
).data.artifacts.find((artifact) => artifact.name === ARTIFACT_NAME);
|
||||||
|
if (!latestArtifact) {
|
||||||
|
throw Error("Latest nightly workflow run has no translations artifact");
|
||||||
|
}
|
||||||
|
writings.push(
|
||||||
|
createExtractDir.then(
|
||||||
|
fs.writeFile(ARTIFACT_FILE, JSON.stringify(latestArtifact, null, 2))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove the current translations
|
||||||
|
const deleteCurrent = Promise.all(writings).then(
|
||||||
|
del([`${EXTRACT_DIR}/*`, `!${ARTIFACT_FILE}`, `!${TOKEN_FILE}`])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the download URL and follow the redirect to download (stored as ArrayBuffer)
|
||||||
|
const downloadResponse = await octokit.actions.downloadArtifact({
|
||||||
|
owner: OWNER,
|
||||||
|
repo: REPO,
|
||||||
|
artifact_id: latestArtifact.id,
|
||||||
|
archive_format: "zip",
|
||||||
|
});
|
||||||
|
if (downloadResponse.status !== 200) {
|
||||||
|
throw Error("Failure downloading translations artifact");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artifact is a tarball, but GitHub adds it to a zip file
|
||||||
|
console.log("Unpacking downloaded translations...");
|
||||||
|
const zip = await jszip.loadAsync(downloadResponse.data);
|
||||||
|
await deleteCurrent;
|
||||||
|
const extractStream = zip.file(/.*/)[0].nodeStream().pipe(tar.extract());
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
extractStream.on("close", resolve).on("error", reject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
"setup-and-fetch-nightly-translations",
|
||||||
|
gulp.series(
|
||||||
|
"allow-setup-fetch-nightly-translations",
|
||||||
|
"fetch-nightly-translations"
|
||||||
|
)
|
||||||
|
);
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// Run demo develop mode
|
// Run demo develop mode
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
@@ -41,7 +40,7 @@ gulp.task("gather-gallery-pages", async function gatherPages() {
|
|||||||
}
|
}
|
||||||
processed.add(pageId);
|
processed.add(pageId);
|
||||||
|
|
||||||
const [category, name] = pageId.split("/", 2);
|
const [category] = pageId.split("/", 2);
|
||||||
|
|
||||||
const demoFile = path.resolve(pageDir, `${pageId}.ts`);
|
const demoFile = path.resolve(pageDir, `${pageId}.ts`);
|
||||||
const descriptionFile = path.resolve(pageDir, `${pageId}.markdown`);
|
const descriptionFile = path.resolve(pageDir, `${pageId}.markdown`);
|
||||||
|
@@ -156,3 +156,12 @@ gulp.task("gen-icons-json", (done) => {
|
|||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("gen-dummy-icons-json", (done) => {
|
||||||
|
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||||
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(path.resolve(OUTPUT_DIR, "iconList.json"), "[]");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
@@ -9,6 +9,7 @@ require("./compress.js");
|
|||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
require("./gather-static.js");
|
require("./gather-static.js");
|
||||||
require("./translations.js");
|
require("./translations.js");
|
||||||
|
require("./gen-icons-json.js");
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"develop-hassio",
|
"develop-hassio",
|
||||||
@@ -17,6 +18,7 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "development";
|
process.env.NODE_ENV = "development";
|
||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
|
"gen-dummy-icons-json",
|
||||||
"gen-index-hassio-dev",
|
"gen-index-hassio-dev",
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
"copy-translations-supervisor",
|
"copy-translations-supervisor",
|
||||||
@@ -33,6 +35,7 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "production";
|
process.env.NODE_ENV = "production";
|
||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
|
"gen-dummy-icons-json",
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
"copy-translations-supervisor",
|
"copy-translations-supervisor",
|
||||||
"build-locale-data",
|
"build-locale-data",
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
|
|
||||||
const del = require("del");
|
const del = require("del");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
|
@@ -5,9 +5,9 @@ const rollup = require("rollup");
|
|||||||
const handler = require("serve-handler");
|
const handler = require("serve-handler");
|
||||||
const http = require("http");
|
const http = require("http");
|
||||||
const log = require("fancy-log");
|
const log = require("fancy-log");
|
||||||
|
const open = require("open");
|
||||||
const rollupConfig = require("../rollup");
|
const rollupConfig = require("../rollup");
|
||||||
const paths = require("../paths");
|
const paths = require("../paths");
|
||||||
const open = require("open");
|
|
||||||
|
|
||||||
const bothBuilds = (createConfigFunc, params) =>
|
const bothBuilds = (createConfigFunc, params) =>
|
||||||
gulp.series(
|
gulp.series(
|
||||||
@@ -30,11 +30,11 @@ const bothBuilds = (createConfigFunc, params) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
function createServer(serveOptions) {
|
function createServer(serveOptions) {
|
||||||
const server = http.createServer((request, response) => {
|
const server = http.createServer((request, response) =>
|
||||||
return handler(request, response, {
|
handler(request, response, {
|
||||||
public: serveOptions.root,
|
public: serveOptions.root,
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
server.listen(
|
server.listen(
|
||||||
serveOptions.port,
|
serveOptions.port,
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
// Generate service worker.
|
// Generate service worker.
|
||||||
// Based on manifest, create a file with the content as service_worker.js
|
// Based on manifest, create a file with the content as service_worker.js
|
||||||
/* eslint-disable import/no-dynamic-require */
|
|
||||||
/* eslint-disable global-require */
|
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
|
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const del = require("del");
|
const del = require("del");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
@@ -15,6 +13,8 @@ const { mapFiles } = require("../util");
|
|||||||
const env = require("../env");
|
const env = require("../env");
|
||||||
const paths = require("../paths");
|
const paths = require("../paths");
|
||||||
|
|
||||||
|
require("./fetch-nightly_translations");
|
||||||
|
|
||||||
const inFrontendDir = "translations/frontend";
|
const inFrontendDir = "translations/frontend";
|
||||||
const inBackendDir = "translations/backend";
|
const inBackendDir = "translations/backend";
|
||||||
const workDir = "build/translations";
|
const workDir = "build/translations";
|
||||||
@@ -23,10 +23,13 @@ const coreDir = workDir + "/core";
|
|||||||
const outDir = workDir + "/output";
|
const outDir = workDir + "/output";
|
||||||
let mergeBackend = false;
|
let mergeBackend = false;
|
||||||
|
|
||||||
gulp.task("translations-enable-merge-backend", (done) => {
|
gulp.task(
|
||||||
mergeBackend = true;
|
"translations-enable-merge-backend",
|
||||||
done();
|
gulp.parallel((done) => {
|
||||||
});
|
mergeBackend = true;
|
||||||
|
done();
|
||||||
|
}, "allow-setup-fetch-nightly-translations")
|
||||||
|
);
|
||||||
|
|
||||||
// Panel translations which should be split from the core translations.
|
// Panel translations which should be split from the core translations.
|
||||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||||
@@ -170,17 +173,24 @@ gulp.task("build-master-translation", () => {
|
|||||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||||
.pipe(
|
.pipe(
|
||||||
merge({
|
merge({
|
||||||
fileName: "translationMaster.json",
|
fileName: "en.json",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(fullDir));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("build-merged-translations", () =>
|
gulp.task("build-merged-translations", () =>
|
||||||
gulp
|
gulp
|
||||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
.src(
|
||||||
allowEmpty: true,
|
[
|
||||||
})
|
inFrontendDir + "/*.json",
|
||||||
|
"!" + inFrontendDir + "/en.json",
|
||||||
|
workDir + "/test.json",
|
||||||
|
],
|
||||||
|
{
|
||||||
|
allowEmpty: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||||
.pipe(
|
.pipe(
|
||||||
flatmap((stream, file) => {
|
flatmap((stream, file) => {
|
||||||
@@ -193,7 +203,7 @@ gulp.task("build-merged-translations", () =>
|
|||||||
// than a base translation + region.
|
// than a base translation + region.
|
||||||
const tr = path.basename(file.history[0], ".json");
|
const tr = path.basename(file.history[0], ".json");
|
||||||
const subtags = tr.split("-");
|
const subtags = tr.split("-");
|
||||||
const src = [workDir + "/translationMaster.json"];
|
const src = [fullDir + "/en.json"];
|
||||||
for (let i = 1; i <= subtags.length; i++) {
|
for (let i = 1; i <= subtags.length; i++) {
|
||||||
const lang = subtags.slice(0, i).join("-");
|
const lang = subtags.slice(0, i).join("-");
|
||||||
if (lang === "test") {
|
if (lang === "test") {
|
||||||
@@ -378,7 +388,6 @@ gulp.task("build-translation-write-metadata", () =>
|
|||||||
if (value.nativeName) {
|
if (value.nativeName) {
|
||||||
newData[key] = value;
|
newData[key] = value;
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn(
|
console.warn(
|
||||||
`Skipping language ${key}. Native name was not translated.`
|
`Skipping language ${key}. Native name was not translated.`
|
||||||
);
|
);
|
||||||
@@ -411,8 +420,10 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"build-translations",
|
"build-translations",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
"clean-translations",
|
gulp.parallel(
|
||||||
"ensure-translations-build-dir",
|
"fetch-nightly-translations",
|
||||||
|
gulp.series("clean-translations", "ensure-translations-build-dir")
|
||||||
|
),
|
||||||
"create-translations",
|
"create-translations",
|
||||||
"build-translation-fingerprints",
|
"build-translation-fingerprints",
|
||||||
"build-translation-write-metadata"
|
"build-translation-write-metadata"
|
||||||
@@ -422,8 +433,10 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
"clean-translations",
|
gulp.parallel(
|
||||||
"ensure-translations-build-dir",
|
"fetch-nightly-translations",
|
||||||
|
gulp.series("clean-translations", "ensure-translations-build-dir")
|
||||||
|
),
|
||||||
"build-master-translation",
|
"build-master-translation",
|
||||||
"build-merged-translations",
|
"build-merged-translations",
|
||||||
"build-translation-fragment-supervisor",
|
"build-translation-fragment-supervisor",
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
// Tasks to run webpack.
|
// Tasks to run webpack.
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
@@ -69,7 +68,6 @@ const doneHandler = (done) => (err, stats) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(stats.toString("minimal"));
|
console.log(stats.toString("minimal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@@ -1 +1,30 @@
|
|||||||
[]
|
[
|
||||||
|
{
|
||||||
|
"path": "M20,20H7A2,2 0 0,1 5,18V8.94L2.23,5.64C2.09,5.47 2,5.24 2,5A1,1 0 0,1 3,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20M8.5,7A0.5,0.5 0 0,0 8,7.5V8.5A0.5,0.5 0 0,0 8.5,9H18.5A0.5,0.5 0 0,0 19,8.5V7.5A0.5,0.5 0 0,0 18.5,7H8.5M8.5,11A0.5,0.5 0 0,0 8,11.5V12.5A0.5,0.5 0 0,0 8.5,13H18.5A0.5,0.5 0 0,0 19,12.5V11.5A0.5,0.5 0 0,0 18.5,11H8.5M8.5,15A0.5,0.5 0 0,0 8,15.5V16.5A0.5,0.5 0 0,0 8.5,17H13.5A0.5,0.5 0 0,0 14,16.5V15.5A0.5,0.5 0 0,0 13.5,15H8.5Z",
|
||||||
|
"name": "android-messages"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "M4,6H2V20A2,2 0 0,0 4,22H18V20H4V6M20,2H8A2,2 0 0,0 6,4V16A2,2 0 0,0 8,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2M20,12L17.5,10.5L15,12V4H20V12Z",
|
||||||
|
"name": "book-variant-multiple"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "M21,14H3V4H21M21,2H3C1.89,2 1,2.89 1,4V16A2,2 0 0,0 3,18H10L8,21V22H16V21L14,18H21A2,2 0 0,0 23,16V4C23,2.89 22.1,2 21,2Z",
|
||||||
|
"name": "desktop-mac"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "M21,14V4H3V14H21M21,2A2,2 0 0,1 23,4V16A2,2 0 0,1 21,18H14L16,21V22H8V21L10,18H3C1.89,18 1,17.1 1,16V4C1,2.89 1.89,2 3,2H21M4,5H15V10H4V5M16,5H20V7H16V5M20,8V13H16V8H20M4,11H9V13H4V11M10,11H15V13H10V11Z",
|
||||||
|
"name": "desktop-mac-dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "M22,24L16.75,19L17.38,21H4.5A2.5,2.5 0 0,1 2,18.5V3.5A2.5,2.5 0 0,1 4.5,1H19.5A2.5,2.5 0 0,1 22,3.5V24M12,6.8C9.32,6.8 7.44,7.95 7.44,7.95C8.47,7.03 10.27,6.5 10.27,6.5L10.1,6.33C8.41,6.36 6.88,7.53 6.88,7.53C5.16,11.12 5.27,14.22 5.27,14.22C6.67,16.03 8.75,15.9 8.75,15.9L9.46,15C8.21,14.73 7.42,13.62 7.42,13.62C7.42,13.62 9.3,14.9 12,14.9C14.7,14.9 16.58,13.62 16.58,13.62C16.58,13.62 15.79,14.73 14.54,15L15.25,15.9C15.25,15.9 17.33,16.03 18.73,14.22C18.73,14.22 18.84,11.12 17.12,7.53C17.12,7.53 15.59,6.36 13.9,6.33L13.73,6.5C13.73,6.5 15.53,7.03 16.56,7.95C16.56,7.95 14.68,6.8 12,6.8M9.93,10.59C10.58,10.59 11.11,11.16 11.1,11.86C11.1,12.55 10.58,13.13 9.93,13.13C9.29,13.13 8.77,12.55 8.77,11.86C8.77,11.16 9.28,10.59 9.93,10.59M14.1,10.59C14.75,10.59 15.27,11.16 15.27,11.86C15.27,12.55 14.75,13.13 14.1,13.13C13.46,13.13 12.94,12.55 12.94,11.86C12.94,11.16 13.45,10.59 14.1,10.59Z",
|
||||||
|
"name": "discord"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "M8.06,7.78C7.5,7.78 7.17,7.73 7.08,7.64L6.66,13.73C7.19,14.05 7.88,14.3 8.72,14.5C9.56,14.71 10.78,14.77 12.38,14.67C13.97,14.58 15.63,14.23 17.34,13.64L16.55,4.22C15.67,5.09 14.38,5.91 12.66,6.66C11.13,7.31 9.81,7.69 8.72,7.78H8.06M7.97,5.34C7.28,5.94 7,6.34 7.13,6.56C7.22,6.78 7.7,6.84 8.58,6.75C9.67,6.66 10.91,6.31 12.28,5.72C13.22,5.31 14.03,4.88 14.72,4.41C15.41,3.94 15.88,3.55 16.13,3.23C16.38,2.92 16.47,2.7 16.41,2.58C16.34,2.42 16.03,2.34 15.47,2.34C14.34,2.34 12.94,2.7 11.25,3.42C9.81,4.05 8.72,4.69 7.97,5.34M17.34,2.2C17.41,2.33 17.44,2.47 17.44,2.63L18.61,17C18.61,18.73 18,20.09 16.83,21.07C15.64,22.05 14.03,22.55 12,22.55C10,22.55 8.4,22.04 7.2,21C6,20 5.39,18.64 5.39,16.92L6.09,6.47C6.09,6.22 6.2,5.94 6.42,5.63C6.64,5.31 6.84,5.06 7.03,4.88L7.36,4.59C8.33,3.78 9.5,3.08 10.88,2.5C11.81,2.08 12.73,1.77 13.62,1.57C14.5,1.37 15.3,1.3 16,1.38C16.71,1.46 17.16,1.73 17.34,2.2Z",
|
||||||
|
"name": "google-home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "M19.25,19H4.75V3H19.25M14,22H10V21H14M18,0H6A3,3 0 0,0 3,3V21A3,3 0 0,0 6,24H18A3,3 0 0,0 21,21V3A3,3 0 0,0 18,0Z",
|
||||||
|
"name": "tablet-android"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
@@ -81,13 +81,13 @@ module.exports = function (opts = {}) {
|
|||||||
opts.workerRegexp.flags
|
opts.workerRegexp.flags
|
||||||
);
|
);
|
||||||
if (!workerRegexp.test(code)) {
|
if (!workerRegexp.test(code)) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ms = new MagicString(code);
|
const ms = new MagicString(code);
|
||||||
// Reset the regexp
|
// Reset the regexp
|
||||||
workerRegexp.lastIndex = 0;
|
workerRegexp.lastIndex = 0;
|
||||||
while (true) {
|
for (;;) {
|
||||||
const match = workerRegexp.exec(code);
|
const match = workerRegexp.exec(code);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
break;
|
break;
|
||||||
@@ -98,6 +98,7 @@ module.exports = function (opts = {}) {
|
|||||||
// Parse the optional options object
|
// Parse the optional options object
|
||||||
if (match[3] && match[3].length > 0) {
|
if (match[3] && match[3].length > 0) {
|
||||||
// FIXME: ooooof!
|
// FIXME: ooooof!
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||||
optionsObject = new Function(`return ${match[3].slice(1)};`)();
|
optionsObject = new Function(`return ${match[3].slice(1)};`)();
|
||||||
}
|
}
|
||||||
delete optionsObject.type;
|
delete optionsObject.type;
|
||||||
@@ -110,12 +111,14 @@ module.exports = function (opts = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find worker file and store it as a chunk with ID prefixed for our loader
|
// Find worker file and store it as a chunk with ID prefixed for our loader
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const resolvedWorkerFile = (await this.resolve(workerFile, id)).id;
|
const resolvedWorkerFile = (await this.resolve(workerFile, id)).id;
|
||||||
let chunkRefId;
|
let chunkRefId;
|
||||||
if (resolvedWorkerFile in refIds) {
|
if (resolvedWorkerFile in refIds) {
|
||||||
chunkRefId = refIds[resolvedWorkerFile];
|
chunkRefId = refIds[resolvedWorkerFile];
|
||||||
} else {
|
} else {
|
||||||
this.addWatchFile(resolvedWorkerFile);
|
this.addWatchFile(resolvedWorkerFile);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const source = await getBundledWorker(
|
const source = await getBundledWorker(
|
||||||
resolvedWorkerFile,
|
resolvedWorkerFile,
|
||||||
rollupOptions
|
rollupOptions
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const commonjs = require("@rollup/plugin-commonjs");
|
const commonjs = require("@rollup/plugin-commonjs");
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
@@ -76,7 +75,7 @@ const createWebpackConfig = ({
|
|||||||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new WebpackBar({ fancy: !isProdBuild }),
|
!isStatsBuild && new WebpackBar({ fancy: !isProdBuild }),
|
||||||
new WebpackManifestPlugin({
|
new WebpackManifestPlugin({
|
||||||
// Only include the JS of entrypoints
|
// Only include the JS of entrypoints
|
||||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||||
@@ -103,7 +102,6 @@ const createWebpackConfig = ({
|
|||||||
? path.resolve(context, resource)
|
? path.resolve(context, resource)
|
||||||
: require.resolve(resource);
|
: require.resolve(resource);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(
|
console.error(
|
||||||
"Error in Home Assistant ignore plugin",
|
"Error in Home Assistant ignore plugin",
|
||||||
resource,
|
resource,
|
||||||
|
9
cast/public/_redirects
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# These redirects are handled by Netlify
|
||||||
|
#
|
||||||
|
|
||||||
|
# Some custom cards are not prefixing the instance URL when fetching data
|
||||||
|
# and can end up fetching the data from the Cast domain instead of HA.
|
||||||
|
# This will make sure that some common ones are replaced with a placeholder.
|
||||||
|
/api/camera_proxy/* /images/google-nest-hub.png
|
||||||
|
/api/camera_proxy_stream/* /images/google-nest-hub.png
|
||||||
|
/api/media_player_proxy/* /images/google-nest-hub.png
|
@@ -213,7 +213,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Google Chrome (all platforms except iOS)</li>
|
<li>Google Chrome (all platforms except iOS)</li>
|
||||||
<li>Microsoft Edge (all platforms)</li>
|
<li>Microsoft Edge (all platforms except iOS)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -88,7 +88,7 @@ class HcCast extends LitElement {
|
|||||||
>
|
>
|
||||||
${(this.lovelaceConfig
|
${(this.lovelaceConfig
|
||||||
? this.lovelaceConfig.views
|
? this.lovelaceConfig.views
|
||||||
: [generateDefaultViewConfig([], [], [], {}, () => "")]
|
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
|
||||||
).map(
|
).map(
|
||||||
(view, idx) => html`
|
(view, idx) => html`
|
||||||
<paper-icon-item
|
<paper-icon-item
|
||||||
|
@@ -44,7 +44,7 @@ class HcLayout extends LitElement {
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<a href="./faq.html">Frequently Asked Questions</a> – Found a bug?
|
<a href="./faq.html">Frequently Asked Questions</a> – Found a bug?
|
||||||
<a
|
<a
|
||||||
href="https://github.com/home-assistant/home-assistant-polymer/issues"
|
href="https://github.com/home-assistant/frontend/issues"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>Let us know!</a
|
>Let us know!</a
|
||||||
>
|
>
|
||||||
|
@@ -508,7 +508,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
origin_addresses: ["XYZ"],
|
origin_addresses: ["XYZ"],
|
||||||
status: "OK",
|
status: "OK",
|
||||||
mode: "driving",
|
mode: "driving",
|
||||||
units: "imperial",
|
units: "us_customary",
|
||||||
duration_in_traffic: "41 mins",
|
duration_in_traffic: "41 mins",
|
||||||
duration: "44 mins",
|
duration: "44 mins",
|
||||||
distance: "34.3 mi",
|
distance: "34.3 mi",
|
||||||
@@ -527,7 +527,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
origin_addresses: ["XYZ"],
|
origin_addresses: ["XYZ"],
|
||||||
status: "OK",
|
status: "OK",
|
||||||
mode: "driving",
|
mode: "driving",
|
||||||
units: "imperial",
|
units: "us_customary",
|
||||||
duration_in_traffic: "37 mins",
|
duration_in_traffic: "37 mins",
|
||||||
duration: "37 mins",
|
duration: "37 mins",
|
||||||
distance: "30.2 mi",
|
distance: "30.2 mi",
|
||||||
|
@@ -1196,7 +1196,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
left: "15%",
|
left: "15%",
|
||||||
},
|
},
|
||||||
type: "state-icon",
|
type: "state-icon",
|
||||||
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefix: "Kitchen: ",
|
prefix: "Kitchen: ",
|
||||||
@@ -1206,7 +1206,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
top: "89%",
|
top: "89%",
|
||||||
left: "32%",
|
left: "32%",
|
||||||
},
|
},
|
||||||
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
@@ -1215,7 +1215,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
left: "60%",
|
left: "60%",
|
||||||
},
|
},
|
||||||
type: "state-icon",
|
type: "state-icon",
|
||||||
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefix: "Bathroom: ",
|
prefix: "Bathroom: ",
|
||||||
@@ -1225,7 +1225,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
top: "89%",
|
top: "89%",
|
||||||
left: "77%",
|
left: "77%",
|
||||||
},
|
},
|
||||||
entity: "binary_sensor.water_leak_sensor_158d00026e26dc",
|
entity: "binary_sensor.water_leak_sensor_158d0002338651",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
type: "picture-elements",
|
type: "picture-elements",
|
||||||
|
@@ -59,7 +59,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
radius: 50,
|
radius: 50,
|
||||||
friendly_name: "Skolan",
|
friendly_name: "School",
|
||||||
icon: "mdi:school",
|
icon: "mdi:school",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -137,7 +137,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
state: "73",
|
state: "73",
|
||||||
attributes: {
|
attributes: {
|
||||||
unit_of_measurement: "%",
|
unit_of_measurement: "%",
|
||||||
friendly_name: "oskar batteri",
|
friendly_name: "Oskar battery",
|
||||||
device_class: "battery",
|
device_class: "battery",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -146,7 +146,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
state: "88",
|
state: "88",
|
||||||
attributes: {
|
attributes: {
|
||||||
unit_of_measurement: "%",
|
unit_of_measurement: "%",
|
||||||
friendly_name: "bella batteri",
|
friendly_name: "Bella battery",
|
||||||
device_class: "battery",
|
device_class: "battery",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -154,7 +154,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
entity_id: "binary_sensor.unifi_camera",
|
entity_id: "binary_sensor.unifi_camera",
|
||||||
state: "off",
|
state: "off",
|
||||||
attributes: {
|
attributes: {
|
||||||
friendly_name: "R\u00f6relsesensor kamera",
|
friendly_name: "Motion sensor camera",
|
||||||
icon: "mdi:walk",
|
icon: "mdi:walk",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -707,7 +707,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
cloudiness: 25,
|
cloudiness: 25,
|
||||||
friendly_name: "V\u00e4der",
|
friendly_name: "Weather",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"binary_sensor.ubiquiti_switch": {
|
"binary_sensor.ubiquiti_switch": {
|
||||||
@@ -731,7 +731,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
round_trip_time_max: "0.626",
|
round_trip_time_max: "0.626",
|
||||||
round_trip_time_mdev: "",
|
round_trip_time_mdev: "",
|
||||||
round_trip_time_min: "0.358",
|
round_trip_time_min: "0.358",
|
||||||
friendly_name: "Entr\u00e9 kamera",
|
friendly_name: "Entrance camera",
|
||||||
device_class: "connectivity",
|
device_class: "connectivity",
|
||||||
icon: "mdi:cctv",
|
icon: "mdi:cctv",
|
||||||
},
|
},
|
||||||
@@ -797,7 +797,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
battery_level: 34,
|
battery_level: 34,
|
||||||
on: true,
|
on: true,
|
||||||
friendly_name: "altan_motion_sensor",
|
friendly_name: "Porch motion sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -807,7 +807,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
battery_level: 88,
|
battery_level: 88,
|
||||||
on: true,
|
on: true,
|
||||||
friendly_name: "Altand\u00f6rren sensor",
|
friendly_name: "Back door sensor",
|
||||||
device_class: "opening",
|
device_class: "opening",
|
||||||
icon: "mdi:door",
|
icon: "mdi:door",
|
||||||
},
|
},
|
||||||
@@ -818,7 +818,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
battery_level: 74,
|
battery_level: 74,
|
||||||
on: true,
|
on: true,
|
||||||
friendly_name: "badrumssensor",
|
friendly_name: "Bathroom motion sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -829,7 +829,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
battery_level: 47,
|
battery_level: 47,
|
||||||
on: true,
|
on: true,
|
||||||
dark: true,
|
dark: true,
|
||||||
friendly_name: "R\u00f6relsesensor k\u00e4llaren 1",
|
friendly_name: "Basement motion sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
icon: "mdi:walk",
|
icon: "mdi:walk",
|
||||||
},
|
},
|
||||||
@@ -841,7 +841,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
battery_level: 60,
|
battery_level: 60,
|
||||||
on: true,
|
on: true,
|
||||||
dark: true,
|
dark: true,
|
||||||
friendly_name: "R\u00f6relsesensor tv\u00e4ttstugan",
|
friendly_name: "Laundy room motion sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
icon: "mdi:walk",
|
icon: "mdi:walk",
|
||||||
},
|
},
|
||||||
@@ -863,7 +863,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
battery_level: 60,
|
battery_level: 60,
|
||||||
on: true,
|
on: true,
|
||||||
friendly_name: "R\u00f6relsesensor skafferiet",
|
friendly_name: "Pantry motion sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
icon: "mdi:walk",
|
icon: "mdi:walk",
|
||||||
},
|
},
|
||||||
@@ -875,7 +875,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
battery_level: 60,
|
battery_level: 60,
|
||||||
on: true,
|
on: true,
|
||||||
dark: true,
|
dark: true,
|
||||||
friendly_name: "R\u00f6relsesensor k\u00e4llaren 2",
|
friendly_name: "Stair motion sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
icon: "mdi:walk",
|
icon: "mdi:walk",
|
||||||
},
|
},
|
||||||
@@ -887,7 +887,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
battery_level: 47,
|
battery_level: 47,
|
||||||
on: true,
|
on: true,
|
||||||
dark: true,
|
dark: true,
|
||||||
friendly_name: "B\u00e4nksensor",
|
friendly_name: "Bench sensor",
|
||||||
device_class: "motion",
|
device_class: "motion",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -277,7 +277,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
],
|
],
|
||||||
show_header_toggle: false,
|
show_header_toggle: false,
|
||||||
type: "entities",
|
type: "entities",
|
||||||
title: "Bandbredd",
|
title: "Bandwidth",
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// title: "Updater",
|
// title: "Updater",
|
||||||
|
@@ -138,7 +138,7 @@ if (!window.cardTools) {
|
|||||||
return cardTools.createThing("row", config);
|
return cardTools.createThing("row", config);
|
||||||
|
|
||||||
const domain = config.entity.split(".", 1)[0];
|
const domain = config.entity.split(".", 1)[0];
|
||||||
Object.assign(config, { type: DEFAULT_ROWS[domain] || "text" });
|
Object.assign(config, { type: DEFAULT_ROWS[domain] || "simple" });
|
||||||
return cardTools.createThing("entity-row", config);
|
return cardTools.createThing("entity-row", config);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../../src/resources/compatibility";
|
|
||||||
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
||||||
import { navigate } from "../../src/common/navigate";
|
import { navigate } from "../../src/common/navigate";
|
||||||
import {
|
import {
|
||||||
@@ -7,22 +6,25 @@ import {
|
|||||||
provideHass,
|
provideHass,
|
||||||
} from "../../src/fake_data/provide_hass";
|
} from "../../src/fake_data/provide_hass";
|
||||||
import { HomeAssistantAppEl } from "../../src/layouts/home-assistant";
|
import { HomeAssistantAppEl } from "../../src/layouts/home-assistant";
|
||||||
|
import "../../src/resources/compatibility";
|
||||||
import { HomeAssistant } from "../../src/types";
|
import { HomeAssistant } from "../../src/types";
|
||||||
import { selectedDemoConfig } from "./configs/demo-configs";
|
import { selectedDemoConfig } from "./configs/demo-configs";
|
||||||
import { mockAuth } from "./stubs/auth";
|
import { mockAuth } from "./stubs/auth";
|
||||||
|
import { mockConfigEntries } from "./stubs/config_entries";
|
||||||
|
import { mockEnergy } from "./stubs/energy";
|
||||||
|
import { energyEntities } from "./stubs/entities";
|
||||||
|
import { mockEntityRegistry } from "./stubs/entity_registry";
|
||||||
import { mockEvents } from "./stubs/events";
|
import { mockEvents } from "./stubs/events";
|
||||||
import { mockFrontend } from "./stubs/frontend";
|
import { mockFrontend } from "./stubs/frontend";
|
||||||
import { mockHistory } from "./stubs/history";
|
import { mockHistory } from "./stubs/history";
|
||||||
import { mockLovelace } from "./stubs/lovelace";
|
import { mockLovelace } from "./stubs/lovelace";
|
||||||
import { mockMediaPlayer } from "./stubs/media_player";
|
import { mockMediaPlayer } from "./stubs/media_player";
|
||||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||||
|
import { mockRecorder } from "./stubs/recorder";
|
||||||
import { mockShoppingList } from "./stubs/shopping_list";
|
import { mockShoppingList } from "./stubs/shopping_list";
|
||||||
import { mockSystemLog } from "./stubs/system_log";
|
import { mockSystemLog } from "./stubs/system_log";
|
||||||
import { mockTemplate } from "./stubs/template";
|
import { mockTemplate } from "./stubs/template";
|
||||||
import { mockTranslations } from "./stubs/translations";
|
import { mockTranslations } from "./stubs/translations";
|
||||||
import { mockEnergy } from "./stubs/energy";
|
|
||||||
import { mockConfig } from "./stubs/config";
|
|
||||||
import { energyEntities } from "./stubs/entities";
|
|
||||||
|
|
||||||
class HaDemo extends HomeAssistantAppEl {
|
class HaDemo extends HomeAssistantAppEl {
|
||||||
protected async _initializeHass() {
|
protected async _initializeHass() {
|
||||||
@@ -44,6 +46,7 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
mockAuth(hass);
|
mockAuth(hass);
|
||||||
mockTranslations(hass);
|
mockTranslations(hass);
|
||||||
mockHistory(hass);
|
mockHistory(hass);
|
||||||
|
mockRecorder(hass);
|
||||||
mockShoppingList(hass);
|
mockShoppingList(hass);
|
||||||
mockSystemLog(hass);
|
mockSystemLog(hass);
|
||||||
mockTemplate(hass);
|
mockTemplate(hass);
|
||||||
@@ -51,8 +54,40 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
mockMediaPlayer(hass);
|
mockMediaPlayer(hass);
|
||||||
mockFrontend(hass);
|
mockFrontend(hass);
|
||||||
mockEnergy(hass);
|
mockEnergy(hass);
|
||||||
mockConfig(hass);
|
|
||||||
mockPersistentNotification(hass);
|
mockPersistentNotification(hass);
|
||||||
|
mockConfigEntries(hass);
|
||||||
|
mockEntityRegistry(hass, [
|
||||||
|
{
|
||||||
|
config_entry_id: "co2signal",
|
||||||
|
device_id: "co2signal",
|
||||||
|
area_id: null,
|
||||||
|
disabled_by: null,
|
||||||
|
entity_id: "sensor.co2_intensity",
|
||||||
|
id: "sensor.co2_intensity",
|
||||||
|
name: null,
|
||||||
|
icon: null,
|
||||||
|
platform: "co2signal",
|
||||||
|
hidden_by: null,
|
||||||
|
entity_category: null,
|
||||||
|
has_entity_name: false,
|
||||||
|
unique_id: "co2_intensity",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config_entry_id: "co2signal",
|
||||||
|
device_id: "co2signal",
|
||||||
|
area_id: null,
|
||||||
|
disabled_by: null,
|
||||||
|
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||||
|
id: "sensor.co2_intensity",
|
||||||
|
name: null,
|
||||||
|
icon: null,
|
||||||
|
platform: "co2signal",
|
||||||
|
hidden_by: null,
|
||||||
|
entity_category: null,
|
||||||
|
has_entity_name: false,
|
||||||
|
unique_id: "grid_fossil_fuel_percentage",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
hass.addEntities(energyEntities());
|
hass.addEntities(energyEntities());
|
||||||
|
|
||||||
@@ -87,3 +122,9 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-demo", HaDemo);
|
customElements.define("ha-demo", HaDemo);
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-demo": HaDemo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
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",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
};
|
|
20
demo/src/stubs/config_entries.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockConfigEntries = (hass: MockHomeAssistant) => {
|
||||||
|
hass.mockWS("config_entries/get", () => [
|
||||||
|
{
|
||||||
|
entry_id: "co2signal",
|
||||||
|
domain: "co2signal",
|
||||||
|
title: "CO2 Signal",
|
||||||
|
source: "user",
|
||||||
|
state: "loaded",
|
||||||
|
supports_options: false,
|
||||||
|
supports_remove_device: false,
|
||||||
|
supports_unload: true,
|
||||||
|
pref_disable_new_entities: false,
|
||||||
|
pref_disable_polling: false,
|
||||||
|
disabled_by: null,
|
||||||
|
reason: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
@@ -1,90 +1,101 @@
|
|||||||
import { format, startOfToday, startOfTomorrow } from "date-fns/esm";
|
import { format, startOfToday, startOfTomorrow } from "date-fns/esm";
|
||||||
import { EnergySolarForecasts } from "../../../src/data/energy";
|
import {
|
||||||
|
EnergyInfo,
|
||||||
|
EnergyPreferences,
|
||||||
|
EnergySolarForecasts,
|
||||||
|
FossilEnergyConsumption,
|
||||||
|
} from "../../../src/data/energy";
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||||
hass.mockWS("energy/get_prefs", () => ({
|
hass.mockWS(
|
||||||
energy_sources: [
|
"energy/get_prefs",
|
||||||
{
|
(): EnergyPreferences => ({
|
||||||
type: "grid",
|
energy_sources: [
|
||||||
flow_from: [
|
{
|
||||||
{
|
type: "grid",
|
||||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
flow_from: [
|
||||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
{
|
||||||
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||||
entity_energy_price: null,
|
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||||
number_energy_price: null,
|
entity_energy_price: null,
|
||||||
},
|
number_energy_price: null,
|
||||||
{
|
},
|
||||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
{
|
||||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||||
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||||
entity_energy_price: null,
|
entity_energy_price: null,
|
||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
flow_to: [
|
flow_to: [
|
||||||
{
|
{
|
||||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||||
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
stat_compensation:
|
||||||
entity_energy_to: "sensor.energy_production_tarif_1",
|
"sensor.energy_production_tarif_1_compensation",
|
||||||
entity_energy_price: null,
|
entity_energy_price: null,
|
||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||||
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
stat_compensation:
|
||||||
entity_energy_to: "sensor.energy_production_tarif_2",
|
"sensor.energy_production_tarif_2_compensation",
|
||||||
entity_energy_price: null,
|
entity_energy_price: null,
|
||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cost_adjustment_day: 0,
|
cost_adjustment_day: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "solar",
|
type: "solar",
|
||||||
stat_energy_from: "sensor.solar_production",
|
stat_energy_from: "sensor.solar_production",
|
||||||
config_entry_solar_forecast: ["solar_forecast"],
|
config_entry_solar_forecast: ["solar_forecast"],
|
||||||
},
|
},
|
||||||
/* {
|
/* {
|
||||||
type: "battery",
|
type: "battery",
|
||||||
stat_energy_from: "sensor.battery_output",
|
stat_energy_from: "sensor.battery_output",
|
||||||
stat_energy_to: "sensor.battery_input",
|
stat_energy_to: "sensor.battery_input",
|
||||||
}, */
|
}, */
|
||||||
{
|
{
|
||||||
type: "gas",
|
type: "gas",
|
||||||
stat_energy_from: "sensor.energy_gas",
|
stat_energy_from: "sensor.energy_gas",
|
||||||
stat_cost: "sensor.energy_gas_cost",
|
stat_cost: "sensor.energy_gas_cost",
|
||||||
entity_energy_from: "sensor.energy_gas",
|
entity_energy_price: null,
|
||||||
entity_energy_price: null,
|
number_energy_price: null,
|
||||||
number_energy_price: null,
|
},
|
||||||
},
|
],
|
||||||
],
|
device_consumption: [
|
||||||
device_consumption: [
|
{
|
||||||
{
|
stat_consumption: "sensor.energy_car",
|
||||||
stat_consumption: "sensor.energy_car",
|
},
|
||||||
},
|
{
|
||||||
{
|
stat_consumption: "sensor.energy_ac",
|
||||||
stat_consumption: "sensor.energy_ac",
|
},
|
||||||
},
|
{
|
||||||
{
|
stat_consumption: "sensor.energy_washing_machine",
|
||||||
stat_consumption: "sensor.energy_washing_machine",
|
},
|
||||||
},
|
{
|
||||||
{
|
stat_consumption: "sensor.energy_dryer",
|
||||||
stat_consumption: "sensor.energy_dryer",
|
},
|
||||||
},
|
{
|
||||||
{
|
stat_consumption: "sensor.energy_heat_pump",
|
||||||
stat_consumption: "sensor.energy_heat_pump",
|
},
|
||||||
},
|
{
|
||||||
{
|
stat_consumption: "sensor.energy_boiler",
|
||||||
stat_consumption: "sensor.energy_boiler",
|
},
|
||||||
},
|
],
|
||||||
],
|
})
|
||||||
}));
|
);
|
||||||
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
hass.mockWS(
|
||||||
hass.mockWS("energy/fossil_energy_consumption", ({ period }) => ({
|
"energy/info",
|
||||||
start: period === "month" ? 250 : period === "day" ? 10 : 2,
|
(): EnergyInfo => ({ cost_sensors: {}, solar_forecast_domains: [] })
|
||||||
}));
|
);
|
||||||
|
hass.mockWS(
|
||||||
|
"energy/fossil_energy_consumption",
|
||||||
|
({ period }): FossilEnergyConsumption => ({
|
||||||
|
start: period === "month" ? 250 : period === "day" ? 10 : 2,
|
||||||
|
})
|
||||||
|
);
|
||||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||||
hass.mockWS(
|
hass.mockWS(
|
||||||
|
@@ -4,4 +4,6 @@ import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|||||||
export const mockEntityRegistry = (
|
export const mockEntityRegistry = (
|
||||||
hass: MockHomeAssistant,
|
hass: MockHomeAssistant,
|
||||||
data: EntityRegistryEntry[] = []
|
data: EntityRegistryEntry[] = []
|
||||||
) => hass.mockWS("config/entity_registry/list", () => data);
|
) => {
|
||||||
|
hass.mockWS("config/entity_registry/list", () => data);
|
||||||
|
};
|
||||||
|
@@ -1,12 +1,4 @@
|
|||||||
import {
|
|
||||||
addDays,
|
|
||||||
addHours,
|
|
||||||
addMonths,
|
|
||||||
differenceInHours,
|
|
||||||
endOfDay,
|
|
||||||
} from "date-fns/esm";
|
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { StatisticValue } from "../../../src/data/history";
|
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
interface HistoryQueryParams {
|
interface HistoryQueryParams {
|
||||||
@@ -72,331 +64,6 @@ const generateHistory = (state, deltas) => {
|
|||||||
|
|
||||||
const incrementalUnits = ["clients", "queries", "ads"];
|
const incrementalUnits = ["clients", "queries", "ads"];
|
||||||
|
|
||||||
const generateMeanStatistics = (
|
|
||||||
id: string,
|
|
||||||
start: Date,
|
|
||||||
end: Date,
|
|
||||||
period: "5minute" | "hour" | "day" | "month" = "hour",
|
|
||||||
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(),
|
|
||||||
end: 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 =
|
|
||||||
period === "day"
|
|
||||||
? addDays(currentDate, 1)
|
|
||||||
: period === "month"
|
|
||||||
? addMonths(currentDate, 1)
|
|
||||||
: addHours(currentDate, 1);
|
|
||||||
}
|
|
||||||
return statistics;
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateSumStatistics = (
|
|
||||||
id: string,
|
|
||||||
start: Date,
|
|
||||||
end: Date,
|
|
||||||
period: "5minute" | "hour" | "day" | "month" = "hour",
|
|
||||||
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(),
|
|
||||||
end: currentDate.toISOString(),
|
|
||||||
mean: null,
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
last_reset: "1970-01-01T00:00:00+00:00",
|
|
||||||
state: initValue + sum,
|
|
||||||
sum,
|
|
||||||
});
|
|
||||||
currentDate =
|
|
||||||
period === "day"
|
|
||||||
? addDays(currentDate, 1)
|
|
||||||
: period === "month"
|
|
||||||
? addMonths(currentDate, 1)
|
|
||||||
: addHours(currentDate, 1);
|
|
||||||
}
|
|
||||||
return statistics;
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateCurvedStatistics = (
|
|
||||||
id: string,
|
|
||||||
start: Date,
|
|
||||||
end: Date,
|
|
||||||
_period: "5minute" | "hour" | "day" | "month" = "hour",
|
|
||||||
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(),
|
|
||||||
end: 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,
|
|
||||||
period: "5minute" | "hour" | "day" | "month"
|
|
||||||
) => StatisticValue[]
|
|
||||||
> = {
|
|
||||||
"sensor.energy_consumption_tarif_1": (
|
|
||||||
id: string,
|
|
||||||
start: Date,
|
|
||||||
end: Date,
|
|
||||||
period = "hour"
|
|
||||||
) => {
|
|
||||||
if (period !== "hour") {
|
|
||||||
return generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
period === "day" ? 17 : 504
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
|
|
||||||
const morningLow = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
morningEnd,
|
|
||||||
period,
|
|
||||||
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,
|
|
||||||
period,
|
|
||||||
morningFinalVal,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const eveningLow = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
eveningStart,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
morningFinalVal,
|
|
||||||
0.7
|
|
||||||
);
|
|
||||||
return [...morningLow, ...empty, ...eveningLow];
|
|
||||||
},
|
|
||||||
"sensor.energy_consumption_tarif_2": (
|
|
||||||
id: string,
|
|
||||||
start: Date,
|
|
||||||
end: Date,
|
|
||||||
period = "hour"
|
|
||||||
) => {
|
|
||||||
if (period !== "hour") {
|
|
||||||
return generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
period === "day" ? 17 : 504
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
0.3
|
|
||||||
);
|
|
||||||
const highTarifFinalVal = highTarif.length
|
|
||||||
? highTarif[highTarif.length - 1].sum!
|
|
||||||
: 0;
|
|
||||||
const morning = generateSumStatistics(id, start, morningEnd, period, 0, 0);
|
|
||||||
const evening = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
eveningStart,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
highTarifFinalVal,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
return [...morning, ...highTarif, ...evening];
|
|
||||||
},
|
|
||||||
"sensor.energy_production_tarif_1": (id, start, end, period = "hour") =>
|
|
||||||
generateSumStatistics(id, start, end, period, 0, 0),
|
|
||||||
"sensor.energy_production_tarif_1_compensation": (
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
period = "hour"
|
|
||||||
) => generateSumStatistics(id, start, end, period, 0, 0),
|
|
||||||
"sensor.energy_production_tarif_2": (id, start, end, period = "hour") => {
|
|
||||||
if (period !== "hour") {
|
|
||||||
return generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
period === "day" ? 17 : 504
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
0.15,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
const productionFinalVal = production.length
|
|
||||||
? production[production.length - 1].sum!
|
|
||||||
: 0;
|
|
||||||
const morning = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
productionStart,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const evening = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
productionEnd,
|
|
||||||
dayEnd,
|
|
||||||
period,
|
|
||||||
productionFinalVal,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const rest = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
dayEnd,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
productionFinalVal,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
return [...morning, ...production, ...evening, ...rest];
|
|
||||||
},
|
|
||||||
"sensor.solar_production": (id, start, end, period = "hour") => {
|
|
||||||
if (period !== "hour") {
|
|
||||||
return generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
period === "day" ? 17 : 504
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
0.3,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
const productionFinalVal = production.length
|
|
||||||
? production[production.length - 1].sum!
|
|
||||||
: 0;
|
|
||||||
const morning = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
productionStart,
|
|
||||||
period,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const evening = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
productionEnd,
|
|
||||||
dayEnd,
|
|
||||||
period,
|
|
||||||
productionFinalVal,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const rest = generateSumStatistics(
|
|
||||||
id,
|
|
||||||
dayEnd,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
productionFinalVal,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
return [...morning, ...production, ...evening, ...rest];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||||
mockHass.mockAPI(
|
mockHass.mockAPI(
|
||||||
new RegExp("history/period/.+"),
|
new RegExp("history/period/.+"),
|
||||||
@@ -466,42 +133,4 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
mockHass.mockWS("history/list_statistic_ids", () => []);
|
|
||||||
mockHass.mockWS(
|
|
||||||
"history/statistics_during_period",
|
|
||||||
({ statistic_ids, start_time, end_time, period }, 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, period);
|
|
||||||
} 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,
|
|
||||||
period,
|
|
||||||
state,
|
|
||||||
state * (state > 80 ? 0.01 : 0.05)
|
|
||||||
)
|
|
||||||
: generateMeanStatistics(
|
|
||||||
id,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
period,
|
|
||||||
state,
|
|
||||||
state * (state > 80 ? 0.05 : 0.1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return statistics;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
342
demo/src/stubs/recorder.ts
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
import {
|
||||||
|
addDays,
|
||||||
|
addHours,
|
||||||
|
addMonths,
|
||||||
|
differenceInHours,
|
||||||
|
endOfDay,
|
||||||
|
} from "date-fns";
|
||||||
|
import {
|
||||||
|
Statistics,
|
||||||
|
StatisticsMetaData,
|
||||||
|
StatisticValue,
|
||||||
|
} from "../../../src/data/recorder";
|
||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
const generateMeanStatistics = (
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
period: "5minute" | "hour" | "day" | "month" = "hour",
|
||||||
|
initValue: number,
|
||||||
|
maxDiff: number
|
||||||
|
): StatisticValue[] => {
|
||||||
|
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({
|
||||||
|
start: currentDate.getTime(),
|
||||||
|
end: currentDate.getTime(),
|
||||||
|
mean,
|
||||||
|
min: mean - Math.random() * maxDiff,
|
||||||
|
max: mean + Math.random() * maxDiff,
|
||||||
|
last_reset: 0,
|
||||||
|
state: mean,
|
||||||
|
sum: null,
|
||||||
|
});
|
||||||
|
lastVal = mean;
|
||||||
|
currentDate =
|
||||||
|
period === "day"
|
||||||
|
? addDays(currentDate, 1)
|
||||||
|
: period === "month"
|
||||||
|
? addMonths(currentDate, 1)
|
||||||
|
: addHours(currentDate, 1);
|
||||||
|
}
|
||||||
|
return statistics;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateSumStatistics = (
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
period: "5minute" | "hour" | "day" | "month" = "hour",
|
||||||
|
initValue: number,
|
||||||
|
maxDiff: number
|
||||||
|
): StatisticValue[] => {
|
||||||
|
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({
|
||||||
|
start: currentDate.getTime(),
|
||||||
|
end: currentDate.getTime(),
|
||||||
|
mean: null,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
last_reset: 0,
|
||||||
|
state: initValue + sum,
|
||||||
|
sum,
|
||||||
|
});
|
||||||
|
currentDate =
|
||||||
|
period === "day"
|
||||||
|
? addDays(currentDate, 1)
|
||||||
|
: period === "month"
|
||||||
|
? addMonths(currentDate, 1)
|
||||||
|
: addHours(currentDate, 1);
|
||||||
|
}
|
||||||
|
return statistics;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateCurvedStatistics = (
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
_period: "5minute" | "hour" | "day" | "month" = "hour",
|
||||||
|
initValue: number,
|
||||||
|
maxDiff: number,
|
||||||
|
metered: boolean
|
||||||
|
): StatisticValue[] => {
|
||||||
|
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({
|
||||||
|
start: currentDate.getTime(),
|
||||||
|
end: currentDate.getTime(),
|
||||||
|
mean: null,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
last_reset: 0,
|
||||||
|
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,
|
||||||
|
period: "5minute" | "hour" | "day" | "month"
|
||||||
|
) => StatisticValue[]
|
||||||
|
> = {
|
||||||
|
"sensor.energy_consumption_tarif_1": (
|
||||||
|
_id: string,
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
period = "hour"
|
||||||
|
) => {
|
||||||
|
if (period !== "hour") {
|
||||||
|
return generateSumStatistics(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
period === "day" ? 17 : 504
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
|
||||||
|
const morningLow = generateSumStatistics(start, morningEnd, period, 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(
|
||||||
|
morningEnd,
|
||||||
|
eveningStart,
|
||||||
|
period,
|
||||||
|
morningFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const eveningLow = generateSumStatistics(
|
||||||
|
eveningStart,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
morningFinalVal,
|
||||||
|
0.7
|
||||||
|
);
|
||||||
|
return [...morningLow, ...empty, ...eveningLow];
|
||||||
|
},
|
||||||
|
"sensor.energy_consumption_tarif_2": (
|
||||||
|
_id: string,
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
period = "hour"
|
||||||
|
) => {
|
||||||
|
if (period !== "hour") {
|
||||||
|
return generateSumStatistics(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
period === "day" ? 17 : 504
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const morningEnd = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||||
|
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||||
|
const highTarif = generateSumStatistics(
|
||||||
|
morningEnd,
|
||||||
|
eveningStart,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
0.3
|
||||||
|
);
|
||||||
|
const highTarifFinalVal = highTarif.length
|
||||||
|
? highTarif[highTarif.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const morning = generateSumStatistics(start, morningEnd, period, 0, 0);
|
||||||
|
const evening = generateSumStatistics(
|
||||||
|
eveningStart,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
highTarifFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return [...morning, ...highTarif, ...evening];
|
||||||
|
},
|
||||||
|
"sensor.energy_production_tarif_1": (_id, start, end, period = "hour") =>
|
||||||
|
generateSumStatistics(start, end, period, 0, 0),
|
||||||
|
"sensor.energy_production_tarif_1_compensation": (
|
||||||
|
_id,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period = "hour"
|
||||||
|
) => generateSumStatistics(start, end, period, 0, 0),
|
||||||
|
"sensor.energy_production_tarif_2": (_id, start, end, period = "hour") => {
|
||||||
|
if (period !== "hour") {
|
||||||
|
return generateSumStatistics(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
period === "day" ? 17 : 504
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
productionStart,
|
||||||
|
productionEnd,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
0.15,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const productionFinalVal = production.length
|
||||||
|
? production[production.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const morning = generateSumStatistics(start, productionStart, period, 0, 0);
|
||||||
|
const evening = generateSumStatistics(
|
||||||
|
productionEnd,
|
||||||
|
dayEnd,
|
||||||
|
period,
|
||||||
|
productionFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const rest = generateSumStatistics(
|
||||||
|
dayEnd,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
productionFinalVal,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
return [...morning, ...production, ...evening, ...rest];
|
||||||
|
},
|
||||||
|
"sensor.solar_production": (_id, start, end, period = "hour") => {
|
||||||
|
if (period !== "hour") {
|
||||||
|
return generateSumStatistics(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
period === "day" ? 17 : 504
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
productionStart,
|
||||||
|
productionEnd,
|
||||||
|
period,
|
||||||
|
0,
|
||||||
|
0.3,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const productionFinalVal = production.length
|
||||||
|
? production[production.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const morning = generateSumStatistics(start, productionStart, period, 0, 0);
|
||||||
|
const evening = generateSumStatistics(
|
||||||
|
productionEnd,
|
||||||
|
dayEnd,
|
||||||
|
period,
|
||||||
|
productionFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const rest = generateSumStatistics(
|
||||||
|
dayEnd,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
productionFinalVal,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
return [...morning, ...production, ...evening, ...rest];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export const mockRecorder = (mockHass: MockHomeAssistant) => {
|
||||||
|
mockHass.mockWS(
|
||||||
|
"recorder/get_statistics_metadata",
|
||||||
|
(): StatisticsMetaData[] => []
|
||||||
|
);
|
||||||
|
mockHass.mockWS(
|
||||||
|
"recorder/list_statistic_ids",
|
||||||
|
(): StatisticsMetaData[] => []
|
||||||
|
);
|
||||||
|
mockHass.mockWS(
|
||||||
|
"recorder/statistics_during_period",
|
||||||
|
({ statistic_ids, start_time, end_time, period }, hass): Statistics => {
|
||||||
|
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, period);
|
||||||
|
} else {
|
||||||
|
const entityState = hass.states[id];
|
||||||
|
const state = entityState ? Number(entityState.state) : 1;
|
||||||
|
statistics[id] =
|
||||||
|
entityState && "last_reset" in entityState.attributes
|
||||||
|
? generateSumStatistics(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
state,
|
||||||
|
state * (state > 80 ? 0.01 : 0.05)
|
||||||
|
)
|
||||||
|
: generateMeanStatistics(
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
period,
|
||||||
|
state,
|
||||||
|
state * (state > 80 ? 0.05 : 0.1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 32 KiB |
@@ -8,7 +8,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
category: "lovelace",
|
category: "lovelace",
|
||||||
// Label for in the sidebar
|
// Label for in the sidebar
|
||||||
header: "Lovelace",
|
header: "Dashboards",
|
||||||
// Specify order of pages. Any pages in the category folder but not listed here will
|
// Specify order of pages. Any pages in the category folder but not listed here will
|
||||||
// automatically be added after the pages listed here.
|
// automatically be added after the pages listed here.
|
||||||
pages: ["introduction"],
|
pages: ["introduction"],
|
||||||
@@ -34,7 +34,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "misc",
|
category: "misc",
|
||||||
header: "Miscelaneous",
|
header: "Miscellaneous",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "brand",
|
category: "brand",
|
||||||
|
@@ -5,7 +5,7 @@ import { html, css, LitElement, PropertyValues } from "lit";
|
|||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import "../../src/components/ha-icon-button";
|
import "../../src/components/ha-icon-button";
|
||||||
import "../../src/managers/notification-manager";
|
import "../../src/managers/notification-manager";
|
||||||
import "../../src/components/ha-expansion-panel";
|
import { HaExpansionPanel } from "../../src/components/ha-expansion-panel";
|
||||||
import { haStyle } from "../../src/resources/styles";
|
import { haStyle } from "../../src/resources/styles";
|
||||||
import { PAGES, SIDEBAR } from "../build/import-pages";
|
import { PAGES, SIDEBAR } from "../build/import-pages";
|
||||||
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
||||||
@@ -174,9 +174,10 @@ class HaGallery extends LitElement {
|
|||||||
const menuItem = this.shadowRoot!.querySelector(
|
const menuItem = this.shadowRoot!.querySelector(
|
||||||
`a[href="#${this._page}"]`
|
`a[href="#${this._page}"]`
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
// Make sure section is expanded
|
// Make sure section is expanded
|
||||||
if (menuItem.parentElement instanceof HTMLDetailsElement) {
|
if (menuItem.parentElement instanceof HaExpansionPanel) {
|
||||||
menuItem.parentElement.open = true;
|
menuItem.parentElement.expanded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
gallery/src/pages/Text/remove-delete-add-create.markdown
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
title: When to use remove, delete, add and create
|
||||||
|
subtitle: The difference between remove/delete and add/create.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Remove vs Delete
|
||||||
|
Remove and Delete are quite similar, but can be frustrating if used inconsistently.
|
||||||
|
|
||||||
|
## Remove
|
||||||
|
Take away and set aside, but kept in existence.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
* Removing a user's permission
|
||||||
|
* Removing a user from a group
|
||||||
|
* Removing links between items
|
||||||
|
* Removing a widget
|
||||||
|
* Removing a link
|
||||||
|
* Removing an item from a cart
|
||||||
|
|
||||||
|
## Delete
|
||||||
|
Erase, rendered nonexistent or nonrecoverable.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
* Deleting a field
|
||||||
|
* Deleting a value in a field
|
||||||
|
* Deleting a task
|
||||||
|
* Deleting a group
|
||||||
|
* Deleting a permission
|
||||||
|
* Deleting a calendar event
|
||||||
|
|
||||||
|
# Add vs Create
|
||||||
|
In most cases, Create can be paired with Delete, and Add can be paired with Remove.
|
||||||
|
|
||||||
|
## Add
|
||||||
|
An already-exisiting item.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
* Adding a permission to a user
|
||||||
|
* Adding a user to a group
|
||||||
|
* Adding links between items
|
||||||
|
* Adding a widget
|
||||||
|
* Adding a link
|
||||||
|
* Adding an item to a cart
|
||||||
|
|
||||||
|
## Create
|
||||||
|
Something made from scratch.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
* Creating a new field
|
||||||
|
* Creating a new value in a field
|
||||||
|
* Creating a new task
|
||||||
|
* Creating a new group
|
||||||
|
* Creating a new permission
|
||||||
|
* Creating a new calendar event
|
||||||
|
|
||||||
|
Based on this is [UX magazine article](https://uxmag.com/articles/ui-copy-remove-vs-delete2-banner).
|
@@ -1,7 +1,9 @@
|
|||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
|
import { Action } from "../../../../src/data/script";
|
||||||
import { describeAction } from "../../../../src/data/script_i18n";
|
import { describeAction } from "../../../../src/data/script_i18n";
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
@@ -88,6 +90,15 @@ const ACTIONS = [
|
|||||||
then: [{ delay: "00:00:01" }],
|
then: [{ delay: "00:00:01" }],
|
||||||
else: [{ delay: "00:00:05" }],
|
else: [{ delay: "00:00:05" }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
if: [{ condition: "state" }],
|
||||||
|
then: [{ delay: "00:00:01" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
if: [{ condition: "state" }, { condition: "state" }],
|
||||||
|
then: [{ delay: "00:00:01" }],
|
||||||
|
else: [{ delay: "00:00:05" }],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
choose: [
|
choose: [
|
||||||
{
|
{
|
||||||
@@ -103,16 +114,38 @@ const ACTIONS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const initialAction: Action = {
|
||||||
|
service: "light.turn_on",
|
||||||
|
target: {
|
||||||
|
entity_id: "light.kitchen",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("demo-automation-describe-action")
|
@customElement("demo-automation-describe-action")
|
||||||
export class DemoAutomationDescribeAction extends LitElement {
|
export class DemoAutomationDescribeAction extends LitElement {
|
||||||
@property({ attribute: false }) hass!: HomeAssistant;
|
@property({ attribute: false }) hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() _action = initialAction;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Actions">
|
<ha-card header="Actions">
|
||||||
|
<div class="action">
|
||||||
|
<span>
|
||||||
|
${this._action
|
||||||
|
? describeAction(this.hass, this._action)
|
||||||
|
: "<invalid YAML>"}
|
||||||
|
</span>
|
||||||
|
<ha-yaml-editor
|
||||||
|
label="Action Config"
|
||||||
|
.defaultValue=${initialAction}
|
||||||
|
@value-changed=${this._dataChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
</div>
|
||||||
|
|
||||||
${ACTIONS.map(
|
${ACTIONS.map(
|
||||||
(conf) => html`
|
(conf) => html`
|
||||||
<div class="action">
|
<div class="action">
|
||||||
@@ -132,6 +165,11 @@ export class DemoAutomationDescribeAction extends LitElement {
|
|||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _dataChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._action = ev.detail.isValid ? ev.detail.value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
ha-card {
|
ha-card {
|
||||||
@@ -147,6 +185,9 @@ export class DemoAutomationDescribeAction extends LitElement {
|
|||||||
span {
|
span {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
ha-yaml-editor {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,31 +1,82 @@
|
|||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
|
import { Condition } from "../../../../src/data/automation";
|
||||||
import { describeCondition } from "../../../../src/data/automation_i18n";
|
import { describeCondition } from "../../../../src/data/automation_i18n";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("light", "kitchen", "on", {
|
||||||
|
friendly_name: "Kitchen Light",
|
||||||
|
}),
|
||||||
|
getEntity("device_tracker", "person", "home", {
|
||||||
|
friendly_name: "Person",
|
||||||
|
}),
|
||||||
|
getEntity("zone", "home", "", {
|
||||||
|
friendly_name: "Home",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
const conditions = [
|
const conditions = [
|
||||||
{ condition: "and" },
|
{ condition: "and" },
|
||||||
{ condition: "not" },
|
{ condition: "not" },
|
||||||
{ condition: "or" },
|
{ condition: "or" },
|
||||||
{ condition: "state" },
|
{ condition: "state", entity_id: "light.kitchen", state: "on" },
|
||||||
{ condition: "numeric_state" },
|
{
|
||||||
|
condition: "numeric_state",
|
||||||
|
entity_id: "light.kitchen",
|
||||||
|
attribute: "brightness",
|
||||||
|
below: 80,
|
||||||
|
above: 20,
|
||||||
|
},
|
||||||
{ condition: "sun", after: "sunset" },
|
{ condition: "sun", after: "sunset" },
|
||||||
{ condition: "sun", after: "sunrise" },
|
{ condition: "sun", after: "sunrise", offset: "-01:00" },
|
||||||
{ condition: "zone" },
|
{ condition: "zone", entity_id: "device_tracker.person", zone: "zone.home" },
|
||||||
|
{ condition: "trigger", id: "motion" },
|
||||||
{ condition: "time" },
|
{ condition: "time" },
|
||||||
{ condition: "template" },
|
{ condition: "template" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const initialCondition: Condition = {
|
||||||
|
condition: "state",
|
||||||
|
entity_id: "light.kitchen",
|
||||||
|
state: "on",
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("demo-automation-describe-condition")
|
@customElement("demo-automation-describe-condition")
|
||||||
export class DemoAutomationDescribeCondition extends LitElement {
|
export class DemoAutomationDescribeCondition extends LitElement {
|
||||||
|
@property({ attribute: false }) hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() _condition = initialCondition;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Conditions">
|
<ha-card header="Conditions">
|
||||||
|
<div class="condition">
|
||||||
|
<span>
|
||||||
|
${this._condition
|
||||||
|
? describeCondition(this._condition, this.hass)
|
||||||
|
: "<invalid YAML>"}
|
||||||
|
</span>
|
||||||
|
<ha-yaml-editor
|
||||||
|
label="Condition Config"
|
||||||
|
.defaultValue=${initialCondition}
|
||||||
|
@value-changed=${this._dataChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
</div>
|
||||||
|
|
||||||
${conditions.map(
|
${conditions.map(
|
||||||
(conf) => html`
|
(conf) => html`
|
||||||
<div class="condition">
|
<div class="condition">
|
||||||
<span>${describeCondition(conf as any)}</span>
|
<span>${describeCondition(conf as any, this.hass)}</span>
|
||||||
<pre>${dump(conf)}</pre>
|
<pre>${dump(conf)}</pre>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -34,6 +85,18 @@ export class DemoAutomationDescribeCondition extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dataChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._condition = ev.detail.isValid ? ev.detail.value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
ha-card {
|
ha-card {
|
||||||
@@ -49,6 +112,9 @@ export class DemoAutomationDescribeCondition extends LitElement {
|
|||||||
span {
|
span {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
ha-yaml-editor {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,34 +1,92 @@
|
|||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
|
import { Trigger } from "../../../../src/data/automation";
|
||||||
import { describeTrigger } from "../../../../src/data/automation_i18n";
|
import { describeTrigger } from "../../../../src/data/automation_i18n";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("light", "kitchen", "on", {
|
||||||
|
friendly_name: "Kitchen Light",
|
||||||
|
}),
|
||||||
|
getEntity("person", "person", "", {
|
||||||
|
friendly_name: "Person",
|
||||||
|
}),
|
||||||
|
getEntity("zone", "home", "", {
|
||||||
|
friendly_name: "Home",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
const triggers = [
|
const triggers = [
|
||||||
{ platform: "state" },
|
{ platform: "state", entity_id: "light.kitchen", from: "off", to: "on" },
|
||||||
{ platform: "mqtt" },
|
{ platform: "mqtt" },
|
||||||
{ platform: "geo_location" },
|
{
|
||||||
{ platform: "homeassistant" },
|
platform: "geo_location",
|
||||||
{ platform: "numeric_state" },
|
source: "test_source",
|
||||||
{ platform: "sun" },
|
zone: "zone.home",
|
||||||
|
event: "enter",
|
||||||
|
},
|
||||||
|
{ platform: "homeassistant", event: "start" },
|
||||||
|
{
|
||||||
|
platform: "numeric_state",
|
||||||
|
entity_id: "light.kitchen",
|
||||||
|
attribute: "brightness",
|
||||||
|
below: 80,
|
||||||
|
above: 20,
|
||||||
|
},
|
||||||
|
{ platform: "sun", event: "sunset" },
|
||||||
{ platform: "time_pattern" },
|
{ platform: "time_pattern" },
|
||||||
{ platform: "webhook" },
|
{ platform: "webhook" },
|
||||||
{ platform: "zone" },
|
{
|
||||||
|
platform: "zone",
|
||||||
|
entity_id: "person.person",
|
||||||
|
zone: "zone.home",
|
||||||
|
event: "enter",
|
||||||
|
},
|
||||||
{ platform: "tag" },
|
{ platform: "tag" },
|
||||||
{ platform: "time" },
|
{ platform: "time", at: "15:32" },
|
||||||
{ platform: "template" },
|
{ platform: "template" },
|
||||||
{ platform: "event" },
|
{ platform: "event", event_type: "homeassistant_started" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const initialTrigger: Trigger = {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "light.kitchen",
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("demo-automation-describe-trigger")
|
@customElement("demo-automation-describe-trigger")
|
||||||
export class DemoAutomationDescribeTrigger extends LitElement {
|
export class DemoAutomationDescribeTrigger extends LitElement {
|
||||||
|
@property({ attribute: false }) hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() _trigger = initialTrigger;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Triggers">
|
<ha-card header="Triggers">
|
||||||
|
<div class="trigger">
|
||||||
|
<span>
|
||||||
|
${this._trigger
|
||||||
|
? describeTrigger(this._trigger, this.hass)
|
||||||
|
: "<invalid YAML>"}
|
||||||
|
</span>
|
||||||
|
<ha-yaml-editor
|
||||||
|
label="Trigger Config"
|
||||||
|
.defaultValue=${initialTrigger}
|
||||||
|
@value-changed=${this._dataChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
</div>
|
||||||
${triggers.map(
|
${triggers.map(
|
||||||
(conf) => html`
|
(conf) => html`
|
||||||
<div class="trigger">
|
<div class="trigger">
|
||||||
<span>${describeTrigger(conf as any)}</span>
|
<span>${describeTrigger(conf as any, this.hass)}</span>
|
||||||
<pre>${dump(conf)}</pre>
|
<pre>${dump(conf)}</pre>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -37,6 +95,18 @@ export class DemoAutomationDescribeTrigger extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dataChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._trigger = ev.detail.isValid ? ev.detail.value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
ha-card {
|
ha-card {
|
||||||
@@ -52,6 +122,9 @@ export class DemoAutomationDescribeTrigger extends LitElement {
|
|||||||
span {
|
span {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
ha-yaml-editor {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
import { LitElement, TemplateResult, html, css } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
@@ -47,6 +47,8 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [
|
|||||||
class DemoHaAutomationEditorAction extends LitElement {
|
class DemoHaAutomationEditorAction extends LitElement {
|
||||||
@state() private hass!: HomeAssistant;
|
@state() private hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _disabled = false;
|
||||||
|
|
||||||
private data: any = SCHEMAS.map((info) => info.actions);
|
private data: any = SCHEMAS.map((info) => info.actions);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -67,6 +69,15 @@ class DemoHaAutomationEditorAction extends LitElement {
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
|
<div class="options">
|
||||||
|
<ha-formfield label="Disabled">
|
||||||
|
<ha-switch
|
||||||
|
.name=${"disabled"}
|
||||||
|
.checked=${this._disabled}
|
||||||
|
@change=${this._handleOptionChange}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
</div>
|
||||||
${SCHEMAS.map(
|
${SCHEMAS.map(
|
||||||
(info, sampleIdx) => html`
|
(info, sampleIdx) => html`
|
||||||
<demo-black-white-row
|
<demo-black-white-row
|
||||||
@@ -81,6 +92,7 @@ class DemoHaAutomationEditorAction extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.actions=${this.data[sampleIdx]}
|
.actions=${this.data[sampleIdx]}
|
||||||
.sampleIdx=${sampleIdx}
|
.sampleIdx=${sampleIdx}
|
||||||
|
.disabled=${this._disabled}
|
||||||
@value-changed=${valueChanged}
|
@value-changed=${valueChanged}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`
|
`
|
||||||
@@ -90,6 +102,20 @@ class DemoHaAutomationEditorAction extends LitElement {
|
|||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOptionChange(ev) {
|
||||||
|
this[`_${ev.target.name}`] = ev.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.options {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 16px auto;
|
||||||
|
}
|
||||||
|
.options ha-formfield {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
import { LitElement, TemplateResult, html, css } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
@@ -83,6 +83,8 @@ const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
|
|||||||
class DemoHaAutomationEditorCondition extends LitElement {
|
class DemoHaAutomationEditorCondition extends LitElement {
|
||||||
@state() private hass!: HomeAssistant;
|
@state() private hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _disabled = false;
|
||||||
|
|
||||||
private data: any = SCHEMAS.map((info) => info.conditions);
|
private data: any = SCHEMAS.map((info) => info.conditions);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -103,6 +105,15 @@ class DemoHaAutomationEditorCondition extends LitElement {
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
|
<div class="options">
|
||||||
|
<ha-formfield label="Disabled">
|
||||||
|
<ha-switch
|
||||||
|
.name=${"disabled"}
|
||||||
|
.checked=${this._disabled}
|
||||||
|
@change=${this._handleOptionChange}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
</div>
|
||||||
${SCHEMAS.map(
|
${SCHEMAS.map(
|
||||||
(info, sampleIdx) => html`
|
(info, sampleIdx) => html`
|
||||||
<demo-black-white-row
|
<demo-black-white-row
|
||||||
@@ -117,6 +128,7 @@ class DemoHaAutomationEditorCondition extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.conditions=${this.data[sampleIdx]}
|
.conditions=${this.data[sampleIdx]}
|
||||||
.sampleIdx=${sampleIdx}
|
.sampleIdx=${sampleIdx}
|
||||||
|
.disabled=${this._disabled}
|
||||||
@value-changed=${valueChanged}
|
@value-changed=${valueChanged}
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
`
|
`
|
||||||
@@ -126,6 +138,20 @@ class DemoHaAutomationEditorCondition extends LitElement {
|
|||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOptionChange(ev) {
|
||||||
|
this[`_${ev.target.name}`] = ev.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.options {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 16px auto;
|
||||||
|
}
|
||||||
|
.options ha-formfield {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import { LitElement, TemplateResult, html } from "lit";
|
import { LitElement, TemplateResult, html, css } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
@@ -107,6 +107,8 @@ const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
|
|||||||
class DemoHaAutomationEditorTrigger extends LitElement {
|
class DemoHaAutomationEditorTrigger extends LitElement {
|
||||||
@state() private hass!: HomeAssistant;
|
@state() private hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _disabled = false;
|
||||||
|
|
||||||
private data: any = SCHEMAS.map((info) => info.triggers);
|
private data: any = SCHEMAS.map((info) => info.triggers);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -127,6 +129,15 @@ class DemoHaAutomationEditorTrigger extends LitElement {
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
};
|
};
|
||||||
return html`
|
return html`
|
||||||
|
<div class="options">
|
||||||
|
<ha-formfield label="Disabled">
|
||||||
|
<ha-switch
|
||||||
|
.name=${"disabled"}
|
||||||
|
.checked=${this._disabled}
|
||||||
|
@change=${this._handleOptionChange}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
</div>
|
||||||
${SCHEMAS.map(
|
${SCHEMAS.map(
|
||||||
(info, sampleIdx) => html`
|
(info, sampleIdx) => html`
|
||||||
<demo-black-white-row
|
<demo-black-white-row
|
||||||
@@ -141,6 +152,7 @@ class DemoHaAutomationEditorTrigger extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.triggers=${this.data[sampleIdx]}
|
.triggers=${this.data[sampleIdx]}
|
||||||
.sampleIdx=${sampleIdx}
|
.sampleIdx=${sampleIdx}
|
||||||
|
.disabled=${this._disabled}
|
||||||
@value-changed=${valueChanged}
|
@value-changed=${valueChanged}
|
||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
`
|
`
|
||||||
@@ -150,6 +162,20 @@ class DemoHaAutomationEditorTrigger extends LitElement {
|
|||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOptionChange(ev) {
|
||||||
|
this[`_${ev.target.name}`] = ev.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.options {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 16px auto;
|
||||||
|
}
|
||||||
|
.options ha-formfield {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -2,8 +2,6 @@
|
|||||||
title: "Logo"
|
title: "Logo"
|
||||||
---
|
---
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
# Using our logo
|
# Using our logo
|
||||||
|
|
||||||
As a community, we are proud of our logo. Follow these guidelines to ensure it always looks its best. Our logo follows Google's material design spec and uses the blue interface color.
|
As a community, we are proud of our logo. Follow these guidelines to ensure it always looks its best. Our logo follows Google's material design spec and uses the blue interface color.
|
||||||
|
@@ -3,6 +3,13 @@ title: Alerts
|
|||||||
subtitle: An alert displays a short, important message in a way that attracts the user's attention without interrupting the user's task.
|
subtitle: An alert displays a short, important message in a way that attracts the user's attention without interrupting the user's task.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<style>
|
||||||
|
ha-alert {
|
||||||
|
display: block;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
# Alert `<ha-alert>`
|
# Alert `<ha-alert>`
|
||||||
The alert offers four severity levels that set a distinctive icon and color.
|
The alert offers four severity levels that set a distinctive icon and color.
|
||||||
|
|
||||||
|
3
gallery/src/pages/components/ha-bar-slider.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Bar Slider
|
||||||
|
---
|
169
gallery/src/pages/components/ha-bar-slider.ts
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, state } from "lit/decorators";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import "../../../../src/components/ha-bar-slider";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
|
||||||
|
const sliders: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
mode?: "start" | "end" | "cursor";
|
||||||
|
class?: string;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
id: "slider-start",
|
||||||
|
label: "Slider (start mode)",
|
||||||
|
mode: "start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "slider-end",
|
||||||
|
label: "Slider (end mode)",
|
||||||
|
mode: "end",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "slider-cursor",
|
||||||
|
label: "Slider (cursor mode)",
|
||||||
|
mode: "cursor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "slider-start-custom",
|
||||||
|
label: "Slider (start mode) and custom style",
|
||||||
|
mode: "start",
|
||||||
|
class: "custom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "slider-end-custom",
|
||||||
|
label: "Slider (end mode) and custom style",
|
||||||
|
mode: "end",
|
||||||
|
class: "custom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "slider-cursor-custom",
|
||||||
|
label: "Slider (cursor mode) and custom style",
|
||||||
|
mode: "cursor",
|
||||||
|
class: "custom",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-bar-slider")
|
||||||
|
export class DemoHaBarSlider extends LitElement {
|
||||||
|
@state() private value = 50;
|
||||||
|
|
||||||
|
@state() private sliderPosition?: number;
|
||||||
|
|
||||||
|
handleValueChanged(e: CustomEvent) {
|
||||||
|
this.value = e.detail.value as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSliderMoved(e: CustomEvent) {
|
||||||
|
this.sliderPosition = e.detail.value as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<p><b>Slider values</b></p>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>position</td>
|
||||||
|
<td>${this.sliderPosition ?? "-"}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>value</td>
|
||||||
|
<td>${this.value ?? "-"}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
${repeat(sliders, (slider) => {
|
||||||
|
const { id, label, ...config } = slider;
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<label id=${id}>${label}</label>
|
||||||
|
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||||
|
<ha-bar-slider
|
||||||
|
.value=${this.value}
|
||||||
|
.mode=${config.mode}
|
||||||
|
class=${ifDefined(config.class)}
|
||||||
|
@value-changed=${this.handleValueChanged}
|
||||||
|
@slider-moved=${this.handleSliderMoved}
|
||||||
|
aria-labelledby=${id}
|
||||||
|
>
|
||||||
|
</ha-bar-slider>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="title"><b>Vertical</b></p>
|
||||||
|
<div class="vertical-sliders">
|
||||||
|
${repeat(sliders, (slider) => {
|
||||||
|
const { id, label, ...config } = slider;
|
||||||
|
return html`
|
||||||
|
<ha-bar-slider
|
||||||
|
.value=${this.value}
|
||||||
|
.mode=${config.mode}
|
||||||
|
vertical
|
||||||
|
class=${ifDefined(config.class)}
|
||||||
|
@value-changed=${this.handleValueChanged}
|
||||||
|
@slider-moved=${this.handleSliderMoved}
|
||||||
|
aria-label=${label}
|
||||||
|
>
|
||||||
|
</ha-bar-slider>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.custom {
|
||||||
|
--slider-bar-color: #ffcf4c;
|
||||||
|
--slider-bar-background: #ffcf4c64;
|
||||||
|
--slider-bar-thickness: 100px;
|
||||||
|
--slider-bar-border-radius: 24px;
|
||||||
|
}
|
||||||
|
.vertical-sliders {
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
p.title {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.vertical-sliders > *:not(:last-child) {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-bar-slider": DemoHaBarSlider;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/components/ha-bar-switch.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Bar Switch
|
||||||
|
---
|
145
gallery/src/pages/components/ha-bar-switch.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import {
|
||||||
|
mdiGarage,
|
||||||
|
mdiGarageOpen,
|
||||||
|
mdiLightbulb,
|
||||||
|
mdiLightbulbOff,
|
||||||
|
} from "@mdi/js";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, state } from "lit/decorators";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import "../../../../src/components/ha-bar-switch";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
|
||||||
|
const switches: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
class?: string;
|
||||||
|
reversed?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
id: "switch",
|
||||||
|
label: "Switch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "switch-reversed",
|
||||||
|
label: "Switch Reversed",
|
||||||
|
reversed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "switch-custom",
|
||||||
|
label: "Switch and custom style",
|
||||||
|
class: "custom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "switch-disabled",
|
||||||
|
label: "Disabled Switch",
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-bar-switch")
|
||||||
|
export class DemoHaBarSwitch extends LitElement {
|
||||||
|
@state() private checked = false;
|
||||||
|
|
||||||
|
handleValueChanged(e: any) {
|
||||||
|
this.checked = e.target.checked as boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${repeat(switches, (sw) => {
|
||||||
|
const { id, label, ...config } = sw;
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<label id=${id}>${label}</label>
|
||||||
|
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||||
|
<ha-bar-switch
|
||||||
|
.checked=${this.checked}
|
||||||
|
class=${ifDefined(config.class)}
|
||||||
|
@change=${this.handleValueChanged}
|
||||||
|
.pathOn=${mdiLightbulb}
|
||||||
|
.pathOff=${mdiLightbulbOff}
|
||||||
|
aria-labelledby=${id}
|
||||||
|
disabled=${ifDefined(config.disabled)}
|
||||||
|
reversed=${ifDefined(config.reversed)}
|
||||||
|
>
|
||||||
|
</ha-bar-switch>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="title"><b>Vertical</b></p>
|
||||||
|
<div class="vertical-switches">
|
||||||
|
${repeat(switches, (sw) => {
|
||||||
|
const { id, label, ...config } = sw;
|
||||||
|
return html`
|
||||||
|
<ha-bar-switch
|
||||||
|
.checked=${this.checked}
|
||||||
|
vertical
|
||||||
|
class=${ifDefined(config.class)}
|
||||||
|
@change=${this.handleValueChanged}
|
||||||
|
aria-label=${label}
|
||||||
|
.pathOn=${mdiGarageOpen}
|
||||||
|
.pathOff=${mdiGarage}
|
||||||
|
disabled=${ifDefined(config.disabled)}
|
||||||
|
reversed=${ifDefined(config.reversed)}
|
||||||
|
>
|
||||||
|
</ha-bar-switch>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.custom {
|
||||||
|
--switch-bar-color-on: var(--rgb-green-color);
|
||||||
|
--switch-bar-color-off: var(--rgb-red-color);
|
||||||
|
--switch-bar-thickness: 100px;
|
||||||
|
--switch-bar-border-radius: 24px;
|
||||||
|
--switch-bar-padding: 6px;
|
||||||
|
--mdc-icon-size: 24px;
|
||||||
|
}
|
||||||
|
.vertical-switches {
|
||||||
|
height: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
p.title {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.vertical-switches > *:not(:last-child) {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-bar-switch": DemoHaBarSwitch;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,3 @@
|
|||||||
---
|
---
|
||||||
title: Chips
|
title: Chip
|
||||||
---
|
---
|
||||||
|
32
gallery/src/pages/components/ha-dialogs.markdown
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
title: Dialog
|
||||||
|
subtitle: Dialogs provide important prompts in a user flow.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Material Design 3
|
||||||
|
|
||||||
|
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||||
|
|
||||||
|
# Highlighted guidelines
|
||||||
|
|
||||||
|
## Content
|
||||||
|
* A best practice is to always use a title, even if it is optional by Material guidelines.
|
||||||
|
* People mainly read the title and a button. Put the most important information in those two.
|
||||||
|
* Try to avoid user generated content in the title, this could make the title unreadable long.
|
||||||
|
* If users become unsure, they read the description. Make sure this explains what will happen.
|
||||||
|
* Strive for minimalism.
|
||||||
|
|
||||||
|
## Buttons and X-icon
|
||||||
|
* Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||||
|
* Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||||
|
* Destructive actions should be a red warning button.
|
||||||
|
* Alert or confirmation dialogs only have buttons and no X-icon.
|
||||||
|
* Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
### Confirmation dialog
|
||||||
|
> **Delete dashboard?**
|
||||||
|
>
|
||||||
|
> Dashboard [dashboard name] will be permanently deleted from Home Assistant.
|
||||||
|
>
|
||||||
|
> Cancel / Delete
|
5
gallery/src/pages/components/ha-expansion-panel.markdown
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: Expansion Panel
|
||||||
|
---
|
||||||
|
|
||||||
|
Expansion panel following all the ARIA guidelines.
|
157
gallery/src/pages/components/ha-expansion-panel.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import { mdiPacMan } from "@mdi/js";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
|
import "../../../../src/components/ha-markdown";
|
||||||
|
import "../../components/demo-black-white-row";
|
||||||
|
import { LONG_TEXT } from "../../data/text";
|
||||||
|
|
||||||
|
const SHORT_TEXT = LONG_TEXT.substring(0, 113);
|
||||||
|
|
||||||
|
const SAMPLES: {
|
||||||
|
template: (slot: string, leftChevron: boolean) => TemplateResult;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
slot=${slot}
|
||||||
|
.leftChevron=${leftChevron}
|
||||||
|
header="Attr header"
|
||||||
|
>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
slot=${slot}
|
||||||
|
.leftChevron=${leftChevron}
|
||||||
|
header="Attr header"
|
||||||
|
secondary="Attr secondary"
|
||||||
|
>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
slot=${slot}
|
||||||
|
.leftChevron=${leftChevron}
|
||||||
|
.header=${"Prop header"}
|
||||||
|
>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
slot=${slot}
|
||||||
|
.leftChevron=${leftChevron}
|
||||||
|
.header=${"Prop header"}
|
||||||
|
.secondary=${"Prop secondary"}
|
||||||
|
>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
slot=${slot}
|
||||||
|
.leftChevron=${leftChevron}
|
||||||
|
.header=${"Prop header"}
|
||||||
|
>
|
||||||
|
<span slot="secondary">Slot Secondary</span>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel slot=${slot} .leftChevron=${leftChevron}>
|
||||||
|
<span slot="header">Slot header</span>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel slot=${slot} .leftChevron=${leftChevron}>
|
||||||
|
<span slot="header">Slot header with actions</span>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="icons"
|
||||||
|
label="Some Action"
|
||||||
|
.path=${mdiPacMan}
|
||||||
|
></ha-icon-button>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template(slot, leftChevron) {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
slot=${slot}
|
||||||
|
.leftChevron=${leftChevron}
|
||||||
|
header="Attr Header with actions"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="icons"
|
||||||
|
label="Some Action"
|
||||||
|
.path=${mdiPacMan}
|
||||||
|
></ha-icon-button>
|
||||||
|
${SHORT_TEXT}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-expansion-panel")
|
||||||
|
export class DemoHaExpansionPanel extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${SAMPLES.map(
|
||||||
|
(sample) => html`
|
||||||
|
<demo-black-white-row>
|
||||||
|
${["light", "dark"].map((slot) =>
|
||||||
|
sample.template(slot, slot === "dark")
|
||||||
|
)}
|
||||||
|
</demo-black-white-row>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-expansion-panel {
|
||||||
|
margin: -16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-expansion-panel": DemoHaExpansionPanel;
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,7 @@ import "@material/mwc-button";
|
|||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||||
|
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
|
||||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||||
@@ -20,16 +21,22 @@ const ENTITIES = [
|
|||||||
}),
|
}),
|
||||||
getEntity("media_player", "livingroom", "playing", {
|
getEntity("media_player", "livingroom", "playing", {
|
||||||
friendly_name: "Livingroom",
|
friendly_name: "Livingroom",
|
||||||
|
media_content_type: "music",
|
||||||
|
device_class: "tv",
|
||||||
}),
|
}),
|
||||||
getEntity("media_player", "lounge", "idle", {
|
getEntity("media_player", "lounge", "idle", {
|
||||||
friendly_name: "Lounge",
|
friendly_name: "Lounge",
|
||||||
supported_features: 444983,
|
supported_features: 444983,
|
||||||
|
device_class: "speaker",
|
||||||
}),
|
}),
|
||||||
getEntity("light", "bedroom", "on", {
|
getEntity("light", "bedroom", "on", {
|
||||||
friendly_name: "Bedroom",
|
friendly_name: "Bedroom",
|
||||||
|
effect: "colorloop",
|
||||||
|
effect_list: ["colorloop", "random"],
|
||||||
}),
|
}),
|
||||||
getEntity("switch", "coffee", "off", {
|
getEntity("switch", "coffee", "off", {
|
||||||
friendly_name: "Coffee",
|
friendly_name: "Coffee",
|
||||||
|
device_class: "switch",
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -141,7 +148,13 @@ const SCHEMAS: {
|
|||||||
selector: { attribute: { entity_id: "" } },
|
selector: { attribute: { entity_id: "" } },
|
||||||
context: { filter_entity: "entity" },
|
context: { filter_entity: "entity" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "State",
|
||||||
|
selector: { state: { entity_id: "" } },
|
||||||
|
context: { filter_entity: "entity", filter_attribute: "Attribute" },
|
||||||
|
},
|
||||||
{ name: "Device", selector: { device: {} } },
|
{ name: "Device", selector: { device: {} } },
|
||||||
|
{ name: "Config entry", selector: { config_entry: {} } },
|
||||||
{ name: "Duration", selector: { duration: {} } },
|
{ name: "Duration", selector: { duration: {} } },
|
||||||
{ name: "area", selector: { area: {} } },
|
{ name: "area", selector: { area: {} } },
|
||||||
{ name: "target", selector: { target: {} } },
|
{ name: "target", selector: { target: {} } },
|
||||||
@@ -423,6 +436,7 @@ class DemoHaForm extends LitElement {
|
|||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
mockEntityRegistry(hass);
|
mockEntityRegistry(hass);
|
||||||
mockDeviceRegistry(hass, DEVICES);
|
mockDeviceRegistry(hass, DEVICES);
|
||||||
|
mockConfigEntries(hass);
|
||||||
mockAreaRegistry(hass, AREAS);
|
mockAreaRegistry(hass, AREAS);
|
||||||
mockHassioSupervisor(hass);
|
mockHassioSupervisor(hass);
|
||||||
}
|
}
|
||||||
|
61
gallery/src/pages/components/ha-gauge.markdown
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
title: Gauge
|
||||||
|
---
|
||||||
|
|
||||||
|
<style>
|
||||||
|
ha-gauge {
|
||||||
|
display: block;
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
# Gauge `<ha-gauge>`
|
||||||
|
|
||||||
|
A gauge that can be used to represent sensor data and provide visual feedback about the value and the corresponding severity (success, warning, error).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Info color gauge
|
||||||
|
<ha-gauge value="75" style="--gauge-color: var(--info-color)"></ha-gauge>
|
||||||
|
|
||||||
|
Success color gauge
|
||||||
|
<ha-gauge value="25" style="--gauge-color: var(--success-color)" label="°C"></ha-gauge>
|
||||||
|
|
||||||
|
Warning color gauge
|
||||||
|
<ha-gauge value="50" style="--gauge-color: var(--warning-color)" label="°C"></ha-gauge>
|
||||||
|
|
||||||
|
Error color gauge
|
||||||
|
<ha-gauge value="75" style="--gauge-color: var(--error-color)" label="°C"></ha-gauge>
|
||||||
|
|
||||||
|
Gauge with background color
|
||||||
|
<ha-gauge value="75" style="--gauge-color: var(--info-color); --primary-background-color: lightgray"></ha-gauge>
|
||||||
|
|
||||||
|
|
||||||
|
## CSS variables
|
||||||
|
|
||||||
|
### Gauge
|
||||||
|
|
||||||
|
`primary-background-color`
|
||||||
|
Background color of the dial (rounded arch)
|
||||||
|
|
||||||
|
`primary-text-color`
|
||||||
|
Text color below dial (value and unit of measurement) plus needle color (if gauge is in needle mode)
|
||||||
|
|
||||||
|
#### Dial colors
|
||||||
|
|
||||||
|
`gauge-color`
|
||||||
|
Used in the coding to control what color the gauge value is rendered with, but cannot be set via theme since its value will dynamically be set (either to `info-color` or to the matching severity variable if the severity color mode is used). To control the used colors, adjust the following variables.
|
||||||
|
|
||||||
|
`success-color`
|
||||||
|
Dial color for the "green" severity level
|
||||||
|
|
||||||
|
`warning-color`
|
||||||
|
Dial color for the "yellow" severity level
|
||||||
|
|
||||||
|
`error-color`
|
||||||
|
Dial color for the "red" severity level
|
||||||
|
|
||||||
|
`info-color`
|
||||||
|
Static dial color if not in severity color mode
|
1
gallery/src/pages/components/ha-gauge.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import "../../../../src/components/ha-gauge";
|
@@ -3,6 +3,7 @@ import "@material/mwc-button";
|
|||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||||
|
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
|
||||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||||
@@ -115,11 +116,19 @@ const SCHEMAS: {
|
|||||||
name: "One of each",
|
name: "One of each",
|
||||||
input: {
|
input: {
|
||||||
entity: { name: "Entity", selector: { entity: {} } },
|
entity: { name: "Entity", selector: { entity: {} } },
|
||||||
|
state: {
|
||||||
|
name: "State",
|
||||||
|
selector: { state: { entity_id: "alarm_control_panel.alarm" } },
|
||||||
|
},
|
||||||
attribute: {
|
attribute: {
|
||||||
name: "Attribute",
|
name: "Attribute",
|
||||||
selector: { attribute: { entity_id: "" } },
|
selector: { attribute: { entity_id: "" } },
|
||||||
},
|
},
|
||||||
device: { name: "Device", selector: { device: {} } },
|
device: { name: "Device", selector: { device: {} } },
|
||||||
|
config_entry: {
|
||||||
|
name: "Integration",
|
||||||
|
selector: { config_entry: {} },
|
||||||
|
},
|
||||||
duration: { name: "Duration", selector: { duration: {} } },
|
duration: { name: "Duration", selector: { duration: {} } },
|
||||||
addon: { name: "Addon", selector: { addon: {} } },
|
addon: { name: "Addon", selector: { addon: {} } },
|
||||||
area: { name: "Area", selector: { area: {} } },
|
area: { name: "Area", selector: { area: {} } },
|
||||||
@@ -186,6 +195,48 @@ const SCHEMAS: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
select_disabled_list: {
|
||||||
|
name: "Select disabled option",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
options: [
|
||||||
|
{ label: "Option 1", value: "Option 1" },
|
||||||
|
{ label: "Option 2", value: "Option 2" },
|
||||||
|
{ label: "Option 3", value: "Option 3", disabled: true },
|
||||||
|
],
|
||||||
|
mode: "list",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select_disabled_multiple: {
|
||||||
|
name: "Select disabled option",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
multiple: true,
|
||||||
|
options: [
|
||||||
|
{ label: "Option 1", value: "Option 1" },
|
||||||
|
{ label: "Option 2", value: "Option 2" },
|
||||||
|
{ label: "Option 3", value: "Option 3", disabled: true },
|
||||||
|
],
|
||||||
|
mode: "list",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select_disabled: {
|
||||||
|
name: "Select disabled option",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
options: [
|
||||||
|
{ label: "Option 1", value: "Option 1" },
|
||||||
|
{ label: "Option 2", value: "Option 2" },
|
||||||
|
{ label: "Option 3", value: "Option 3", disabled: true },
|
||||||
|
{ label: "Option 4", value: "Option 4", disabled: true },
|
||||||
|
{ label: "Option 5", value: "Option 5", disabled: true },
|
||||||
|
{ label: "Option 6", value: "Option 6" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
select_custom: {
|
select_custom: {
|
||||||
name: "Select (Custom)",
|
name: "Select (Custom)",
|
||||||
selector: {
|
selector: {
|
||||||
@@ -276,6 +327,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
|||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
mockEntityRegistry(hass);
|
mockEntityRegistry(hass);
|
||||||
mockDeviceRegistry(hass, DEVICES);
|
mockDeviceRegistry(hass, DEVICES);
|
||||||
|
mockConfigEntries(hass);
|
||||||
mockAreaRegistry(hass, AREAS);
|
mockAreaRegistry(hass, AREAS);
|
||||||
mockHassioSupervisor(hass);
|
mockHassioSupervisor(hass);
|
||||||
hass.mockWS("auth/sign_path", (params) => params);
|
hass.mockWS("auth/sign_path", (params) => params);
|
||||||
|
39
gallery/src/pages/components/ha-switch.markdown
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
title: Switch / Toggle
|
||||||
|
---
|
||||||
|
|
||||||
|
<style>
|
||||||
|
ha-switch {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
# Switch `<ha-switch>`
|
||||||
|
|
||||||
|
A toggle switch can represent two states: on and off.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Switch in on state
|
||||||
|
<ha-switch checked></ha-switch>
|
||||||
|
|
||||||
|
Switch in off state
|
||||||
|
<ha-switch></ha-switch>
|
||||||
|
|
||||||
|
Disabled switch
|
||||||
|
<ha-switch disabled></ha-switch>
|
||||||
|
|
||||||
|
## CSS variables
|
||||||
|
|
||||||
|
For the switch / toggle there are always two variables, one for the on / checked state and one for the off / unchecked state.
|
||||||
|
|
||||||
|
The track element (background rounded rectangle that the round circular handle travels on) is set to being half transparent, so the final color will also be impacted by the color behind the track.
|
||||||
|
|
||||||
|
`switch-checked-color` / `switch-unchecked-color`
|
||||||
|
Set both the color of the round handle and the track behind it. If you want to control them separately, use the variables below instead.
|
||||||
|
|
||||||
|
`switch-checked-button-color` / `switch-unchecked-button-color`
|
||||||
|
Color of the round handle
|
||||||
|
|
||||||
|
`switch-checked-track-color` / `switch-unchecked-track-color`
|
||||||
|
Color of the track behind the round handle
|
1
gallery/src/pages/components/ha-switch.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import "../../../../src/components/ha-switch";
|
@@ -1,3 +1,3 @@
|
|||||||
---
|
---
|
||||||
title: Tips
|
title: Tip
|
||||||
---
|
---
|
||||||
|
@@ -31,7 +31,7 @@ const ENTITIES = [
|
|||||||
friendly_name: "Office Light",
|
friendly_name: "Office Light",
|
||||||
}),
|
}),
|
||||||
getEntity("fan", "kitchen", "on", {
|
getEntity("fan", "kitchen", "on", {
|
||||||
friendly_name: "Second Office Fan",
|
friendly_name: "Kitchen Fan",
|
||||||
}),
|
}),
|
||||||
getEntity("binary_sensor", "kitchen_door", "on", {
|
getEntity("binary_sensor", "kitchen_door", "on", {
|
||||||
friendly_name: "Office Door",
|
friendly_name: "Office Door",
|
||||||
@@ -102,7 +102,7 @@ class DemoArea extends LitElement {
|
|||||||
picture: "/images/office.jpg",
|
picture: "/images/office.jpg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Second Office",
|
name: "Kitchen",
|
||||||
area_id: "kitchen",
|
area_id: "kitchen",
|
||||||
picture: "/images/kitchen.png",
|
picture: "/images/kitchen.png",
|
||||||
},
|
},
|
||||||
|
@@ -75,6 +75,10 @@ const ENTITIES = [
|
|||||||
timestamp: 1641801600,
|
timestamp: 1641801600,
|
||||||
friendly_name: "Date and Time",
|
friendly_name: "Date and Time",
|
||||||
}),
|
}),
|
||||||
|
getEntity("sensor", "humidity", "23.2", {
|
||||||
|
friendly_name: "Humidity",
|
||||||
|
unit_of_measurement: "%",
|
||||||
|
}),
|
||||||
getEntity("input_select", "dropdown", "Soda", {
|
getEntity("input_select", "dropdown", "Soda", {
|
||||||
friendly_name: "Dropdown",
|
friendly_name: "Dropdown",
|
||||||
options: ["Soda", "Beer", "Wine"],
|
options: ["Soda", "Beer", "Wine"],
|
||||||
@@ -94,6 +98,9 @@ const ENTITIES = [
|
|||||||
minimum: 0,
|
minimum: 0,
|
||||||
maximum: 10,
|
maximum: 10,
|
||||||
}),
|
}),
|
||||||
|
getEntity("text", "message", "Hello!", {
|
||||||
|
friendly_name: "Message",
|
||||||
|
}),
|
||||||
|
|
||||||
getEntity("light", "unavailable", "unavailable", {
|
getEntity("light", "unavailable", "unavailable", {
|
||||||
friendly_name: "Bed Light",
|
friendly_name: "Bed Light",
|
||||||
@@ -125,6 +132,9 @@ const ENTITIES = [
|
|||||||
friendly_name: "Who cooks",
|
friendly_name: "Who cooks",
|
||||||
icon: "mdi:cheff",
|
icon: "mdi:cheff",
|
||||||
}),
|
}),
|
||||||
|
getEntity("text", "unavailable", "unavailable", {
|
||||||
|
friendly_name: "Message",
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
@@ -142,6 +152,8 @@ const CONFIGS = [
|
|||||||
- light.non_existing
|
- light.non_existing
|
||||||
- climate.ecobee
|
- climate.ecobee
|
||||||
- input_number.number
|
- input_number.number
|
||||||
|
- sensor.humidity
|
||||||
|
- text.message
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -214,6 +226,7 @@ const CONFIGS = [
|
|||||||
- climate.unavailable
|
- climate.unavailable
|
||||||
- input_number.unavailable
|
- input_number.unavailable
|
||||||
- input_select.unavailable
|
- input_select.unavailable
|
||||||
|
- text.unavailable
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -23,13 +23,12 @@ const CONFIGS = [
|
|||||||
heading: "Basic example",
|
heading: "Basic example",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
title: Humidity
|
|
||||||
entity: sensor.outside_humidity
|
entity: sensor.outside_humidity
|
||||||
name: Outside Humidity
|
name: Outside Humidity
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Custom Unit of Measurement",
|
heading: "Custom unit of measurement",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.outside_temperature
|
entity: sensor.outside_temperature
|
||||||
@@ -38,7 +37,16 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Setting Severity Levels",
|
heading: "Rendering needle",
|
||||||
|
config: `
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.outside_humidity
|
||||||
|
name: Outside Humidity
|
||||||
|
needle: true
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Setting severity levels",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.brightness
|
entity: sensor.brightness
|
||||||
@@ -50,7 +58,7 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Setting Severity Levels",
|
heading: "Setting severity levels",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.brightness_medium
|
entity: sensor.brightness_medium
|
||||||
@@ -62,7 +70,7 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Setting Severity Levels",
|
heading: "Setting severity levels",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.brightness_high
|
entity: sensor.brightness_high
|
||||||
@@ -74,7 +82,7 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Setting Min (0) and Max (15) Values",
|
heading: "Setting min (0) and mx (15) values",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.brightness
|
entity: sensor.brightness
|
||||||
@@ -84,14 +92,14 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Invalid Entity",
|
heading: "Invalid entity",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.invalid_entity
|
entity: sensor.invalid_entity
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Non-Numeric Value",
|
heading: "Non-numeric value",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: plant.bonsai
|
entity: plant.bonsai
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Introduction
|
title: Introduction
|
||||||
---
|
---
|
||||||
Lovelace has many different cards. Each card allows the user to tell
|
Dashboards have many different cards. Each card allows the user to tell
|
||||||
a different story about what is going on in their house. These cards
|
a different story about what is going on in their house. These cards
|
||||||
are very customizable, as no household is the same.
|
are very customizable, as no household is the same.
|
||||||
|
|
||||||
This gallery helps our developers and designers to see all the
|
This gallery helps our developers and designers to see all the
|
||||||
different states that each card can be in.
|
different states that each card can be in.
|
||||||
|
|
||||||
Check [the Lovelace documentation](https://www.home-assistant.io/lovelace) for instructions on how to get started with Lovelace.
|
Check [the Dashboards documentation](https://www.home-assistant.io/dashboards/) for instructions on how to get started with Dashboards.
|
||||||
|
3
gallery/src/pages/misc/entity-state.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Entity State
|
||||||
|
---
|
377
gallery/src/pages/misc/entity-state.ts
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
import {
|
||||||
|
HassEntity,
|
||||||
|
HassEntityAttributeBase,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { computeDomain } from "../../../../src/common/entity/compute_domain";
|
||||||
|
import { computeStateDisplay } from "../../../../src/common/entity/compute_state_display";
|
||||||
|
import { stateColorCss } from "../../../../src/common/entity/state_color";
|
||||||
|
import { stateIconPath } from "../../../../src/common/entity/state_icon_path";
|
||||||
|
import "../../../../src/components/data-table/ha-data-table";
|
||||||
|
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||||
|
import "../../../../src/components/ha-chip";
|
||||||
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
|
||||||
|
const SENSOR_DEVICE_CLASSES = [
|
||||||
|
"apparent_power",
|
||||||
|
"aqi",
|
||||||
|
// "battery"
|
||||||
|
"carbon_dioxide",
|
||||||
|
"carbon_monoxide",
|
||||||
|
"current",
|
||||||
|
"date",
|
||||||
|
"distance",
|
||||||
|
"duration",
|
||||||
|
"energy",
|
||||||
|
"frequency",
|
||||||
|
"gas",
|
||||||
|
"humidity",
|
||||||
|
"illuminance",
|
||||||
|
"moisture",
|
||||||
|
"monetary",
|
||||||
|
"nitrogen_dioxide",
|
||||||
|
"nitrogen_monoxide",
|
||||||
|
"nitrous_oxide",
|
||||||
|
"ozone",
|
||||||
|
"pm1",
|
||||||
|
"pm10",
|
||||||
|
"pm25",
|
||||||
|
"power_factor",
|
||||||
|
"power",
|
||||||
|
"precipitation",
|
||||||
|
"precipitation_intensity",
|
||||||
|
"pressure",
|
||||||
|
"reactive_power",
|
||||||
|
"signal_strength",
|
||||||
|
"speed",
|
||||||
|
"sulphur_dioxide",
|
||||||
|
"temperature",
|
||||||
|
"timestamp",
|
||||||
|
"volatile_organic_compounds",
|
||||||
|
"voltage",
|
||||||
|
"volume",
|
||||||
|
"water",
|
||||||
|
"weight",
|
||||||
|
"wind_speed",
|
||||||
|
];
|
||||||
|
|
||||||
|
const BINARY_SENSOR_DEVICE_CLASSES = [
|
||||||
|
"battery",
|
||||||
|
"battery_charging",
|
||||||
|
"carbon_monoxide",
|
||||||
|
"cold",
|
||||||
|
"connectivity",
|
||||||
|
"door",
|
||||||
|
"garage_door",
|
||||||
|
"gas",
|
||||||
|
"heat",
|
||||||
|
"light",
|
||||||
|
"lock",
|
||||||
|
"moisture",
|
||||||
|
"motion",
|
||||||
|
"moving",
|
||||||
|
"occupancy",
|
||||||
|
"opening",
|
||||||
|
"plug",
|
||||||
|
"power",
|
||||||
|
"presence",
|
||||||
|
"problem",
|
||||||
|
"running",
|
||||||
|
"safety",
|
||||||
|
"smoke",
|
||||||
|
"sound",
|
||||||
|
"tamper",
|
||||||
|
"update",
|
||||||
|
"vibration",
|
||||||
|
"window",
|
||||||
|
];
|
||||||
|
|
||||||
|
const ENTITIES: HassEntity[] = [
|
||||||
|
// Alarm control panel
|
||||||
|
createEntity("alarm_control_panel.disarmed", "disarmed"),
|
||||||
|
createEntity("alarm_control_panel.armed_home", "armed_home"),
|
||||||
|
createEntity("alarm_control_panel.armed_away", "armed_away"),
|
||||||
|
createEntity("alarm_control_panel.armed_night", "armed_night"),
|
||||||
|
createEntity("alarm_control_panel.armed_vacation", "armed_vacation"),
|
||||||
|
createEntity(
|
||||||
|
"alarm_control_panel.armed_custom_bypass",
|
||||||
|
"armed_custom_bypass"
|
||||||
|
),
|
||||||
|
createEntity("alarm_control_panel.pending", "pending"),
|
||||||
|
createEntity("alarm_control_panel.arming", "arming"),
|
||||||
|
createEntity("alarm_control_panel.disarming", "disarming"),
|
||||||
|
createEntity("alarm_control_panel.triggered", "triggered"),
|
||||||
|
// Binary Sensor
|
||||||
|
...BINARY_SENSOR_DEVICE_CLASSES.map((dc) =>
|
||||||
|
createEntity(`binary_sensor.${dc}`, "on", dc)
|
||||||
|
),
|
||||||
|
// Button
|
||||||
|
createEntity("button.restart", "unknown", "restart"),
|
||||||
|
createEntity("button.update", "unknown", "update"),
|
||||||
|
// Calendar
|
||||||
|
createEntity("calendar.on", "on"),
|
||||||
|
createEntity("calendar.off", "off"),
|
||||||
|
// Climate
|
||||||
|
createEntity("climate.off", "off"),
|
||||||
|
createEntity("climate.heat", "heat"),
|
||||||
|
createEntity("climate.cool", "cool"),
|
||||||
|
createEntity("climate.heat_cool", "heat_cool"),
|
||||||
|
createEntity("climate.auto", "auto"),
|
||||||
|
createEntity("climate.dry", "dry"),
|
||||||
|
createEntity("climate.fan_only", "fan_only"),
|
||||||
|
// Cover
|
||||||
|
createEntity("cover.opening", "opening"),
|
||||||
|
createEntity("cover.open", "open"),
|
||||||
|
createEntity("cover.closing", "closing"),
|
||||||
|
createEntity("cover.closed", "closed"),
|
||||||
|
createEntity("cover.awning", "open", "awning"),
|
||||||
|
createEntity("cover.blind", "open", "blind"),
|
||||||
|
createEntity("cover.curtain", "open", "curtain"),
|
||||||
|
createEntity("cover.damper", "open", "damper"),
|
||||||
|
createEntity("cover.door", "open", "door"),
|
||||||
|
createEntity("cover.garage", "open", "garage"),
|
||||||
|
createEntity("cover.gate", "open", "gate"),
|
||||||
|
createEntity("cover.shade", "open", "shade"),
|
||||||
|
createEntity("cover.shutter", "open", "shutter"),
|
||||||
|
createEntity("cover.window", "open", "window"),
|
||||||
|
// Device tracker/person
|
||||||
|
createEntity("device_tracker.home", "home"),
|
||||||
|
createEntity("device_tracker.not_home", "not_home"),
|
||||||
|
createEntity("device_tracker.work", "work"),
|
||||||
|
createEntity("person.home", "home"),
|
||||||
|
createEntity("person.not_home", "not_home"),
|
||||||
|
createEntity("person.work", "work"),
|
||||||
|
// Fan
|
||||||
|
createEntity("fan.on", "on"),
|
||||||
|
createEntity("fan.off", "off"),
|
||||||
|
// Humidifier
|
||||||
|
createEntity("humidifier.on", "on"),
|
||||||
|
createEntity("humidifier.off", "off"),
|
||||||
|
// Light
|
||||||
|
createEntity("light.on", "on"),
|
||||||
|
createEntity("light.off", "off"),
|
||||||
|
// Locks
|
||||||
|
createEntity("lock.locked", "locked"),
|
||||||
|
createEntity("lock.unlocked", "unlocked"),
|
||||||
|
createEntity("lock.locking", "locking"),
|
||||||
|
createEntity("lock.unlocking", "unlocking"),
|
||||||
|
createEntity("lock.jammed", "jammed"),
|
||||||
|
// Media Player
|
||||||
|
createEntity("media_player.off", "off"),
|
||||||
|
createEntity("media_player.on", "on"),
|
||||||
|
createEntity("media_player.idle", "idle"),
|
||||||
|
createEntity("media_player.playing", "playing"),
|
||||||
|
createEntity("media_player.paused", "paused"),
|
||||||
|
createEntity("media_player.standby", "standby"),
|
||||||
|
createEntity("media_player.buffering", "buffering"),
|
||||||
|
createEntity("media_player.tv_off", "off", "tv"),
|
||||||
|
createEntity("media_player.tv_playing", "playing", "tv"),
|
||||||
|
createEntity("media_player.tv_paused", "paused", "tv"),
|
||||||
|
createEntity("media_player.tv_standby", "standby", "tv"),
|
||||||
|
createEntity("media_player.receiver_off", "off", "receiver"),
|
||||||
|
createEntity("media_player.receiver_playing", "playing", "receiver"),
|
||||||
|
createEntity("media_player.receiver_paused", "paused", "receiver"),
|
||||||
|
createEntity("media_player.receiver_standby", "standby", "receiver"),
|
||||||
|
createEntity("media_player.speaker_off", "off", "speaker"),
|
||||||
|
createEntity("media_player.speaker_playing", "playing", "speaker"),
|
||||||
|
createEntity("media_player.speaker_paused", "paused", "speaker"),
|
||||||
|
createEntity("media_player.speaker_standby", "standby", "speaker"),
|
||||||
|
// Sensor
|
||||||
|
...SENSOR_DEVICE_CLASSES.map((dc) => createEntity(`sensor.${dc}`, "10", dc)),
|
||||||
|
// Battery sensor
|
||||||
|
...[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].map((value) =>
|
||||||
|
createEntity(`sensor.battery_${value}`, value.toString(), "battery")
|
||||||
|
),
|
||||||
|
// Siren
|
||||||
|
createEntity("siren.off", "off"),
|
||||||
|
createEntity("siren.on", "on"),
|
||||||
|
// Switch
|
||||||
|
createEntity("switch.off", "off"),
|
||||||
|
createEntity("switch.on", "on"),
|
||||||
|
createEntity("switch.outlet_off", "off", "outlet"),
|
||||||
|
createEntity("switch.outlet_on", "on", "outlet"),
|
||||||
|
createEntity("switch.switch_off", "off", "switch"),
|
||||||
|
createEntity("switch.switch_on", "on", "switch"),
|
||||||
|
// Vacuum
|
||||||
|
createEntity("vacuum.cleaning", "cleaning"),
|
||||||
|
createEntity("vacuum.docked", "docked"),
|
||||||
|
createEntity("vacuum.paused", "paused"),
|
||||||
|
createEntity("vacuum.idle", "idle"),
|
||||||
|
createEntity("vacuum.returning", "returning"),
|
||||||
|
createEntity("vacuum.error", "error"),
|
||||||
|
createEntity("vacuum.cleaning", "cleaning"),
|
||||||
|
createEntity("vacuum.off", "off"),
|
||||||
|
createEntity("vacuum.on", "on"),
|
||||||
|
// Update
|
||||||
|
createEntity("update.off", "off", undefined, {
|
||||||
|
installed_version: "1.0.0",
|
||||||
|
latest_version: "2.0.0",
|
||||||
|
}),
|
||||||
|
createEntity("update.on", "on", undefined, {
|
||||||
|
installed_version: "1.0.0",
|
||||||
|
latest_version: "2.0.0",
|
||||||
|
}),
|
||||||
|
createEntity("update.installing", "on", undefined, {
|
||||||
|
installed_version: "1.0.0",
|
||||||
|
latest_version: "2.0.0",
|
||||||
|
in_progress: true,
|
||||||
|
}),
|
||||||
|
createEntity("update.off", "off", "firmware", {
|
||||||
|
installed_version: "1.0.0",
|
||||||
|
latest_version: "2.0.0",
|
||||||
|
}),
|
||||||
|
createEntity("update.on", "on", "firmware", {
|
||||||
|
installed_version: "1.0.0",
|
||||||
|
latest_version: "2.0.0",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
function createEntity(
|
||||||
|
entity_id: string,
|
||||||
|
state: string,
|
||||||
|
device_class?: string,
|
||||||
|
attributes?: HassEntityAttributeBase | HassEntity["attributes"]
|
||||||
|
): HassEntity {
|
||||||
|
return {
|
||||||
|
entity_id,
|
||||||
|
state,
|
||||||
|
attributes: {
|
||||||
|
...attributes,
|
||||||
|
device_class: device_class,
|
||||||
|
},
|
||||||
|
last_changed: new Date().toString(),
|
||||||
|
last_updated: new Date().toString(),
|
||||||
|
context: {
|
||||||
|
id: "1",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type EntityRowData = {
|
||||||
|
stateObj: HassEntity;
|
||||||
|
entity_id: string;
|
||||||
|
state: string;
|
||||||
|
device_class?: string;
|
||||||
|
domain: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createRowData(stateObj: HassEntity): EntityRowData {
|
||||||
|
return {
|
||||||
|
stateObj,
|
||||||
|
entity_id: stateObj.entity_id,
|
||||||
|
state: stateObj.state,
|
||||||
|
device_class: stateObj.attributes.device_class,
|
||||||
|
domain: computeDomain(stateObj.entity_id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("demo-misc-entity-state")
|
||||||
|
export class DemoEntityState extends LitElement {
|
||||||
|
@property({ attribute: false }) hass?: HomeAssistant;
|
||||||
|
|
||||||
|
private _columns = memoizeOne(
|
||||||
|
(hass: HomeAssistant): DataTableColumnContainer => {
|
||||||
|
const columns: DataTableColumnContainer<EntityRowData> = {
|
||||||
|
icon: {
|
||||||
|
title: "Icon",
|
||||||
|
template: (_, entry) => {
|
||||||
|
const cssColor = stateColorCss(entry.stateObj);
|
||||||
|
return html`
|
||||||
|
<ha-svg-icon
|
||||||
|
style=${styleMap({
|
||||||
|
color: `rgb(${cssColor})`,
|
||||||
|
})}
|
||||||
|
.path=${stateIconPath(entry.stateObj)}
|
||||||
|
>
|
||||||
|
</ha-svg-icon>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
entity_id: {
|
||||||
|
title: "Entity id",
|
||||||
|
width: "30%",
|
||||||
|
filterable: true,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
title: "State",
|
||||||
|
width: "20%",
|
||||||
|
sortable: true,
|
||||||
|
template: (_, entry) =>
|
||||||
|
html`${computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
entry.stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.entities
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
device_class: {
|
||||||
|
title: "Device class",
|
||||||
|
template: (dc) => html`${dc ?? "-"}`,
|
||||||
|
width: "20%",
|
||||||
|
filterable: true,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
domain: {
|
||||||
|
title: "Domain",
|
||||||
|
template: (_, entry) => html`${computeDomain(entry.entity_id)}`,
|
||||||
|
width: "20%",
|
||||||
|
filterable: true,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _rows = memoizeOne((): EntityRowData[] =>
|
||||||
|
ENTITIES.map(createRowData)
|
||||||
|
);
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("config", "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-data-table
|
||||||
|
.hass=${this.hass}
|
||||||
|
.columns=${this._columns(this.hass)}
|
||||||
|
.data=${this._rows()}
|
||||||
|
auto-height
|
||||||
|
></ha-data-table>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
.color {
|
||||||
|
display: block;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgb(--color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-misc-entity-state": DemoEntityState;
|
||||||
|
}
|
||||||
|
}
|
@@ -191,9 +191,12 @@ const createEntityRegistryEntries = (
|
|||||||
hidden_by: null,
|
hidden_by: null,
|
||||||
entity_category: null,
|
entity_category: null,
|
||||||
entity_id: "binary_sensor.updater",
|
entity_id: "binary_sensor.updater",
|
||||||
|
id: "binary_sensor.updater",
|
||||||
name: null,
|
name: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
platform: "updater",
|
platform: "updater",
|
||||||
|
has_entity_name: false,
|
||||||
|
unique_id: "updater",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -280,7 +283,7 @@ export class DemoIntegrationCard extends LitElement {
|
|||||||
.deviceRegistryEntries=${createDeviceRegistryEntries(
|
.deviceRegistryEntries=${createDeviceRegistryEntries(
|
||||||
info.items[0]
|
info.items[0]
|
||||||
)}
|
)}
|
||||||
?disabled=${info.disabled}
|
?entryDisabled=${info.disabled}
|
||||||
.selectedConfigEntryId=${info.highlight}
|
.selectedConfigEntryId=${info.highlight}
|
||||||
></ha-integration-card>
|
></ha-integration-card>
|
||||||
`
|
`
|
||||||
|
@@ -1,16 +1,7 @@
|
|||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import { CoverEntityFeature } from "../../../../src/data/cover";
|
||||||
SUPPORT_OPEN,
|
|
||||||
SUPPORT_STOP,
|
|
||||||
SUPPORT_CLOSE,
|
|
||||||
SUPPORT_SET_POSITION,
|
|
||||||
SUPPORT_OPEN_TILT,
|
|
||||||
SUPPORT_STOP_TILT,
|
|
||||||
SUPPORT_CLOSE_TILT,
|
|
||||||
SUPPORT_SET_TILT_POSITION,
|
|
||||||
} from "../../../../src/data/cover";
|
|
||||||
import "../../../../src/dialogs/more-info/more-info-content";
|
import "../../../../src/dialogs/more-info/more-info-content";
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
import {
|
import {
|
||||||
@@ -22,113 +13,127 @@ import "../../components/demo-more-infos";
|
|||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
getEntity("cover", "position_buttons", "on", {
|
getEntity("cover", "position_buttons", "on", {
|
||||||
friendly_name: "Position Buttons",
|
friendly_name: "Position Buttons",
|
||||||
supported_features: SUPPORT_OPEN + SUPPORT_STOP + SUPPORT_CLOSE,
|
supported_features:
|
||||||
|
CoverEntityFeature.OPEN +
|
||||||
|
CoverEntityFeature.STOP +
|
||||||
|
CoverEntityFeature.CLOSE,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_half", "on", {
|
getEntity("cover", "position_slider_half", "on", {
|
||||||
friendly_name: "Position Half-Open",
|
friendly_name: "Position Half-Open",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN + SUPPORT_STOP + SUPPORT_CLOSE + SUPPORT_SET_POSITION,
|
CoverEntityFeature.OPEN +
|
||||||
|
CoverEntityFeature.STOP +
|
||||||
|
CoverEntityFeature.CLOSE +
|
||||||
|
CoverEntityFeature.SET_POSITION,
|
||||||
current_position: 50,
|
current_position: 50,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_open", "on", {
|
getEntity("cover", "position_slider_open", "on", {
|
||||||
friendly_name: "Position Open",
|
friendly_name: "Position Open",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN + SUPPORT_STOP + SUPPORT_CLOSE + SUPPORT_SET_POSITION,
|
CoverEntityFeature.OPEN +
|
||||||
|
CoverEntityFeature.STOP +
|
||||||
|
CoverEntityFeature.CLOSE +
|
||||||
|
CoverEntityFeature.SET_POSITION,
|
||||||
current_position: 100,
|
current_position: 100,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_closed", "on", {
|
getEntity("cover", "position_slider_closed", "on", {
|
||||||
friendly_name: "Position Closed",
|
friendly_name: "Position Closed",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN + SUPPORT_STOP + SUPPORT_CLOSE + SUPPORT_SET_POSITION,
|
CoverEntityFeature.OPEN +
|
||||||
|
CoverEntityFeature.STOP +
|
||||||
|
CoverEntityFeature.CLOSE +
|
||||||
|
CoverEntityFeature.SET_POSITION,
|
||||||
current_position: 0,
|
current_position: 0,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "tilt_buttons", "on", {
|
getEntity("cover", "tilt_buttons", "on", {
|
||||||
friendly_name: "Tilt Buttons",
|
friendly_name: "Tilt Buttons",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN_TILT + SUPPORT_STOP_TILT + SUPPORT_CLOSE_TILT,
|
CoverEntityFeature.OPEN_TILT +
|
||||||
|
CoverEntityFeature.STOP_TILT +
|
||||||
|
CoverEntityFeature.CLOSE_TILT,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "tilt_slider_half", "on", {
|
getEntity("cover", "tilt_slider_half", "on", {
|
||||||
friendly_name: "Tilt Half-Open",
|
friendly_name: "Tilt Half-Open",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT +
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
SUPPORT_SET_TILT_POSITION,
|
CoverEntityFeature.SET_TILT_POSITION,
|
||||||
current_tilt_position: 50,
|
current_tilt_position: 50,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "tilt_slider_open", "on", {
|
getEntity("cover", "tilt_slider_open", "on", {
|
||||||
friendly_name: "Tilt Open",
|
friendly_name: "Tilt Open",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT +
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
SUPPORT_SET_TILT_POSITION,
|
CoverEntityFeature.SET_TILT_POSITION,
|
||||||
current_tilt_position: 100,
|
current_tilt_position: 100,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "tilt_slider_closed", "on", {
|
getEntity("cover", "tilt_slider_closed", "on", {
|
||||||
friendly_name: "Tilt Closed",
|
friendly_name: "Tilt Closed",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT +
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
SUPPORT_SET_TILT_POSITION,
|
CoverEntityFeature.SET_TILT_POSITION,
|
||||||
current_tilt_position: 0,
|
current_tilt_position: 0,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_tilt_slider", "on", {
|
getEntity("cover", "position_slider_tilt_slider", "on", {
|
||||||
friendly_name: "Both Sliders",
|
friendly_name: "Both Sliders",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN +
|
CoverEntityFeature.OPEN +
|
||||||
SUPPORT_STOP +
|
CoverEntityFeature.STOP +
|
||||||
SUPPORT_CLOSE +
|
CoverEntityFeature.CLOSE +
|
||||||
SUPPORT_SET_POSITION +
|
CoverEntityFeature.SET_POSITION +
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT +
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
SUPPORT_SET_TILT_POSITION,
|
CoverEntityFeature.SET_TILT_POSITION,
|
||||||
current_position: 30,
|
current_position: 30,
|
||||||
current_tilt_position: 70,
|
current_tilt_position: 70,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_tilt_slider", "on", {
|
getEntity("cover", "position_tilt_slider", "on", {
|
||||||
friendly_name: "Position & Tilt Slider",
|
friendly_name: "Position & Tilt Slider",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN +
|
CoverEntityFeature.OPEN +
|
||||||
SUPPORT_STOP +
|
CoverEntityFeature.STOP +
|
||||||
SUPPORT_CLOSE +
|
CoverEntityFeature.CLOSE +
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT +
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
SUPPORT_SET_TILT_POSITION,
|
CoverEntityFeature.SET_TILT_POSITION,
|
||||||
current_tilt_position: 70,
|
current_tilt_position: 70,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_tilt", "on", {
|
getEntity("cover", "position_slider_tilt", "on", {
|
||||||
friendly_name: "Position Slider & Tilt",
|
friendly_name: "Position Slider & Tilt",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_OPEN +
|
CoverEntityFeature.OPEN +
|
||||||
SUPPORT_STOP +
|
CoverEntityFeature.STOP +
|
||||||
SUPPORT_CLOSE +
|
CoverEntityFeature.CLOSE +
|
||||||
SUPPORT_SET_POSITION +
|
CoverEntityFeature.SET_POSITION +
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT,
|
CoverEntityFeature.CLOSE_TILT,
|
||||||
current_position: 30,
|
current_position: 30,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_only_tilt_slider", "on", {
|
getEntity("cover", "position_slider_only_tilt_slider", "on", {
|
||||||
friendly_name: "Position Slider Only & Tilt Buttons",
|
friendly_name: "Position Slider Only & Tilt Buttons",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_SET_POSITION +
|
CoverEntityFeature.SET_POSITION +
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT,
|
CoverEntityFeature.CLOSE_TILT,
|
||||||
current_position: 30,
|
current_position: 30,
|
||||||
}),
|
}),
|
||||||
getEntity("cover", "position_slider_only_tilt", "on", {
|
getEntity("cover", "position_slider_only_tilt", "on", {
|
||||||
friendly_name: "Position Slider Only & Tilt",
|
friendly_name: "Position Slider Only & Tilt",
|
||||||
supported_features:
|
supported_features:
|
||||||
SUPPORT_SET_POSITION +
|
CoverEntityFeature.SET_POSITION +
|
||||||
SUPPORT_OPEN_TILT +
|
CoverEntityFeature.OPEN_TILT +
|
||||||
SUPPORT_STOP_TILT +
|
CoverEntityFeature.STOP_TILT +
|
||||||
SUPPORT_CLOSE_TILT +
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
SUPPORT_SET_TILT_POSITION,
|
CoverEntityFeature.SET_TILT_POSITION,
|
||||||
current_position: 30,
|
current_position: 30,
|
||||||
current_tilt_position: 70,
|
current_tilt_position: 70,
|
||||||
}),
|
}),
|
||||||
|
3
gallery/src/pages/more-info/input-number.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Input Number
|
||||||
|
---
|
60
gallery/src/pages/more-info/input-number.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/dialogs/more-info/more-info-content";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import {
|
||||||
|
MockHomeAssistant,
|
||||||
|
provideHass,
|
||||||
|
} from "../../../../src/fake_data/provide_hass";
|
||||||
|
import "../../components/demo-more-infos";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("input_number", "box1", 0, {
|
||||||
|
friendly_name: "Box1",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
initial: 0,
|
||||||
|
mode: "box",
|
||||||
|
unit_of_measurement: "items",
|
||||||
|
}),
|
||||||
|
getEntity("input_number", "slider1", 0, {
|
||||||
|
friendly_name: "Slider1",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
initial: 0,
|
||||||
|
mode: "slider",
|
||||||
|
unit_of_measurement: "items",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-input-number")
|
||||||
|
class DemoMoreInfoInputNumber extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-input-number": DemoMoreInfoInputNumber;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +1,7 @@
|
|||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import { LightColorMode, LightEntityFeature } from "../../../../src/data/light";
|
||||||
LightColorModes,
|
|
||||||
SUPPORT_EFFECT,
|
|
||||||
SUPPORT_FLASH,
|
|
||||||
SUPPORT_TRANSITION,
|
|
||||||
} from "../../../../src/data/light";
|
|
||||||
import "../../../../src/dialogs/more-info/more-info-content";
|
import "../../../../src/dialogs/more-info/more-info-content";
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
import {
|
import {
|
||||||
@@ -22,8 +17,8 @@ const ENTITIES = [
|
|||||||
getEntity("light", "kitchen_light", "on", {
|
getEntity("light", "kitchen_light", "on", {
|
||||||
friendly_name: "Brightness Light",
|
friendly_name: "Brightness Light",
|
||||||
brightness: 200,
|
brightness: 200,
|
||||||
supported_color_modes: [LightColorModes.BRIGHTNESS],
|
supported_color_modes: [LightColorMode.BRIGHTNESS],
|
||||||
color_mode: LightColorModes.BRIGHTNESS,
|
color_mode: LightColorMode.BRIGHTNESS,
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_temperature_light", "on", {
|
getEntity("light", "color_temperature_light", "on", {
|
||||||
friendly_name: "White Color Temperature Light",
|
friendly_name: "White Color Temperature Light",
|
||||||
@@ -32,10 +27,10 @@ const ENTITIES = [
|
|||||||
min_mireds: 30,
|
min_mireds: 30,
|
||||||
max_mireds: 150,
|
max_mireds: 150,
|
||||||
supported_color_modes: [
|
supported_color_modes: [
|
||||||
LightColorModes.BRIGHTNESS,
|
LightColorMode.BRIGHTNESS,
|
||||||
LightColorModes.COLOR_TEMP,
|
LightColorMode.COLOR_TEMP,
|
||||||
],
|
],
|
||||||
color_mode: LightColorModes.COLOR_TEMP,
|
color_mode: LightColorMode.COLOR_TEMP,
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_hs_light", "on", {
|
getEntity("light", "color_hs_light", "on", {
|
||||||
friendly_name: "Color HS Light",
|
friendly_name: "Color HS Light",
|
||||||
@@ -44,13 +39,16 @@ const ENTITIES = [
|
|||||||
rgb_color: [30, 100, 255],
|
rgb_color: [30, 100, 255],
|
||||||
min_mireds: 30,
|
min_mireds: 30,
|
||||||
max_mireds: 150,
|
max_mireds: 150,
|
||||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
supported_features:
|
||||||
|
LightEntityFeature.EFFECT +
|
||||||
|
LightEntityFeature.FLASH +
|
||||||
|
LightEntityFeature.TRANSITION,
|
||||||
supported_color_modes: [
|
supported_color_modes: [
|
||||||
LightColorModes.BRIGHTNESS,
|
LightColorMode.BRIGHTNESS,
|
||||||
LightColorModes.COLOR_TEMP,
|
LightColorMode.COLOR_TEMP,
|
||||||
LightColorModes.HS,
|
LightColorMode.HS,
|
||||||
],
|
],
|
||||||
color_mode: LightColorModes.HS,
|
color_mode: LightColorMode.HS,
|
||||||
effect_list: ["random", "colorloop"],
|
effect_list: ["random", "colorloop"],
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_rgb_ct_light", "on", {
|
getEntity("light", "color_rgb_ct_light", "on", {
|
||||||
@@ -59,22 +57,28 @@ const ENTITIES = [
|
|||||||
color_temp: 75,
|
color_temp: 75,
|
||||||
min_mireds: 30,
|
min_mireds: 30,
|
||||||
max_mireds: 150,
|
max_mireds: 150,
|
||||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
supported_features:
|
||||||
|
LightEntityFeature.EFFECT +
|
||||||
|
LightEntityFeature.FLASH +
|
||||||
|
LightEntityFeature.TRANSITION,
|
||||||
supported_color_modes: [
|
supported_color_modes: [
|
||||||
LightColorModes.BRIGHTNESS,
|
LightColorMode.BRIGHTNESS,
|
||||||
LightColorModes.COLOR_TEMP,
|
LightColorMode.COLOR_TEMP,
|
||||||
LightColorModes.RGB,
|
LightColorMode.RGB,
|
||||||
],
|
],
|
||||||
color_mode: LightColorModes.COLOR_TEMP,
|
color_mode: LightColorMode.COLOR_TEMP,
|
||||||
effect_list: ["random", "colorloop"],
|
effect_list: ["random", "colorloop"],
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_RGB_light", "on", {
|
getEntity("light", "color_RGB_light", "on", {
|
||||||
friendly_name: "Color Effets Light",
|
friendly_name: "Color Effects Light",
|
||||||
brightness: 255,
|
brightness: 255,
|
||||||
rgb_color: [30, 100, 255],
|
rgb_color: [30, 100, 255],
|
||||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
supported_features:
|
||||||
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
|
LightEntityFeature.EFFECT +
|
||||||
color_mode: LightColorModes.RGB,
|
LightEntityFeature.FLASH +
|
||||||
|
LightEntityFeature.TRANSITION,
|
||||||
|
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
|
||||||
|
color_mode: LightColorMode.RGB,
|
||||||
effect_list: ["random", "colorloop"],
|
effect_list: ["random", "colorloop"],
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_rgbw_light", "on", {
|
getEntity("light", "color_rgbw_light", "on", {
|
||||||
@@ -83,13 +87,16 @@ const ENTITIES = [
|
|||||||
rgbw_color: [30, 100, 255, 125],
|
rgbw_color: [30, 100, 255, 125],
|
||||||
min_mireds: 30,
|
min_mireds: 30,
|
||||||
max_mireds: 150,
|
max_mireds: 150,
|
||||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
supported_features:
|
||||||
|
LightEntityFeature.EFFECT +
|
||||||
|
LightEntityFeature.FLASH +
|
||||||
|
LightEntityFeature.TRANSITION,
|
||||||
supported_color_modes: [
|
supported_color_modes: [
|
||||||
LightColorModes.BRIGHTNESS,
|
LightColorMode.BRIGHTNESS,
|
||||||
LightColorModes.COLOR_TEMP,
|
LightColorMode.COLOR_TEMP,
|
||||||
LightColorModes.RGBW,
|
LightColorMode.RGBW,
|
||||||
],
|
],
|
||||||
color_mode: LightColorModes.RGBW,
|
color_mode: LightColorMode.RGBW,
|
||||||
effect_list: ["random", "colorloop"],
|
effect_list: ["random", "colorloop"],
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_rgbww_light", "on", {
|
getEntity("light", "color_rgbww_light", "on", {
|
||||||
@@ -98,13 +105,16 @@ const ENTITIES = [
|
|||||||
rgbww_color: [30, 100, 255, 125, 10],
|
rgbww_color: [30, 100, 255, 125, 10],
|
||||||
min_mireds: 30,
|
min_mireds: 30,
|
||||||
max_mireds: 150,
|
max_mireds: 150,
|
||||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
supported_features:
|
||||||
|
LightEntityFeature.EFFECT +
|
||||||
|
LightEntityFeature.FLASH +
|
||||||
|
LightEntityFeature.TRANSITION,
|
||||||
supported_color_modes: [
|
supported_color_modes: [
|
||||||
LightColorModes.BRIGHTNESS,
|
LightColorMode.BRIGHTNESS,
|
||||||
LightColorModes.COLOR_TEMP,
|
LightColorMode.COLOR_TEMP,
|
||||||
LightColorModes.RGBWW,
|
LightColorMode.RGBWW,
|
||||||
],
|
],
|
||||||
color_mode: LightColorModes.RGBWW,
|
color_mode: LightColorMode.RGBWW,
|
||||||
effect_list: ["random", "colorloop"],
|
effect_list: ["random", "colorloop"],
|
||||||
}),
|
}),
|
||||||
getEntity("light", "color_xy_light", "on", {
|
getEntity("light", "color_xy_light", "on", {
|
||||||
@@ -114,13 +124,16 @@ const ENTITIES = [
|
|||||||
rgb_color: [30, 100, 255],
|
rgb_color: [30, 100, 255],
|
||||||
min_mireds: 30,
|
min_mireds: 30,
|
||||||
max_mireds: 150,
|
max_mireds: 150,
|
||||||
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
|
supported_features:
|
||||||
|
LightEntityFeature.EFFECT +
|
||||||
|
LightEntityFeature.FLASH +
|
||||||
|
LightEntityFeature.TRANSITION,
|
||||||
supported_color_modes: [
|
supported_color_modes: [
|
||||||
LightColorModes.BRIGHTNESS,
|
LightColorMode.BRIGHTNESS,
|
||||||
LightColorModes.COLOR_TEMP,
|
LightColorMode.COLOR_TEMP,
|
||||||
LightColorModes.XY,
|
LightColorMode.XY,
|
||||||
],
|
],
|
||||||
color_mode: LightColorModes.XY,
|
color_mode: LightColorMode.XY,
|
||||||
effect_list: ["random", "colorloop"],
|
effect_list: ["random", "colorloop"],
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
@@ -139,6 +139,13 @@ const ENTITIES = [
|
|||||||
title: undefined,
|
title: undefined,
|
||||||
friendly_name: "Installing without title",
|
friendly_name: "Installing without title",
|
||||||
}),
|
}),
|
||||||
|
getEntity("update", "update21", "on", {
|
||||||
|
...base_attributes,
|
||||||
|
in_progress: true,
|
||||||
|
friendly_name: "Update with in_progress true and UPDATE_SUPPORT_PROGRESS",
|
||||||
|
supported_features:
|
||||||
|
base_attributes.supported_features + UPDATE_SUPPORT_PROGRESS,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-more-info-update")
|
@customElement("demo-more-info-update")
|
||||||
|
@@ -6,10 +6,8 @@ import { atLeastVersion } from "../../../src/common/config/version";
|
|||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import {
|
import { HassioAddonRepository } from "../../../src/data/hassio/addon";
|
||||||
HassioAddonInfo,
|
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||||
HassioAddonRepository,
|
|
||||||
} from "../../../src/data/hassio/addon";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import "../components/hassio-card-content";
|
import "../components/hassio-card-content";
|
||||||
@@ -23,20 +21,16 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
||||||
|
|
||||||
@property({ attribute: false }) public addons!: HassioAddonInfo[];
|
@property({ attribute: false }) public addons!: StoreAddon[];
|
||||||
|
|
||||||
@property() public filter!: string;
|
@property() public filter!: string;
|
||||||
|
|
||||||
private _getAddons = memoizeOne(
|
private _getAddons = memoizeOne((addons: StoreAddon[], filter?: string) => {
|
||||||
(addons: HassioAddonInfo[], filter?: string) => {
|
if (filter) {
|
||||||
if (filter) {
|
return filterAndSort(addons, filter);
|
||||||
return filterAndSort(addons, filter);
|
|
||||||
}
|
|
||||||
return addons.sort((a, b) =>
|
|
||||||
caseInsensitiveStringCompare(a.name, b.name)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
return addons.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name));
|
||||||
|
});
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const repo = this.repo;
|
const repo = this.repo;
|
||||||
@@ -87,10 +81,10 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
? this.supervisor.localize(
|
? this.supervisor.localize(
|
||||||
"common.new_version_available"
|
"common.new_version_available"
|
||||||
)
|
)
|
||||||
: this.supervisor.localize("addon.installed")
|
: this.supervisor.localize("addon.state.installed")
|
||||||
: addon.available
|
: addon.available
|
||||||
? this.supervisor.localize("addon.not_installed")
|
? this.supervisor.localize("addon.state.not_installed")
|
||||||
: this.supervisor.localize("addon.not_available")}
|
: this.supervisor.localize("addon.state.not_available")}
|
||||||
.iconClass=${addon.installed
|
.iconClass=${addon.installed
|
||||||
? addon.update_available
|
? addon.update_available
|
||||||
? "update"
|
? "update"
|
||||||
@@ -124,7 +118,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addonTapped(ev) {
|
private _addonTapped(ev) {
|
||||||
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}`);
|
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}?store=true`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -14,16 +14,18 @@ import memoizeOne from "memoize-one";
|
|||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import "../../../src/components/search-input";
|
|
||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-icon-button";
|
import "../../../src/components/ha-icon-button";
|
||||||
|
import "../../../src/components/search-input";
|
||||||
import {
|
import {
|
||||||
HassioAddonInfo,
|
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
reloadHassioAddons,
|
reloadHassioAddons,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
|
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
|
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import "../../../src/layouts/hass-loading-screen";
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../../../src/layouts/hass-subpage";
|
import "../../../src/layouts/hass-subpage";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
@@ -59,17 +61,24 @@ class HassioAddonStore extends LitElement {
|
|||||||
@state() private _filter?: string;
|
@state() private _filter?: string;
|
||||||
|
|
||||||
public async refreshData() {
|
public async refreshData() {
|
||||||
await reloadHassioAddons(this.hass);
|
try {
|
||||||
await this._loadData();
|
await reloadHassioAddons(this.hass);
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
await this._loadData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
let repos: TemplateResult[] = [];
|
let repos: TemplateResult[] = [];
|
||||||
|
|
||||||
if (this.supervisor.addon.repositories) {
|
if (this.supervisor.store.repositories) {
|
||||||
repos = this.addonRepositories(
|
repos = this.addonRepositories(
|
||||||
this.supervisor.addon.repositories,
|
this.supervisor.store.repositories,
|
||||||
this.supervisor.addon.addons,
|
this.supervisor.store.addons,
|
||||||
this._filter
|
this._filter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -145,7 +154,7 @@ class HassioAddonStore extends LitElement {
|
|||||||
private addonRepositories = memoizeOne(
|
private addonRepositories = memoizeOne(
|
||||||
(
|
(
|
||||||
repositories: HassioAddonRepository[],
|
repositories: HassioAddonRepository[],
|
||||||
addons: HassioAddonInfo[],
|
addons: StoreAddon[],
|
||||||
filter?: string
|
filter?: string
|
||||||
) =>
|
) =>
|
||||||
repositories.sort(sortRepos).map((repo) => {
|
repositories.sort(sortRepos).map((repo) => {
|
||||||
|