mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-11 18:29:27 +00:00
Compare commits
1125 Commits
analytics-
...
Move-Devel
Author | SHA1 | Date | |
---|---|---|---|
![]() |
728ea265e2 | ||
![]() |
d859b61365 | ||
![]() |
50bf69860f | ||
![]() |
cf527e4bc2 | ||
![]() |
197b581e8e | ||
![]() |
f75bf1f676 | ||
![]() |
28df79cfda | ||
![]() |
3bf19883a8 | ||
![]() |
303e065433 | ||
![]() |
7ad0b37a9e | ||
![]() |
930c7e4afa | ||
![]() |
81faae6f74 | ||
![]() |
f7fc83ac12 | ||
![]() |
21a099ee9f | ||
![]() |
7d1ce1b240 | ||
![]() |
d1f1309198 | ||
![]() |
68dd818f7a | ||
![]() |
50bea33a19 | ||
![]() |
27cae037ce | ||
![]() |
dbb5bf7550 | ||
![]() |
9ef743a695 | ||
![]() |
f3642a1677 | ||
![]() |
2d651c2a66 | ||
![]() |
ef39317019 | ||
![]() |
441f1fbcb5 | ||
![]() |
09a27a6791 | ||
![]() |
32bbdc194a | ||
![]() |
52588a3915 | ||
![]() |
9fffc93e5d | ||
![]() |
effec839af | ||
![]() |
884ed561a1 | ||
![]() |
4165e64ce0 | ||
![]() |
6053b64b2e | ||
![]() |
ed462dc257 | ||
![]() |
74a05929be | ||
![]() |
5e388b1f02 | ||
![]() |
ff2fa9a78c | ||
![]() |
6d1be9e73f | ||
![]() |
ba570f4004 | ||
![]() |
6ab497edf8 | ||
![]() |
8c1cd273df | ||
![]() |
8f68bcbba9 | ||
![]() |
8291cf9daa | ||
![]() |
bb40e66833 | ||
![]() |
f852208eff | ||
![]() |
3bbe1603eb | ||
![]() |
25d60e11da | ||
![]() |
78d06426cf | ||
![]() |
320b2bb48b | ||
![]() |
a7b8382617 | ||
![]() |
77fe687ec2 | ||
![]() |
4b9ff641ba | ||
![]() |
1520b5832a | ||
![]() |
04f2e2e70c | ||
![]() |
920d2972ea | ||
![]() |
e94fc493b8 | ||
![]() |
3e22270c2c | ||
![]() |
27fa34e24e | ||
![]() |
4e0cebaf32 | ||
![]() |
f021480bc5 | ||
![]() |
34c3374d84 | ||
![]() |
4cb7154917 | ||
![]() |
08863348dc | ||
![]() |
2bcf816b77 | ||
![]() |
d2b99e6963 | ||
![]() |
48a800882e | ||
![]() |
595e13ecac | ||
![]() |
5261d583a8 | ||
![]() |
5c488f8298 | ||
![]() |
6fc87a6f66 | ||
![]() |
3133f9b01f | ||
![]() |
2c0d330f1f | ||
![]() |
fb9ea981ed | ||
![]() |
63c113f78d | ||
![]() |
a67799a670 | ||
![]() |
e3d78d6dc5 | ||
![]() |
76a4b1efbd | ||
![]() |
882e79524b | ||
![]() |
86b9eb0bd7 | ||
![]() |
011cbe7d22 | ||
![]() |
be72bf7b3c | ||
![]() |
3e062ba673 | ||
![]() |
322d965539 | ||
![]() |
7b840527b5 | ||
![]() |
dced053ba2 | ||
![]() |
fe4322e64b | ||
![]() |
b6d6e2fd4b | ||
![]() |
2bbb1bfa7e | ||
![]() |
e2af8ac3cc | ||
![]() |
25ff5fef14 | ||
![]() |
2f9c088091 | ||
![]() |
50c397901b | ||
![]() |
1f7d4c25d4 | ||
![]() |
29819fac23 | ||
![]() |
cc301df57d | ||
![]() |
7d5b566312 | ||
![]() |
07cd68f5d0 | ||
![]() |
99bf6fa781 | ||
![]() |
bfad1eb5ac | ||
![]() |
6f9b2ee569 | ||
![]() |
4ebdca2a46 | ||
![]() |
fc700fdaf0 | ||
![]() |
d8e12f4280 | ||
![]() |
86114758c3 | ||
![]() |
792278cf17 | ||
![]() |
b8832f2121 | ||
![]() |
76339c90f7 | ||
![]() |
b3d4451035 | ||
![]() |
dc58481918 | ||
![]() |
14af735507 | ||
![]() |
a7b558b64a | ||
![]() |
b7665bef6f | ||
![]() |
5ec37a35f1 | ||
![]() |
91bb2ddcc4 | ||
![]() |
85168b3a35 | ||
![]() |
942150cda2 | ||
![]() |
2606d55895 | ||
![]() |
1f671198aa | ||
![]() |
deb65e7108 | ||
![]() |
cd00f7f874 | ||
![]() |
2b0359edba | ||
![]() |
35e9687170 | ||
![]() |
b730676914 | ||
![]() |
2890192c05 | ||
![]() |
bfb84a834f | ||
![]() |
ca6fd6c770 | ||
![]() |
585648ac4c | ||
![]() |
bec5c564b6 | ||
![]() |
48c66e6349 | ||
![]() |
cea40610c0 | ||
![]() |
0c3fd8f3ad | ||
![]() |
02bdeebc82 | ||
![]() |
60c7669d8f | ||
![]() |
919bf94a03 | ||
![]() |
ead5e288eb | ||
![]() |
add8a702cc | ||
![]() |
39774c0e02 | ||
![]() |
149f381bc3 | ||
![]() |
faccb12430 | ||
![]() |
7039bae9be | ||
![]() |
0a7b703d57 | ||
![]() |
24e8028e8f | ||
![]() |
8412cd71cb | ||
![]() |
5c78b74005 | ||
![]() |
2459477ec4 | ||
![]() |
a065740c91 | ||
![]() |
f3104d3c93 | ||
![]() |
1916c179b4 | ||
![]() |
e8b9766eb6 | ||
![]() |
ff7a2c8cb7 | ||
![]() |
7ccde2cb41 | ||
![]() |
d6b9b16f02 | ||
![]() |
66df15007a | ||
![]() |
f164d21c44 | ||
![]() |
911d322aac | ||
![]() |
419879ee7a | ||
![]() |
c3e1a2edf0 | ||
![]() |
8f5751d5bb | ||
![]() |
4095450476 | ||
![]() |
e61f587c51 | ||
![]() |
d43d19190e | ||
![]() |
a283acaabf | ||
![]() |
ea18fc0078 | ||
![]() |
1df11e9bf1 | ||
![]() |
c71b2e6b9d | ||
![]() |
db4aa05bf4 | ||
![]() |
a54a2a54f8 | ||
![]() |
0bcb4d0e09 | ||
![]() |
95dbc811d3 | ||
![]() |
e28a11964e | ||
![]() |
46a9e36516 | ||
![]() |
e99f20c4f3 | ||
![]() |
2100603cdc | ||
![]() |
da4942aca3 | ||
![]() |
7c78fb314e | ||
![]() |
5bc2468cbc | ||
![]() |
a580904c52 | ||
![]() |
48d12ceafe | ||
![]() |
60ce805b3b | ||
![]() |
251416b51d | ||
![]() |
c41c6eedd8 | ||
![]() |
6877fd9e00 | ||
![]() |
4cc104a99f | ||
![]() |
6494177821 | ||
![]() |
cea1a62867 | ||
![]() |
a6b5262d02 | ||
![]() |
2a5fc5181e | ||
![]() |
2fe8f5ff27 | ||
![]() |
0c75d5afc9 | ||
![]() |
cf062bf0f4 | ||
![]() |
acf4d59fde | ||
![]() |
05333ac2d9 | ||
![]() |
4b49da58b1 | ||
![]() |
68373e6372 | ||
![]() |
01049e8eb8 | ||
![]() |
87f7981144 | ||
![]() |
ceac9834b9 | ||
![]() |
ac8f748656 | ||
![]() |
1d97d8dca9 | ||
![]() |
fd6785b593 | ||
![]() |
d5fc751da6 | ||
![]() |
933fd72629 | ||
![]() |
0611133065 | ||
![]() |
02644b923f | ||
![]() |
67f06112c6 | ||
![]() |
49e39644f3 | ||
![]() |
990ad1bb67 | ||
![]() |
dbbf246060 | ||
![]() |
d2c20837a5 | ||
![]() |
e91d1777d0 | ||
![]() |
a5be143c3b | ||
![]() |
0ef07e4835 | ||
![]() |
9361e4cf9c | ||
![]() |
e7fd75703f | ||
![]() |
2c0b2f4bc5 | ||
![]() |
faec09f0d1 | ||
![]() |
b79c06ad71 | ||
![]() |
5614e0d29c | ||
![]() |
0b7fc177f9 | ||
![]() |
367322415e | ||
![]() |
117b50f3ea | ||
![]() |
366aa8aed1 | ||
![]() |
43011179eb | ||
![]() |
6177d2b416 | ||
![]() |
f70485bc49 | ||
![]() |
921763b5f1 | ||
![]() |
5fd4315789 | ||
![]() |
ed291b57d0 | ||
![]() |
f833701e7c | ||
![]() |
8533b90957 | ||
![]() |
c95a54c6f3 | ||
![]() |
a991640f52 | ||
![]() |
3d99b92c07 | ||
![]() |
d28ad17135 | ||
![]() |
3c67fc96b1 | ||
![]() |
4719636176 | ||
![]() |
45efee28b8 | ||
![]() |
3bcf225380 | ||
![]() |
2e81f843ce | ||
![]() |
a430142296 | ||
![]() |
6335b13c5e | ||
![]() |
6c4e987a24 | ||
![]() |
1a5c43d72a | ||
![]() |
91dbfca899 | ||
![]() |
96f103644a | ||
![]() |
5304e5a670 | ||
![]() |
390e5b3881 | ||
![]() |
9f5756c9fa | ||
![]() |
0ca35d7012 | ||
![]() |
0d19f4792f | ||
![]() |
91b009af79 | ||
![]() |
1ebd2fb9f1 | ||
![]() |
4684979ae7 | ||
![]() |
a567312bdb | ||
![]() |
1e851e0e8c | ||
![]() |
7d94615f47 | ||
![]() |
582fab7ea1 | ||
![]() |
822590ec8a | ||
![]() |
e9f0967578 | ||
![]() |
481da19c74 | ||
![]() |
b969db0c0f | ||
![]() |
a6b98fc3c3 | ||
![]() |
87c2046ab5 | ||
![]() |
4b992fb0c4 | ||
![]() |
3154011c65 | ||
![]() |
4e68383cf7 | ||
![]() |
db6ef22ebb | ||
![]() |
c238c7dbbc | ||
![]() |
d04823b4c5 | ||
![]() |
4cb45d6313 | ||
![]() |
6623e5f017 | ||
![]() |
6518aefb7f | ||
![]() |
d5600b7c08 | ||
![]() |
4789295d32 | ||
![]() |
70d54aa855 | ||
![]() |
77549efc47 | ||
![]() |
00299bc74d | ||
![]() |
b74fc5578d | ||
![]() |
9018d4cc18 | ||
![]() |
fcdceba09d | ||
![]() |
06d4ccf344 | ||
![]() |
a268040ae7 | ||
![]() |
67d79d618a | ||
![]() |
0e8a06e24d | ||
![]() |
d7732ee850 | ||
![]() |
729a928cfe | ||
![]() |
fe5a582a74 | ||
![]() |
c26a59d805 | ||
![]() |
ea331dbe0b | ||
![]() |
b97d6d7059 | ||
![]() |
9425b943dd | ||
![]() |
3fd0becfd4 | ||
![]() |
12ef191a0f | ||
![]() |
2bbb4acf3d | ||
![]() |
77d54df007 | ||
![]() |
1c35571ef0 | ||
![]() |
c8804160bf | ||
![]() |
0a6ffb6bc8 | ||
![]() |
6984f19aa0 | ||
![]() |
cb8de53d74 | ||
![]() |
93680b9764 | ||
![]() |
3cf9b745b5 | ||
![]() |
5851fe26ff | ||
![]() |
b188c4ec81 | ||
![]() |
4624c3d75b | ||
![]() |
7d196b4b95 | ||
![]() |
6347e44d94 | ||
![]() |
719d9386c5 | ||
![]() |
bb734be4bc | ||
![]() |
7cadaf1dc3 | ||
![]() |
c30453a86f | ||
![]() |
c2e3d0188e | ||
![]() |
aabb8ea16f | ||
![]() |
df572d59c5 | ||
![]() |
5ef7a37c20 | ||
![]() |
4b44e197ae | ||
![]() |
8b5b21ae69 | ||
![]() |
f5417fad6f | ||
![]() |
7fa6317f5c | ||
![]() |
74533cebc6 | ||
![]() |
10986db7c6 | ||
![]() |
67648baca7 | ||
![]() |
dc9182e9ab | ||
![]() |
4a7a81ffdb | ||
![]() |
09ef72647e | ||
![]() |
da38e6f986 | ||
![]() |
bd1a9f2cb0 | ||
![]() |
171eddd779 | ||
![]() |
7acc2f9e08 | ||
![]() |
27a6341137 | ||
![]() |
6c5e15e707 | ||
![]() |
06b1718ade | ||
![]() |
e50d2e16a7 | ||
![]() |
0b2404a0f2 | ||
![]() |
371804591d | ||
![]() |
54c64c15f3 | ||
![]() |
0e1124cd4f | ||
![]() |
70fd759e18 | ||
![]() |
8e383b2bec | ||
![]() |
63cd576d56 | ||
![]() |
32ac04ea78 | ||
![]() |
5d6bacb0bd | ||
![]() |
398d777681 | ||
![]() |
549a360d98 | ||
![]() |
1140e6026c | ||
![]() |
29a1167782 | ||
![]() |
d61a77f2d9 | ||
![]() |
b9bde1960b | ||
![]() |
a12c2eea5d | ||
![]() |
b5c717a559 | ||
![]() |
3adbc4cfaf | ||
![]() |
dd11fb1b99 | ||
![]() |
bf0d102c86 | ||
![]() |
dad2b92d2e | ||
![]() |
d027ec0018 | ||
![]() |
0c038398aa | ||
![]() |
5c3e0cc016 | ||
![]() |
9bcd26ce57 | ||
![]() |
3e8a6c418c | ||
![]() |
279f3e1183 | ||
![]() |
f77339ad85 | ||
![]() |
da73b316ff | ||
![]() |
82a49d2cbf | ||
![]() |
05711b4636 | ||
![]() |
2c2809573f | ||
![]() |
bbbeafcc92 | ||
![]() |
95c6adc739 | ||
![]() |
7c2e0aea92 | ||
![]() |
d05c76356f | ||
![]() |
f1a0623447 | ||
![]() |
41d02fdb72 | ||
![]() |
52d45d482c | ||
![]() |
a0fea94db2 | ||
![]() |
c3975e48d9 | ||
![]() |
f062e13921 | ||
![]() |
08ca9c9064 | ||
![]() |
667fd39147 | ||
![]() |
b760e543b0 | ||
![]() |
760ead4860 | ||
![]() |
9a4cce74f0 | ||
![]() |
7488eb782d | ||
![]() |
b1e6935df9 | ||
![]() |
df53364d16 | ||
![]() |
777e6c4c72 | ||
![]() |
e47a5effe6 | ||
![]() |
62d3f74513 | ||
![]() |
21e1fef0fb | ||
![]() |
b3f8daa758 | ||
![]() |
04f586721f | ||
![]() |
8e22e41605 | ||
![]() |
2770d1f36b | ||
![]() |
403c042235 | ||
![]() |
bdb3c04037 | ||
![]() |
f1cb21e7fc | ||
![]() |
a8486eda9f | ||
![]() |
d5b98d306d | ||
![]() |
bb2fe650ac | ||
![]() |
b576c3de40 | ||
![]() |
84533b8843 | ||
![]() |
a8ff98b808 | ||
![]() |
f0062b1e67 | ||
![]() |
93f64de875 | ||
![]() |
ec47e320d2 | ||
![]() |
816d5ee594 | ||
![]() |
588f5bd6b7 | ||
![]() |
825ea93dba | ||
![]() |
a690a1d7bf | ||
![]() |
9fe4c79782 | ||
![]() |
42613d6519 | ||
![]() |
4b77910e4f | ||
![]() |
3f2cce936c | ||
![]() |
6e8e9824f9 | ||
![]() |
33e1d34cb1 | ||
![]() |
48948d5854 | ||
![]() |
7fc00ce1cb | ||
![]() |
0c940be5fb | ||
![]() |
bddb505b7f | ||
![]() |
a91d25b27d | ||
![]() |
4ad005f0bf | ||
![]() |
7472545204 | ||
![]() |
164c9c8e73 | ||
![]() |
e52118db93 | ||
![]() |
9e7acacb06 | ||
![]() |
56deb15bca | ||
![]() |
a3d4969d7b | ||
![]() |
4a00957b71 | ||
![]() |
56bd731361 | ||
![]() |
b6c470edf1 | ||
![]() |
5bc0feacf0 | ||
![]() |
dc8d837e88 | ||
![]() |
cddf6ce1f4 | ||
![]() |
8abb212ae7 | ||
![]() |
0056d75127 | ||
![]() |
5be475ea17 | ||
![]() |
b157cf5294 | ||
![]() |
48c9c89e3d | ||
![]() |
83f405b695 | ||
![]() |
9bf41a37b4 | ||
![]() |
774f22b7e7 | ||
![]() |
aaa3964bb3 | ||
![]() |
6f6fc759cc | ||
![]() |
4358b7f924 | ||
![]() |
2841369d3d | ||
![]() |
ad031d4bda | ||
![]() |
588ee2c3b1 | ||
![]() |
038033cf27 | ||
![]() |
84c4bbd380 | ||
![]() |
807ce468d6 | ||
![]() |
a839494a1e | ||
![]() |
80bbc9990a | ||
![]() |
fa52442c1c | ||
![]() |
919ce2afb1 | ||
![]() |
db55be6d33 | ||
![]() |
2dc7c1afed | ||
![]() |
85956dc7fd | ||
![]() |
910cd98a38 | ||
![]() |
8022bd2868 | ||
![]() |
d5ca7e1719 | ||
![]() |
066a0771b3 | ||
![]() |
9e35c1ab68 | ||
![]() |
fb1deb838c | ||
![]() |
8e010618bb | ||
![]() |
365cf1f7ef | ||
![]() |
b226b20e3d | ||
![]() |
ec21f4c2c6 | ||
![]() |
a696d849b2 | ||
![]() |
ea3fae2ce4 | ||
![]() |
736e117eca | ||
![]() |
2fb3ac74eb | ||
![]() |
2d5c8ec3e9 | ||
![]() |
25c1156c88 | ||
![]() |
c44624282c | ||
![]() |
370f2eb9e4 | ||
![]() |
1793c68aae | ||
![]() |
5e52bd905d | ||
![]() |
cba6bbdc74 | ||
![]() |
6f4593508b | ||
![]() |
dc3bad56f2 | ||
![]() |
784e5e6e39 | ||
![]() |
13fe62975d | ||
![]() |
b97fd9918a | ||
![]() |
dc56c2de52 | ||
![]() |
375a5323d5 | ||
![]() |
31b69147f4 | ||
![]() |
8e3011807d | ||
![]() |
ec7c6ab96c | ||
![]() |
8a4097a366 | ||
![]() |
792a736e48 | ||
![]() |
cce0a02ebb | ||
![]() |
2ddab4eecc | ||
![]() |
f66755cbf1 | ||
![]() |
257e60a2b1 | ||
![]() |
75a3566760 | ||
![]() |
7a9f17e059 | ||
![]() |
abbfe7200a | ||
![]() |
419942112b | ||
![]() |
597d4a0426 | ||
![]() |
e023d60be7 | ||
![]() |
bc5010a953 | ||
![]() |
41a7b42037 | ||
![]() |
2936865c55 | ||
![]() |
ff2bf1f3c1 | ||
![]() |
1bccbd4173 | ||
![]() |
d7f00df391 | ||
![]() |
22f88c59c7 | ||
![]() |
8721776839 | ||
![]() |
a89da0dac0 | ||
![]() |
e4b4dc4ae9 | ||
![]() |
b26c44b2b9 | ||
![]() |
68095417b9 | ||
![]() |
b6344eb6e8 | ||
![]() |
224302cfef | ||
![]() |
abc4816888 | ||
![]() |
21e14bd644 | ||
![]() |
a89caccd32 | ||
![]() |
03dc3e52b7 | ||
![]() |
f04be8efa6 | ||
![]() |
2c32f6bcb3 | ||
![]() |
a3a08ff5c7 | ||
![]() |
ea51186767 | ||
![]() |
49494c572b | ||
![]() |
fcac3fa164 | ||
![]() |
9c1153ef37 | ||
![]() |
0adc4b33ef | ||
![]() |
c0f3215340 | ||
![]() |
bab1e6a95f | ||
![]() |
53b26a43c0 | ||
![]() |
2240d019f5 | ||
![]() |
cb11c6b3ea | ||
![]() |
5893559951 | ||
![]() |
8408d25cef | ||
![]() |
1ac2ffcf02 | ||
![]() |
6b6c38c2c8 | ||
![]() |
e55df73a91 | ||
![]() |
360c2cbfa3 | ||
![]() |
aba96674f3 | ||
![]() |
5c3d85fc90 | ||
![]() |
6486b7fd4c | ||
![]() |
5f3e980de0 | ||
![]() |
d0edbec5fb | ||
![]() |
5d46963e8a | ||
![]() |
321f441b63 | ||
![]() |
d55bade070 | ||
![]() |
6ba6b821f5 | ||
![]() |
b3dedae115 | ||
![]() |
5a1070c30f | ||
![]() |
40664997e1 | ||
![]() |
c6e83cb7c0 | ||
![]() |
e7e27e794c | ||
![]() |
1073dbe6ab | ||
![]() |
2bd9b5a015 | ||
![]() |
bc09febd2c | ||
![]() |
b2a87c90a2 | ||
![]() |
d6dbbcb0de | ||
![]() |
9ccb5360b3 | ||
![]() |
0187c4faff | ||
![]() |
605172a0bc | ||
![]() |
8565a0d911 | ||
![]() |
61c8d23a7e | ||
![]() |
5e3487ed59 | ||
![]() |
d5a161769c | ||
![]() |
1692f9c2dd | ||
![]() |
0cbac8bb44 | ||
![]() |
35a81e7f11 | ||
![]() |
ac64d293e7 | ||
![]() |
708b8787c5 | ||
![]() |
49947f3337 | ||
![]() |
2bddd151eb | ||
![]() |
43a585187c | ||
![]() |
324658a36b | ||
![]() |
dd9a9b34d1 | ||
![]() |
2ab0e40952 | ||
![]() |
dfea80ae96 | ||
![]() |
6e38f5accf | ||
![]() |
7c952d92bf | ||
![]() |
2fae0d2d95 | ||
![]() |
67ab63f00e | ||
![]() |
719f9c28af | ||
![]() |
035d621109 | ||
![]() |
791f3b896d | ||
![]() |
fe2172a660 | ||
![]() |
640fbd616b | ||
![]() |
900efe8a36 | ||
![]() |
5bd92d04d9 | ||
![]() |
b15684bcbd | ||
![]() |
a93222dbb2 | ||
![]() |
20744e90a0 | ||
![]() |
32777b4259 | ||
![]() |
271120999c | ||
![]() |
68fe13a67d | ||
![]() |
f3606014c6 | ||
![]() |
efbf4482b2 | ||
![]() |
21a3b4f8e2 | ||
![]() |
de23b2d046 | ||
![]() |
bd8f436c1d | ||
![]() |
e963735dba | ||
![]() |
46c981103d | ||
![]() |
f6d02d8fc6 | ||
![]() |
e08f691510 | ||
![]() |
af9199aaff | ||
![]() |
8576b13f74 | ||
![]() |
2270d8a795 | ||
![]() |
f4dcce6d6c | ||
![]() |
b802a410b9 | ||
![]() |
9e3d339ec5 | ||
![]() |
fb97a98b97 | ||
![]() |
72773f3bc8 | ||
![]() |
b0fd93e0c3 | ||
![]() |
7aa2ec78f2 | ||
![]() |
047e856a61 | ||
![]() |
dbe209e3f2 | ||
![]() |
e0d23ee6cf | ||
![]() |
7a35f46370 | ||
![]() |
4a4465efb6 | ||
![]() |
3a112531cc | ||
![]() |
456209dded | ||
![]() |
2556b0d157 | ||
![]() |
1e8903fd76 | ||
![]() |
ad9f18c231 | ||
![]() |
63e3de00cb | ||
![]() |
d06ffeeede | ||
![]() |
3479fb9d94 | ||
![]() |
304bd002ae | ||
![]() |
5dad18c85f | ||
![]() |
19e4c0657a | ||
![]() |
44548fdc33 | ||
![]() |
d8929074b5 | ||
![]() |
e11532ae92 | ||
![]() |
eff48acdf4 | ||
![]() |
a9850f9641 | ||
![]() |
aab0b8a3ce | ||
![]() |
b12e062d94 | ||
![]() |
b36e342f15 | ||
![]() |
f686816c86 | ||
![]() |
dc50e54afc | ||
![]() |
3897e3d452 | ||
![]() |
2557b03b11 | ||
![]() |
29d29a337f | ||
![]() |
d3ce4af541 | ||
![]() |
34f8e5e28d | ||
![]() |
afd1a68c62 | ||
![]() |
dbcf1cb907 | ||
![]() |
9ca64f9789 | ||
![]() |
d45f47d908 | ||
![]() |
4c247ac49d | ||
![]() |
e8a406526b | ||
![]() |
7fcea16c6b | ||
![]() |
028b799d2c | ||
![]() |
3485296e23 | ||
![]() |
03078cdd45 | ||
![]() |
740310800d | ||
![]() |
6d7c558482 | ||
![]() |
fdb10515c3 | ||
![]() |
5a0f13c485 | ||
![]() |
80b330ad7b | ||
![]() |
d929e1c134 | ||
![]() |
5e40dcdc38 | ||
![]() |
1dd3e2a83b | ||
![]() |
a62742fad9 | ||
![]() |
1f9c45b11c | ||
![]() |
68bec5e158 | ||
![]() |
37f1bd7d63 | ||
![]() |
5ba24211e2 | ||
![]() |
d699647418 | ||
![]() |
b246502cb6 | ||
![]() |
9b33ead8aa | ||
![]() |
32e8c1dc6d | ||
![]() |
e09ef7862e | ||
![]() |
85420304d0 | ||
![]() |
1c097a669d | ||
![]() |
4e1497c5da | ||
![]() |
49d426675f | ||
![]() |
dc6ac668b4 | ||
![]() |
4ee24b0845 | ||
![]() |
ba20aef206 | ||
![]() |
41ef6133c1 | ||
![]() |
50bd5ee8f7 | ||
![]() |
285f3fe330 | ||
![]() |
4d01199986 | ||
![]() |
bcc0052fe0 | ||
![]() |
4b592d81bd | ||
![]() |
884e323288 | ||
![]() |
78b799dd05 | ||
![]() |
847fa2e700 | ||
![]() |
481a79e311 | ||
![]() |
f19f2ff321 | ||
![]() |
6dc4d7bb70 | ||
![]() |
83460a34f4 | ||
![]() |
2adbb47373 | ||
![]() |
2159a5419a | ||
![]() |
044d6a15d9 | ||
![]() |
b6055062c6 | ||
![]() |
6fd85e043b | ||
![]() |
e07ac52356 | ||
![]() |
0f16ba9325 | ||
![]() |
378e6d28bc | ||
![]() |
539d2b880c | ||
![]() |
2982adbfa7 | ||
![]() |
5147dff670 | ||
![]() |
2cdf78c504 | ||
![]() |
cfad45b7c2 | ||
![]() |
5234e9bce5 | ||
![]() |
0ed5454d02 | ||
![]() |
03080973be | ||
![]() |
a4f51b0cb3 | ||
![]() |
749079c1c3 | ||
![]() |
ae10ff42e1 | ||
![]() |
d4cbdab4a3 | ||
![]() |
1bd6392a4c | ||
![]() |
1531e99528 | ||
![]() |
6e7af18494 | ||
![]() |
e1bae65aeb | ||
![]() |
65bfdf94c9 | ||
![]() |
9a92825954 | ||
![]() |
469faf509b | ||
![]() |
f87d4a5ab6 | ||
![]() |
9c31b749d7 | ||
![]() |
521d5df064 | ||
![]() |
4d330fba8a | ||
![]() |
2e04a55d5c | ||
![]() |
0d9d0aa18b | ||
![]() |
f8dd1795bc | ||
![]() |
1d7007584c | ||
![]() |
8cd9f891fb | ||
![]() |
6ab0f1db57 | ||
![]() |
37d754d069 | ||
![]() |
e12b194d41 | ||
![]() |
07d7fa26fe | ||
![]() |
73b9b87ef3 | ||
![]() |
0c0091375c | ||
![]() |
21a29ed3a5 | ||
![]() |
a6312f4279 | ||
![]() |
f459abdf85 | ||
![]() |
586fa1d0f0 | ||
![]() |
bf4192a1f0 | ||
![]() |
ac31eedf65 | ||
![]() |
b05dc5141c | ||
![]() |
32c6fb14dd | ||
![]() |
982c940381 | ||
![]() |
a7a8aaa887 | ||
![]() |
bf83a9980e | ||
![]() |
11be603ed3 | ||
![]() |
a432cf8405 | ||
![]() |
9dd6b3b72d | ||
![]() |
faca62b55f | ||
![]() |
a339de89f5 | ||
![]() |
e40c90e9c0 | ||
![]() |
3f447bb8a7 | ||
![]() |
21dca3fbf8 | ||
![]() |
1078bb4287 | ||
![]() |
daeed06e70 | ||
![]() |
1206e2d75f | ||
![]() |
cc81239b9d | ||
![]() |
e797c01761 | ||
![]() |
12f7366968 | ||
![]() |
b7fd7abe85 | ||
![]() |
a5e1f3d165 | ||
![]() |
212d047ada | ||
![]() |
2e16127fde | ||
![]() |
e8b53a619d | ||
![]() |
df1ca1fd96 | ||
![]() |
8f85132d48 | ||
![]() |
349144599c | ||
![]() |
979093923b | ||
![]() |
137f8ad4cb | ||
![]() |
c1f462b8f8 | ||
![]() |
6701c4c371 | ||
![]() |
fc6e459c09 | ||
![]() |
9e28b3447e | ||
![]() |
5c737e1969 | ||
![]() |
569765e77e | ||
![]() |
bc0d63ed12 | ||
![]() |
02f9893522 | ||
![]() |
b4bbe63f0f | ||
![]() |
fabbcac99f | ||
![]() |
b1b5ab6949 | ||
![]() |
4b9487183b | ||
![]() |
de5a817953 | ||
![]() |
4970f640fa | ||
![]() |
18996535b7 | ||
![]() |
2a1e31b5e9 | ||
![]() |
8ca9a0f409 | ||
![]() |
fcc89a67ba | ||
![]() |
1f377d43c5 | ||
![]() |
30d6c68908 | ||
![]() |
dc781da93a | ||
![]() |
36c20e4348 | ||
![]() |
4466950bb8 | ||
![]() |
be29828454 | ||
![]() |
7bab245073 | ||
![]() |
f5dcf0b760 | ||
![]() |
8141f78a92 | ||
![]() |
be244b3d00 | ||
![]() |
805f5ff9b6 | ||
![]() |
76daeb7e55 | ||
![]() |
9594c8106e | ||
![]() |
ed4052365c | ||
![]() |
377ebadc10 | ||
![]() |
ed4809b71e | ||
![]() |
db37dffdbb | ||
![]() |
13cab7e301 | ||
![]() |
0a50fc66e5 | ||
![]() |
a3d1a3566d | ||
![]() |
ba0be927ed | ||
![]() |
4260606267 | ||
![]() |
4665db4f27 | ||
![]() |
43503ba085 | ||
![]() |
0a83a704f1 | ||
![]() |
08de941c90 | ||
![]() |
62228ef144 | ||
![]() |
9731257782 | ||
![]() |
4ec9c9c16e | ||
![]() |
45436731e2 | ||
![]() |
27730e65e7 | ||
![]() |
a4aba93d57 | ||
![]() |
d93db16963 | ||
![]() |
c327fe11b8 | ||
![]() |
4fbc31d0b0 | ||
![]() |
9a4a1cb4ec | ||
![]() |
202d6957bc | ||
![]() |
14fcff7774 | ||
![]() |
2c9aa1cab4 | ||
![]() |
7745c10d07 | ||
![]() |
c1d571de42 | ||
![]() |
cecb66451c | ||
![]() |
0ef3421fa2 | ||
![]() |
f88e238d41 | ||
![]() |
ce3c8f264d | ||
![]() |
9fbd594f37 | ||
![]() |
5ad95cad90 | ||
![]() |
7e507b40c4 | ||
![]() |
446a9b5c02 | ||
![]() |
e02e61384e | ||
![]() |
5deb570fdf | ||
![]() |
915c46f144 | ||
![]() |
30d6c5eaf3 | ||
![]() |
6e50d1166a | ||
![]() |
0e3eed0563 | ||
![]() |
1b1676cecc | ||
![]() |
d911fe6a0b | ||
![]() |
22253a3385 | ||
![]() |
38640c99e3 | ||
![]() |
d6df8bddea | ||
![]() |
ddfc4bd98e | ||
![]() |
3d6674325c | ||
![]() |
194829f5b1 | ||
![]() |
11a77253f4 | ||
![]() |
67be2343f8 | ||
![]() |
e9b1b3d853 | ||
![]() |
8a33d174d7 | ||
![]() |
226d6216b7 | ||
![]() |
1925bb01be | ||
![]() |
82a4806e01 | ||
![]() |
ce419fae7b | ||
![]() |
c68b76e2da | ||
![]() |
342020b420 | ||
![]() |
1e6e99e3c7 | ||
![]() |
2e9aafc377 | ||
![]() |
299c863f49 | ||
![]() |
c2792a28ba | ||
![]() |
635a027a8e | ||
![]() |
a45b8ca8e7 | ||
![]() |
1e6e945a07 | ||
![]() |
8666e6baae | ||
![]() |
f71157c24d | ||
![]() |
e87a2b36cf | ||
![]() |
5418474f64 | ||
![]() |
8836ba6ceb | ||
![]() |
509c5b497a | ||
![]() |
e00bcc9f48 | ||
![]() |
bdef9fd040 | ||
![]() |
c956491ec5 | ||
![]() |
68bc549d6a | ||
![]() |
9c64eafc21 | ||
![]() |
b05e86d442 | ||
![]() |
fe5f9576c6 | ||
![]() |
1b282b65b7 | ||
![]() |
e49664bad3 | ||
![]() |
2db9f33c41 | ||
![]() |
2a30b55a43 | ||
![]() |
9d0b20adce | ||
![]() |
acd5e1c081 | ||
![]() |
cc1c5e45b2 | ||
![]() |
038199c447 | ||
![]() |
8a1eab7ceb | ||
![]() |
bc5bd35448 | ||
![]() |
1795fd56b7 | ||
![]() |
3d788d6056 | ||
![]() |
4a7c33edad | ||
![]() |
797f60d725 | ||
![]() |
2427d68aa1 | ||
![]() |
00c6b0f8ed | ||
![]() |
7560f98d70 | ||
![]() |
7b8d4ab3d6 | ||
![]() |
07a1a805f6 | ||
![]() |
d8bab6aba9 | ||
![]() |
a930e2dc75 | ||
![]() |
2eb35668fa | ||
![]() |
07f4e5ac5c | ||
![]() |
1533c22d5c | ||
![]() |
db82a90414 | ||
![]() |
51a693badf | ||
![]() |
2aa8f5b352 | ||
![]() |
93b3b8f985 | ||
![]() |
92c8bd80b5 | ||
![]() |
528af0157d | ||
![]() |
10a77b6278 | ||
![]() |
03bbf6a582 | ||
![]() |
63fcb649d2 | ||
![]() |
4f60a92b92 | ||
![]() |
0419c1a41f | ||
![]() |
2d5ae78521 | ||
![]() |
959134df02 | ||
![]() |
cf03f103ab | ||
![]() |
a9f9fc4ce2 | ||
![]() |
cfb370a3c8 | ||
![]() |
353435c8d5 | ||
![]() |
c8c85d096b | ||
![]() |
19c9c8f227 | ||
![]() |
6ea2a29eea | ||
![]() |
59f3f819a6 | ||
![]() |
93e8f52880 | ||
![]() |
02810efcc4 | ||
![]() |
4a8a7c997e | ||
![]() |
4b9be7ce16 | ||
![]() |
f3ec09e480 | ||
![]() |
8291a84e3e | ||
![]() |
b0e1f0f73a | ||
![]() |
a66b966e7d | ||
![]() |
5f56040c64 | ||
![]() |
eaccd22267 | ||
![]() |
27845a7345 | ||
![]() |
f7ef8180e4 | ||
![]() |
5958eb9a55 | ||
![]() |
3ef2912b60 | ||
![]() |
fa9c6a765a | ||
![]() |
c4a8899780 | ||
![]() |
3cc4628d03 | ||
![]() |
b6c5223221 | ||
![]() |
cbd6d4251c | ||
![]() |
fdcbb5b432 | ||
![]() |
de09e31815 | ||
![]() |
f55e911313 | ||
![]() |
465a91dbf3 | ||
![]() |
835a7833ae | ||
![]() |
179717d40c | ||
![]() |
3d4d789f7f | ||
![]() |
d97fb19f05 | ||
![]() |
0dd3757df2 | ||
![]() |
c32a4546f3 | ||
![]() |
1bb025ccd0 | ||
![]() |
2b8033a97f | ||
![]() |
21a3a8c594 | ||
![]() |
1026e90296 | ||
![]() |
0eca602e61 | ||
![]() |
7f75ca81f1 | ||
![]() |
8af05e2726 | ||
![]() |
0a478ee1da | ||
![]() |
a4bdc5a05f | ||
![]() |
d425767dae | ||
![]() |
c78382c119 | ||
![]() |
ee15ddfbc3 | ||
![]() |
0af14eb77e | ||
![]() |
583cc4bc8a | ||
![]() |
2ee92f48e6 | ||
![]() |
d05e02ab3e | ||
![]() |
abb9f8e233 | ||
![]() |
f873ef9b59 | ||
![]() |
1255b56522 | ||
![]() |
fd9bb4d8cc | ||
![]() |
9328576b55 | ||
![]() |
70a1edd1dd | ||
![]() |
87e4c209f4 | ||
![]() |
3d0a5642cc | ||
![]() |
e211d812ad | ||
![]() |
0dcf673b87 | ||
![]() |
cb14e1f20c | ||
![]() |
52087c0e30 | ||
![]() |
1b9286db76 | ||
![]() |
bc92c0b052 | ||
![]() |
245bb639f2 | ||
![]() |
8d81ed58c8 | ||
![]() |
7890ca85a8 | ||
![]() |
07bab7b264 | ||
![]() |
5730c14dc1 | ||
![]() |
f8e8b5ad18 | ||
![]() |
fd2728c02c | ||
![]() |
7e2bf920e1 | ||
![]() |
1f65328f2d | ||
![]() |
4f731baa00 | ||
![]() |
5abb3dd8c1 | ||
![]() |
0a672c55c5 | ||
![]() |
a6b2299c74 | ||
![]() |
37cc6709d4 | ||
![]() |
f4ffbe67e2 | ||
![]() |
9f32d72a41 | ||
![]() |
64a117d8ac | ||
![]() |
ebf0bdc840 | ||
![]() |
cc0a120bf6 | ||
![]() |
fe2fe7468f | ||
![]() |
b12a10ccb5 | ||
![]() |
2ad2a4b198 | ||
![]() |
6a62f05657 | ||
![]() |
4910f60ec4 | ||
![]() |
d35168e88f | ||
![]() |
01b3d2aca9 | ||
![]() |
29e8d1cff0 | ||
![]() |
4e1d10cc08 | ||
![]() |
3575d94ca1 | ||
![]() |
d91546b532 | ||
![]() |
9f554f4917 | ||
![]() |
d4720a9244 | ||
![]() |
5c466712db | ||
![]() |
6dc7e852ae | ||
![]() |
785f614bd9 | ||
![]() |
0a8e27249d | ||
![]() |
15ee87ee67 | ||
![]() |
12612a16df | ||
![]() |
4f449e2600 | ||
![]() |
7f49f039fd | ||
![]() |
88dc65bc4e | ||
![]() |
6edebe18ad | ||
![]() |
38b3a9205d | ||
![]() |
4b796b4929 | ||
![]() |
83cabcac28 | ||
![]() |
d308c5d9b9 | ||
![]() |
9f032a61a9 | ||
![]() |
0f58214ba1 | ||
![]() |
c48a60cce6 | ||
![]() |
cd3ffceeff | ||
![]() |
9be4a00169 | ||
![]() |
a9c7a39a47 | ||
![]() |
abcdd60a21 | ||
![]() |
a94f85a100 | ||
![]() |
9755bf723f | ||
![]() |
a71ebcf47e | ||
![]() |
72695631cd | ||
![]() |
2af211b543 | ||
![]() |
6e5e2625d6 | ||
![]() |
23c1c2f5eb | ||
![]() |
da85ee5d01 | ||
![]() |
9612bc78fe | ||
![]() |
d408e8653c | ||
![]() |
cc76ccc3c9 | ||
![]() |
105a00d3e4 | ||
![]() |
2c08cba8cc | ||
![]() |
344b11a204 | ||
![]() |
1ff5bf0fd5 | ||
![]() |
2b86137388 | ||
![]() |
c29cf7f77c | ||
![]() |
193cb46d60 | ||
![]() |
9dc864d486 | ||
![]() |
cee166839a | ||
![]() |
1a60a3c728 | ||
![]() |
5d946778cb | ||
![]() |
ac5f85820f | ||
![]() |
716e100a28 | ||
![]() |
7b8cb16c12 | ||
![]() |
00d46424a3 | ||
![]() |
2a5f940744 | ||
![]() |
13cc016b36 | ||
![]() |
a8d49c27c8 | ||
![]() |
8fdbe447c1 | ||
![]() |
a8522e91b5 | ||
![]() |
5754f4463d | ||
![]() |
d4118ade0f | ||
![]() |
6d80f15a98 | ||
![]() |
f8aa472409 | ||
![]() |
df22fd00ca | ||
![]() |
ce2743a982 | ||
![]() |
92b32458ad | ||
![]() |
d57e8a45d3 | ||
![]() |
551d3ffdf3 | ||
![]() |
7add6eb736 | ||
![]() |
a28616d535 | ||
![]() |
a288fd370f | ||
![]() |
acd335e249 | ||
![]() |
764ae7e0b6 | ||
![]() |
da0bfa1945 | ||
![]() |
3c61d709b5 | ||
![]() |
ffc92a7b63 | ||
![]() |
af0c7b5a50 | ||
![]() |
1904c4d057 | ||
![]() |
542f169b36 | ||
![]() |
65a30bf60c | ||
![]() |
2e51da32f0 | ||
![]() |
0562242043 | ||
![]() |
debcdefc21 | ||
![]() |
6b7e78320d | ||
![]() |
0de3f3a332 | ||
![]() |
4fcb4d449e | ||
![]() |
408fe25abd | ||
![]() |
236e5e0b25 | ||
![]() |
ebe0caba83 | ||
![]() |
9d33c0cfaf | ||
![]() |
7962130a0c | ||
![]() |
9690434cac | ||
![]() |
7304544c37 | ||
![]() |
5a3408c242 | ||
![]() |
16996f25af | ||
![]() |
0c12586019 | ||
![]() |
93a1adaa56 | ||
![]() |
83e65e2cc6 | ||
![]() |
36586b798e | ||
![]() |
20c351949f | ||
![]() |
b63bd92d81 | ||
![]() |
17d3755152 | ||
![]() |
d7c0c2ea72 | ||
![]() |
7c823c98ae | ||
![]() |
97508a6f31 | ||
![]() |
2507a41b6e | ||
![]() |
9b628546c1 | ||
![]() |
d0837fada8 | ||
![]() |
520647d72f | ||
![]() |
51c888845c | ||
![]() |
e4606219bc | ||
![]() |
716335df2c | ||
![]() |
ad4f90c502 | ||
![]() |
a1bdfa7560 | ||
![]() |
34ca807044 | ||
![]() |
8af55efdb3 | ||
![]() |
6393072e68 | ||
![]() |
9e1a8b646b | ||
![]() |
c810e541ea | ||
![]() |
66432608ed |
123
.eslintrc.json
123
.eslintrc.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"airbnb-typescript/base",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
"plugin:lit/all",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
@@ -29,64 +29,91 @@
|
||||
"__BUILD__": false,
|
||||
"__VERSION__": false,
|
||||
"__STATIC_PATH__": false,
|
||||
"Polymer": true,
|
||||
"webkitSpeechRecognition": false,
|
||||
"ResizeObserver": false
|
||||
"__SUPERVISOR__": false,
|
||||
"Polymer": true
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 2,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"no-console": 2,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"class-methods-use-this": "off",
|
||||
"new-cap": "off",
|
||||
"prefer-template": "off",
|
||||
"object-shorthand": "off",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"strict": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-bitwise": "error",
|
||||
"comma-dangle": "off",
|
||||
"vars-on-top": "off",
|
||||
"no-continue": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-multi-assign": "off",
|
||||
"no-console": "error",
|
||||
"radix": "off",
|
||||
"no-alert": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/order": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-cycle": 0,
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-cycle": "off",
|
||||
"import/extensions": [
|
||||
2,
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{ "ts": "never", "js": "never" }
|
||||
],
|
||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"wc/no-self-class": 0,
|
||||
"no-shadow": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"object-curly-newline": "off",
|
||||
"default-case": "off",
|
||||
"wc/no-self-class": "off",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
"lit/attribute-value-entities": 0
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"off",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase", "snake_case"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": ["variable"],
|
||||
"format": ["camelCase", "snake_case", "UPPER_CASE"],
|
||||
"leadingUnderscore": "allow",
|
||||
"trailingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "typeLike",
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"lit/attribute-value-entities": "off",
|
||||
"lit/no-template-map": "off"
|
||||
},
|
||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
||||
"plugins": ["disable", "unused-imports"],
|
||||
"processor": "disable/disable"
|
||||
}
|
||||
|
88
.github/workflows/ci.yaml
vendored
88
.github/workflows/ci.yaml
vendored
@@ -10,81 +10,64 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
||||
- name: Run eslint
|
||||
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
||||
run: yarn run lint:eslint
|
||||
- name: Run tsc
|
||||
run: ./node_modules/.bin/tsc
|
||||
run: yarn run lint:types
|
||||
- name: Run prettier
|
||||
run: yarn run lint:prettier
|
||||
- name: Check for duplicate dependencies
|
||||
run: yarn dedupe --check
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Run Mocha
|
||||
run: npm run mocha
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp build-translations build-locale-data
|
||||
- name: Run Tests
|
||||
run: yarn run test
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -99,20 +82,11 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
22
.github/workflows/demo.yaml
vendored
22
.github/workflows/demo.yaml
vendored
@@ -4,26 +4,22 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
4
.github/workflows/netflify.yml
vendored
4
.github/workflows/netflify.yml
vendored
@@ -15,5 +15,5 @@ jobs:
|
||||
- name: Trigger Demo build
|
||||
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }}
|
||||
|
||||
- name: Trigger Gallery build
|
||||
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}
|
||||
- name: Trigger Design build
|
||||
run: curl -X POST -d "NIGHTLY" https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}
|
||||
|
18
.github/workflows/release.yaml
vendored
18
.github/workflows/release.yaml
vendored
@@ -6,9 +6,9 @@ on:
|
||||
- published
|
||||
|
||||
env:
|
||||
WHEELS_TAG: 3.7-alpine3.11
|
||||
PYTHON_VERSION: 3.7
|
||||
NODE_VERSION: 12.1
|
||||
PYTHON_VERSION: 3.8
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -30,7 +30,15 @@ jobs:
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Download Translations
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
- name: Build and release package
|
||||
run: |
|
||||
python3 -m pip install twine
|
||||
@@ -64,6 +72,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||
tag:
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Download requirements.txt
|
||||
uses: actions/download-artifact@v2
|
||||
@@ -73,7 +83,7 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@master
|
||||
with:
|
||||
tag: ${{ env.WHEELS_TAG }}
|
||||
tag: ${{ matrix.tag }}
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-host: ${{ secrets.WHEELS_HOST }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
42
.github/workflows/translations.yaml
vendored
42
.github/workflows/translations.yaml
vendored
@@ -1,8 +1,6 @@
|
||||
name: Translations
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 0 * * *"
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
@@ -10,7 +8,7 @@ on:
|
||||
- src/translations/en.json
|
||||
|
||||
env:
|
||||
NODE_VERSION: 12
|
||||
NODE_VERSION: 14
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
@@ -20,46 +18,8 @@ jobs:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
./script/translations_upload_base
|
||||
|
||||
download:
|
||||
name: Download
|
||||
needs: upload
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Download Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
npm install
|
||||
./script/translations_download
|
||||
|
||||
- name: Initialize git
|
||||
uses: home-assistant/actions/helpers/git-init@master
|
||||
with:
|
||||
name: GitHub Action
|
||||
email: github-action@users.noreply.github.com
|
||||
|
||||
- name: Update translation
|
||||
run: |
|
||||
git add translations
|
||||
git commit -am "Translation update"
|
||||
git push
|
||||
|
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,11 +1,23 @@
|
||||
build
|
||||
build-translations/*
|
||||
node_modules/*
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
.reify-cache
|
||||
|
||||
# build
|
||||
build
|
||||
hass_frontend/*
|
||||
dist
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
node_modules/*
|
||||
yarn-error.log
|
||||
npm-debug.log
|
||||
|
||||
# Python stuff
|
||||
*.py[cod]
|
||||
*.egg
|
||||
@@ -14,11 +26,8 @@ hass_frontend/*
|
||||
# venv stuff
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
venv
|
||||
venv/*
|
||||
.venv
|
||||
lib
|
||||
bin
|
||||
dist
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
@@ -31,9 +40,8 @@ src/cast/dev_const.ts
|
||||
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
||||
#asdf
|
||||
# asdf
|
||||
.tool-versions
|
||||
|
||||
# Home Assistant config
|
||||
|
@@ -1,5 +1,4 @@
|
||||
build
|
||||
build-translations/*
|
||||
translations/*
|
||||
node_modules/*
|
||||
hass_frontend/*
|
||||
|
1536
.yarn/patches/@lit-labs/virtualizer/0.7.0.patch
Normal file
1536
.yarn/patches/@lit-labs/virtualizer/0.7.0.patch
Normal file
File diff suppressed because one or more lines are too long
29
.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
Normal file
29
.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
Normal file
@@ -0,0 +1,29 @@
|
||||
diff --git a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
index d92179f7fd5315203f870a6963e871dc8ddf6c0c..362e284121b97e0fba0925225777aebc32e26b8d 100644
|
||||
--- a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
+++ b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
@@ -1,14 +1,15 @@
|
||||
-let _ET, ET;
|
||||
+let _ET;
|
||||
+let ET;
|
||||
export default async function EventTarget() {
|
||||
- return ET || init();
|
||||
+ return ET || init();
|
||||
}
|
||||
async function init() {
|
||||
- _ET = window.EventTarget;
|
||||
- try {
|
||||
- new _ET();
|
||||
- }
|
||||
- catch (_a) {
|
||||
- _ET = (await import('event-target-shim')).EventTarget;
|
||||
- }
|
||||
- return (ET = _ET);
|
||||
+ _ET = window.EventTarget;
|
||||
+ try {
|
||||
+ new _ET();
|
||||
+ } catch (_a) {
|
||||
+ _ET = (await import("event-target-shim")).default.EventTarget;
|
||||
+ }
|
||||
+ return (ET = _ET);
|
||||
}
|
12
.yarn/patches/@material/mwc-icon-button/remove-icon.patch
Normal file
12
.yarn/patches/@material/mwc-icon-button/remove-icon.patch
Normal file
@@ -0,0 +1,12 @@
|
||||
diff --git a/mwc-icon-button-base.js b/mwc-icon-button-base.js
|
||||
index 45cdaab93ccc0a6daaaaabc01266dcdc32e46bfd..b3ea5b541597308d85f86ce6c23fd00785fda835 100644
|
||||
--- a/mwc-icon-button-base.js
|
||||
+++ b/mwc-icon-button-base.js
|
||||
@@ -63,7 +63,6 @@ export class IconButtonBase extends LitElement {
|
||||
@touchend="${this.handleRippleDeactivate}"
|
||||
@touchcancel="${this.handleRippleDeactivate}"
|
||||
>${this.renderRipple()}
|
||||
- <i class="material-icons">${this.icon}</i>
|
||||
<span
|
||||
><slot></slot
|
||||
></span>
|
34
.yarn/patches/@polymer/polymer/pr-5569.patch
Normal file
34
.yarn/patches/@polymer/polymer/pr-5569.patch
Normal file
@@ -0,0 +1,34 @@
|
||||
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
|
||||
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
|
||||
--- a/lib/legacy/class.js
|
||||
+++ b/lib/legacy/class.js
|
||||
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
|
||||
// only proceed if the generated class' prototype has not been registered.
|
||||
const generatedProto = PolymerGenerated.prototype;
|
||||
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
|
||||
- generatedProto.__hasRegisterFinished = true;
|
||||
+ // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
+ // and not the generated class prototype; if the element has been
|
||||
+ // extended, these are *not* the same.
|
||||
+ const proto = Object.getPrototypeOf(this);
|
||||
+ // Only set flag when generated prototype itself is registered,
|
||||
+ // as this element may be extended from, and needs to run `registered`
|
||||
+ // on all behaviors on the subclass as well.
|
||||
+ if (proto === generatedProto) {
|
||||
+ generatedProto.__hasRegisterFinished = true;
|
||||
+ }
|
||||
// ensure superclass is registered first.
|
||||
super._registered();
|
||||
// copy properties onto the generated class lazily if we're optimizing,
|
||||
- if (legacyOptimizations) {
|
||||
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
|
||||
+ generatedProto.__hasCopiedProperties = true;
|
||||
copyPropertiesToProto(generatedProto);
|
||||
}
|
||||
- // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
- // and not the generated class prototype; if the element has been
|
||||
- // extended, these are *not* the same.
|
||||
- const proto = Object.getPrototypeOf(this);
|
||||
let list = lifecycle.beforeRegister;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
77
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
77
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
631
.yarn/releases/yarn-3.0.2.cjs
vendored
Executable file
631
.yarn/releases/yarn-3.0.2.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
9
.yarnrc.yml
Normal file
9
.yarnrc.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.0.2.cjs
|
170
build-scripts/babel-plugins/inline-constants-plugin.js
Normal file
170
build-scripts/babel-plugins/inline-constants-plugin.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||
module.exports = function inlineConstants(babel, options, cwd) {
|
||||
const t = babel.types;
|
||||
|
||||
if (!Array.isArray(options.modules)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `modules` array to be passed"
|
||||
);
|
||||
}
|
||||
|
||||
if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected `resolveExtensions` to be an array"
|
||||
);
|
||||
}
|
||||
|
||||
const ignoreModuleNotFound = options.ignoreModuleNotFound;
|
||||
const resolveExtensions = options.resolveExtensions;
|
||||
|
||||
const hasRelativeModules = options.modules.some(
|
||||
(module) => module.startsWith(".") || module.startsWith("/")
|
||||
);
|
||||
|
||||
const modules = Object.fromEntries(
|
||||
options.modules.map((module) => {
|
||||
const absolute = module.startsWith(".")
|
||||
? require.resolve(module, { paths: [cwd] })
|
||||
: module;
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
return [absolute, require(absolute)];
|
||||
})
|
||||
);
|
||||
|
||||
const toLiteral = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return t.stringLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return t.numericLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return t.booleanLiteral(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return t.nullLiteral();
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot handle non-literal `" + value + "`"
|
||||
);
|
||||
};
|
||||
|
||||
const resolveAbsolute = (value, state, resolveExtensionIndex) => {
|
||||
if (!state.filename) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `filename` to be set for files"
|
||||
);
|
||||
}
|
||||
|
||||
if (resolveExtensions && resolveExtensionIndex !== undefined) {
|
||||
value += resolveExtensions[resolveExtensionIndex];
|
||||
}
|
||||
|
||||
try {
|
||||
return require.resolve(value, { paths: [path.dirname(state.filename)] });
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === "MODULE_NOT_FOUND" &&
|
||||
resolveExtensions &&
|
||||
(resolveExtensionIndex === undefined ||
|
||||
resolveExtensionIndex < resolveExtensions.length - 1)
|
||||
) {
|
||||
const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1;
|
||||
return resolveAbsolute(value, state, resolveExtensionIdx);
|
||||
}
|
||||
|
||||
if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const importDeclaration = (p, state) => {
|
||||
if (p.node.type !== "ImportDeclaration") {
|
||||
return;
|
||||
}
|
||||
const absolute =
|
||||
hasRelativeModules && p.node.source.value.startsWith(".")
|
||||
? resolveAbsolute(p.node.source.value, state)
|
||||
: p.node.source.value;
|
||||
|
||||
if (!absolute || !(absolute in modules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const module = modules[absolute];
|
||||
|
||||
for (const specifier of p.node.specifiers) {
|
||||
if (
|
||||
specifier.type === "ImportDefaultSpecifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!("default" in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access default export from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module.default);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else if (
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported &&
|
||||
specifier.imported.type === "Identifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!(specifier.imported.name in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access `" +
|
||||
specifier.imported.name +
|
||||
"` from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module[specifier.imported.name]);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else {
|
||||
throw new Error("Cannot handle specifier `" + specifier.type + "`");
|
||||
}
|
||||
}
|
||||
p.remove();
|
||||
};
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
ImportDeclaration: importDeclaration,
|
||||
},
|
||||
};
|
||||
};
|
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const env = require("./env.js");
|
||||
const paths = require("./paths.js");
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Bloats bundle and it's not used.
|
||||
path.resolve(require.resolve("moment"), "../locale"),
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
@@ -19,7 +18,8 @@ module.exports.emptyPackages = ({ latestBuild }) =>
|
||||
require.resolve("@polymer/paper-styles/default-theme.js"),
|
||||
// Loads stuff from a CDN
|
||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
|
||||
// Compatibility not needed for latest builds
|
||||
latestBuild &&
|
||||
// wrapped in require.resolve so it blows up if file no longer exists
|
||||
@@ -35,6 +35,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||
__VERSION__: JSON.stringify(env.version()),
|
||||
__DEMO__: false,
|
||||
__SUPERVISOR__: false,
|
||||
__BACKWARDS_COMPAT__: false,
|
||||
__STATIC_PATH__: "/static/",
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
@@ -51,17 +52,29 @@ module.exports.terserOptions = (latestBuild) => ({
|
||||
|
||||
module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
babelrc: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
!latestBuild && [
|
||||
require("@babel/preset-env").default,
|
||||
"@babel/preset-env",
|
||||
{
|
||||
useBuiltIns: "entry",
|
||||
corejs: "3.6",
|
||||
corejs: "3.15",
|
||||
bugfixes: true,
|
||||
},
|
||||
],
|
||||
require("@babel/preset-typescript").default,
|
||||
"@babel/preset-typescript",
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
[
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
"build-scripts/babel-plugins/inline-constants-plugin.js"
|
||||
),
|
||||
{
|
||||
modules: ["@mdi/js"],
|
||||
ignoreModuleNotFound: true,
|
||||
},
|
||||
],
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
!latestBuild && [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
@@ -70,25 +83,21 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
// Only support the syntax, Webpack will handle it.
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-top-level-await",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
[
|
||||
require("@babel/plugin-proposal-decorators").default,
|
||||
{ decoratorsBeforeExport: true },
|
||||
],
|
||||
[
|
||||
require("@babel/plugin-proposal-class-properties").default,
|
||||
{ loose: true },
|
||||
],
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||
["@babel/plugin-proposal-private-methods", { loose: true }],
|
||||
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||
["@babel/plugin-proposal-class-properties", { loose: true }],
|
||||
].filter(Boolean),
|
||||
exclude: [
|
||||
// \\ for Windows, / for Mac OS and Linux
|
||||
/node_modules[\\/]core-js/,
|
||||
/node_modules[\\/]webpack[\\/]buildin/,
|
||||
],
|
||||
});
|
||||
|
||||
// Are already ES5, cause warnings when babelified.
|
||||
module.exports.babelExclude = () => [
|
||||
require.resolve("@mdi/js/mdi.js"),
|
||||
require.resolve("hls.js"),
|
||||
];
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
|
||||
@@ -156,6 +165,7 @@ module.exports.config = {
|
||||
cast({ isProdBuild, latestBuild }) {
|
||||
const entry = {
|
||||
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
|
||||
media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
|
||||
};
|
||||
|
||||
if (latestBuild) {
|
||||
@@ -186,6 +196,9 @@ module.exports.config = {
|
||||
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
defineOverlay: {
|
||||
__SUPERVISOR__: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -198,6 +211,9 @@ module.exports.config = {
|
||||
publicPath: publicPath(latestBuild),
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
defineOverlay: {
|
||||
__DEMO__: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const paths = require("./paths.js");
|
||||
|
@@ -5,6 +5,7 @@ const env = require("../env");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./locale-data.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./gather-static.js");
|
||||
require("./compress.js");
|
||||
@@ -26,7 +27,8 @@ gulp.task(
|
||||
"gen-icons-json",
|
||||
"gen-pages-dev",
|
||||
"gen-index-app-dev",
|
||||
"build-translations"
|
||||
"build-translations",
|
||||
"build-locale-data"
|
||||
),
|
||||
"copy-static-app",
|
||||
env.useWDS()
|
||||
@@ -44,11 +46,11 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
"copy-static-app",
|
||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||
...// Don't compress running tests
|
||||
(env.isTest() ? [] : ["compress-app"]),
|
||||
// Don't compress running tests
|
||||
...(env.isTest() ? [] : ["compress-app"]),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-app-prod",
|
||||
|
@@ -18,7 +18,7 @@ gulp.task(
|
||||
},
|
||||
"clean-cast",
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
"copy-static-cast",
|
||||
"gen-index-cast-dev",
|
||||
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
||||
@@ -33,7 +33,7 @@ gulp.task(
|
||||
},
|
||||
"clean-cast",
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
"copy-static-cast",
|
||||
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
|
||||
"gen-index-cast-prod"
|
||||
|
@@ -5,32 +5,32 @@ require("./translations");
|
||||
|
||||
gulp.task(
|
||||
"clean",
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.app_output_root, paths.build_dir]);
|
||||
})
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.app_output_root, paths.build_dir])
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-demo",
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.demo_output_root, paths.build_dir]);
|
||||
})
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.demo_output_root, paths.build_dir])
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-cast",
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.cast_output_root, paths.build_dir]);
|
||||
})
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.cast_output_root, paths.build_dir])
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("clean-hassio", function cleanOutputAndBuildDir() {
|
||||
return del([paths.hassio_output_root, paths.build_dir]);
|
||||
});
|
||||
gulp.task("clean-hassio", () =>
|
||||
del([paths.hassio_output_root, paths.build_dir])
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-gallery",
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.gallery_output_root, paths.build_dir]);
|
||||
})
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.gallery_output_root, paths.gallery_build, paths.build_dir])
|
||||
)
|
||||
);
|
||||
|
@@ -20,7 +20,12 @@ gulp.task(
|
||||
},
|
||||
"clean-demo",
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
|
||||
gulp.parallel(
|
||||
"gen-icons-json",
|
||||
"gen-index-demo-dev",
|
||||
"build-translations",
|
||||
"build-locale-data"
|
||||
),
|
||||
"copy-static-demo",
|
||||
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
|
||||
)
|
||||
@@ -35,7 +40,7 @@ gulp.task(
|
||||
"clean-demo",
|
||||
// Cast needs to be backwards compatible and older HA has no translations
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
"copy-static-demo",
|
||||
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
|
||||
"gen-index-demo-prod"
|
||||
|
@@ -154,6 +154,15 @@ gulp.task("gen-index-cast-dev", (done) => {
|
||||
contentReceiver
|
||||
);
|
||||
|
||||
const contentMedia = renderCastTemplate("media", {
|
||||
latestMediaJS: "/frontend_latest/media.js",
|
||||
es5MediaJS: "/frontend_es5/media.js",
|
||||
});
|
||||
fs.outputFileSync(
|
||||
path.resolve(paths.cast_output_root, "media.html"),
|
||||
contentMedia
|
||||
);
|
||||
|
||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||
latestLauncherJS: "/frontend_latest/launcher.js",
|
||||
es5LauncherJS: "/frontend_es5/launcher.js",
|
||||
@@ -192,6 +201,15 @@ gulp.task("gen-index-cast-prod", (done) => {
|
||||
contentReceiver
|
||||
);
|
||||
|
||||
const contentMedia = renderCastTemplate("media", {
|
||||
latestMediaJS: latestManifest["media.js"],
|
||||
es5MediaJS: es5Manifest["media.js"],
|
||||
});
|
||||
fs.outputFileSync(
|
||||
path.resolve(paths.cast_output_root, "media.html"),
|
||||
contentMedia
|
||||
);
|
||||
|
||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||
latestLauncherJS: latestManifest["launcher.js"],
|
||||
es5LauncherJS: es5Manifest["launcher.js"],
|
||||
@@ -302,15 +320,23 @@ gulp.task("gen-index-hassio-prod", async () => {
|
||||
|
||||
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||
`
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
function loadES5() {
|
||||
var el = document.createElement('script');
|
||||
el.src = '${es5Entrypoint}';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) {
|
||||
loadES5();
|
||||
} else {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
loadES5();
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ encoding: "utf-8" }
|
||||
|
@@ -1,7 +1,11 @@
|
||||
/* eslint-disable */
|
||||
// Run demo develop mode
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const marked = require("marked");
|
||||
const glob = require("glob");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
@@ -15,26 +19,129 @@ require("./service-worker.js");
|
||||
require("./entry-html.js");
|
||||
require("./rollup.js");
|
||||
|
||||
gulp.task("gather-gallery-demos", async function gatherDemos() {
|
||||
const files = await fs.promises.readdir(
|
||||
path.resolve(paths.gallery_dir, "src/demos")
|
||||
);
|
||||
|
||||
let content = "export const DEMOS = {\n";
|
||||
|
||||
for (const file of files) {
|
||||
const demoId = path.basename(file, ".ts");
|
||||
const demoPath = "../src/demos/" + demoId;
|
||||
content += ` "${demoId}": () => import("${demoPath}"),\n`;
|
||||
}
|
||||
|
||||
content += "};";
|
||||
gulp.task("gather-gallery-pages", async function gatherPages() {
|
||||
const pageDir = path.resolve(paths.gallery_dir, "src/pages");
|
||||
const files = glob.sync(path.resolve(pageDir, "**/*"));
|
||||
|
||||
const galleryBuild = path.resolve(paths.gallery_dir, "build");
|
||||
|
||||
fs.mkdirSync(galleryBuild, { recursive: true });
|
||||
|
||||
let content = "export const PAGES = {\n";
|
||||
|
||||
const processed = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
if (fs.lstatSync(file).isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
const pageId = file.substring(pageDir.length + 1, file.lastIndexOf("."));
|
||||
|
||||
if (processed.has(pageId)) {
|
||||
continue;
|
||||
}
|
||||
processed.add(pageId);
|
||||
|
||||
const [category, name] = pageId.split("/", 2);
|
||||
|
||||
const demoFile = path.resolve(pageDir, `${pageId}.ts`);
|
||||
const descriptionFile = path.resolve(pageDir, `${pageId}.markdown`);
|
||||
const hasDemo = fs.existsSync(demoFile);
|
||||
let hasDescription = fs.existsSync(descriptionFile);
|
||||
let metadata = {};
|
||||
if (hasDescription) {
|
||||
let descriptionContent = fs.readFileSync(descriptionFile, "utf-8");
|
||||
|
||||
if (descriptionContent.startsWith("---")) {
|
||||
const metadataEnd = descriptionContent.indexOf("---", 3);
|
||||
metadata = yaml.load(descriptionContent.substring(3, metadataEnd));
|
||||
descriptionContent = descriptionContent
|
||||
.substring(metadataEnd + 3)
|
||||
.trim();
|
||||
}
|
||||
|
||||
// If description is just metadata
|
||||
if (descriptionContent === "") {
|
||||
hasDescription = false;
|
||||
} else {
|
||||
descriptionContent = marked(descriptionContent).replace(/`/g, "\\`");
|
||||
fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.resolve(galleryBuild, `${pageId}-description.ts`),
|
||||
`
|
||||
import {html} from "lit";
|
||||
export default html\`${descriptionContent}\`
|
||||
`
|
||||
);
|
||||
}
|
||||
}
|
||||
content += ` "${pageId}": {
|
||||
metadata: ${JSON.stringify(metadata)},
|
||||
${
|
||||
hasDescription
|
||||
? `description: () => import("./${pageId}-description").then(m => m.default),`
|
||||
: ""
|
||||
}
|
||||
${hasDemo ? `demo: () => import("../src/pages/${pageId}")` : ""}
|
||||
|
||||
},\n`;
|
||||
}
|
||||
|
||||
content += "};\n";
|
||||
|
||||
// Generate sidebar
|
||||
const sidebarPath = path.resolve(paths.gallery_dir, "sidebar.js");
|
||||
// To make watch work during development
|
||||
delete require.cache[sidebarPath];
|
||||
const sidebar = require(sidebarPath);
|
||||
|
||||
const pagesToProcess = {};
|
||||
for (const key of processed) {
|
||||
const [category, page] = key.split("/", 2);
|
||||
if (!(category in pagesToProcess)) {
|
||||
pagesToProcess[category] = new Set();
|
||||
}
|
||||
pagesToProcess[category].add(page);
|
||||
}
|
||||
|
||||
for (const group of Object.values(sidebar)) {
|
||||
const toProcess = pagesToProcess[group.category];
|
||||
delete pagesToProcess[group.category];
|
||||
|
||||
if (!toProcess) {
|
||||
console.error("Unknown category", group.category);
|
||||
if (!group.pages) {
|
||||
group.pages = [];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any pre-defined groups will not be sorted.
|
||||
if (group.pages) {
|
||||
for (const page of group.pages) {
|
||||
if (!toProcess.delete(page)) {
|
||||
console.error("Found unreferenced demo", page);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
group.pages = [];
|
||||
}
|
||||
for (const page of Array.from(toProcess).sort()) {
|
||||
group.pages.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [category, pages] of Object.entries(pagesToProcess)) {
|
||||
sidebar.push({
|
||||
category,
|
||||
header: category,
|
||||
pages: Array.from(pages).sort(),
|
||||
});
|
||||
}
|
||||
|
||||
content += `export const SIDEBAR = ${JSON.stringify(sidebar, null, 2)};\n`;
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(galleryBuild, "import-demos.ts"),
|
||||
path.resolve(galleryBuild, "import-pages.ts"),
|
||||
content,
|
||||
"utf-8"
|
||||
);
|
||||
@@ -51,11 +158,25 @@ gulp.task(
|
||||
gulp.parallel(
|
||||
"gen-icons-json",
|
||||
"build-translations",
|
||||
"gather-gallery-demos"
|
||||
"build-locale-data",
|
||||
"gather-gallery-pages"
|
||||
),
|
||||
"copy-static-gallery",
|
||||
"gen-index-gallery-dev",
|
||||
env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery"
|
||||
gulp.parallel(
|
||||
env.useRollup()
|
||||
? "rollup-dev-server-gallery"
|
||||
: "webpack-dev-server-gallery",
|
||||
async function watchMarkdownFiles() {
|
||||
gulp.watch(
|
||||
[
|
||||
path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"),
|
||||
path.resolve(paths.gallery_dir, "sidebar.js"),
|
||||
],
|
||||
gulp.series("gather-gallery-pages")
|
||||
);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -70,7 +191,8 @@ gulp.task(
|
||||
gulp.parallel(
|
||||
"gen-icons-json",
|
||||
"build-translations",
|
||||
"gather-gallery-demos"
|
||||
"build-locale-data",
|
||||
"gather-gallery-pages"
|
||||
),
|
||||
"copy-static-gallery",
|
||||
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const cpx = require("cpx");
|
||||
const fs = require("fs-extra");
|
||||
const paths = require("../paths");
|
||||
|
||||
@@ -13,19 +12,28 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
|
||||
const copyFileDir = (fromFile, toDir) =>
|
||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||
|
||||
const genStaticPath = (staticDir) => (...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
const genStaticPath =
|
||||
(staticDir) =>
|
||||
(...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
|
||||
function copyTranslations(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
// Translation output
|
||||
fs.copySync(
|
||||
polyPath("build-translations/output"),
|
||||
polyPath("build/translations/output"),
|
||||
staticPath("translations")
|
||||
);
|
||||
}
|
||||
|
||||
function copyLocaleData(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
// Locale data output
|
||||
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
|
||||
}
|
||||
|
||||
function copyMdiIcons(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
@@ -62,12 +70,20 @@ function copyLoaderJS(staticDir) {
|
||||
function copyFonts(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
// Local fonts
|
||||
cpx.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/*.woff2"),
|
||||
staticPath("fonts/roboto")
|
||||
fs.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/"),
|
||||
staticPath("fonts/roboto/"),
|
||||
{
|
||||
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function copyQrScannerWorker(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js"));
|
||||
}
|
||||
|
||||
function copyMapPanel(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(
|
||||
@@ -80,6 +96,11 @@ function copyMapPanel(staticDir) {
|
||||
);
|
||||
}
|
||||
|
||||
gulp.task("copy-locale-data", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
copyLocaleData(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-translations-app", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
copyTranslations(staticDir);
|
||||
@@ -90,6 +111,11 @@ gulp.task("copy-translations-supervisor", async () => {
|
||||
copyTranslations(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-locale-data-supervisor", async () => {
|
||||
const staticDir = paths.hassio_output_static;
|
||||
copyLocaleData(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-app", async () => {
|
||||
const staticDir = paths.app_output_static;
|
||||
// Basic static files
|
||||
@@ -99,10 +125,14 @@ gulp.task("copy-static-app", async () => {
|
||||
copyPolyfills(staticDir);
|
||||
copyFonts(staticDir);
|
||||
copyTranslations(staticDir);
|
||||
copyLocaleData(staticDir);
|
||||
copyMdiIcons(staticDir);
|
||||
|
||||
// Panel assets
|
||||
copyMapPanel(staticDir);
|
||||
|
||||
// Qr Scanner assets
|
||||
copyQrScannerWorker(staticDir);
|
||||
});
|
||||
|
||||
gulp.task("copy-static-demo", async () => {
|
||||
@@ -119,6 +149,7 @@ gulp.task("copy-static-demo", async () => {
|
||||
copyMapPanel(paths.demo_output_static);
|
||||
copyFonts(paths.demo_output_static);
|
||||
copyTranslations(paths.demo_output_static);
|
||||
copyLocaleData(paths.demo_output_static);
|
||||
copyMdiIcons(paths.demo_output_static);
|
||||
});
|
||||
|
||||
@@ -133,6 +164,7 @@ gulp.task("copy-static-cast", async () => {
|
||||
copyMapPanel(paths.cast_output_static);
|
||||
copyFonts(paths.cast_output_static);
|
||||
copyTranslations(paths.cast_output_static);
|
||||
copyLocaleData(paths.cast_output_static);
|
||||
copyMdiIcons(paths.cast_output_static);
|
||||
});
|
||||
|
||||
@@ -148,5 +180,6 @@ gulp.task("copy-static-gallery", async () => {
|
||||
copyMapPanel(paths.gallery_output_static);
|
||||
copyFonts(paths.gallery_output_static);
|
||||
copyTranslations(paths.gallery_output_static);
|
||||
copyLocaleData(paths.gallery_output_static);
|
||||
copyMdiIcons(paths.gallery_output_static);
|
||||
});
|
||||
|
@@ -22,17 +22,40 @@ const getMeta = () => {
|
||||
const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, {
|
||||
encoding,
|
||||
});
|
||||
return { path: svg.match(/ d="([^"]+)"/)[1], name: icon.name };
|
||||
return {
|
||||
path: svg.match(/ d="([^"]+)"/)[1],
|
||||
name: icon.name,
|
||||
tags: icon.tags,
|
||||
aliases: icon.aliases,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const addRemovedMeta = (meta) => {
|
||||
const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding });
|
||||
const removed = JSON.parse(file);
|
||||
const combinedMeta = [...meta, ...removed];
|
||||
const removedMeta = removed.map((removeIcon) => ({
|
||||
path: removeIcon.path,
|
||||
name: removeIcon.name,
|
||||
tags: [],
|
||||
aliases: [],
|
||||
}));
|
||||
const combinedMeta = [...meta, ...removedMeta];
|
||||
return combinedMeta.sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
const homeAutomationTag = "Home Automation";
|
||||
|
||||
const orderMeta = (meta) => {
|
||||
const homeAutomationMeta = meta.filter((icon) =>
|
||||
icon.tags.includes(homeAutomationTag)
|
||||
);
|
||||
const otherMeta = meta.filter(
|
||||
(icon) => !icon.tags.includes(homeAutomationTag)
|
||||
);
|
||||
return [...homeAutomationMeta, ...otherMeta];
|
||||
};
|
||||
|
||||
const splitBySize = (meta) => {
|
||||
const chunks = [];
|
||||
const CHUNK_SIZE = 50000;
|
||||
@@ -77,8 +100,10 @@ const findDifferentiator = (curString, prevString) => {
|
||||
};
|
||||
|
||||
gulp.task("gen-icons-json", (done) => {
|
||||
const meta = addRemovedMeta(getMeta());
|
||||
const split = splitBySize(meta);
|
||||
const meta = getMeta();
|
||||
|
||||
const metaAndRemoved = addRemovedMeta(meta);
|
||||
const split = splitBySize(metaAndRemoved);
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
@@ -116,5 +141,18 @@ gulp.task("gen-icons-json", (done) => {
|
||||
JSON.stringify({ version: package.version, parts })
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(OUTPUT_DIR, "iconList.json"),
|
||||
JSON.stringify(
|
||||
orderMeta(meta).map((icon) => ({
|
||||
name: icon.name,
|
||||
keywords: [
|
||||
...icon.tags.map((t) => t.toLowerCase().replace(/\s\/\s/g, " ")),
|
||||
...icon.aliases,
|
||||
],
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@@ -1,9 +1,6 @@
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
|
||||
require("./clean.js");
|
||||
require("./gen-icons-json.js");
|
||||
@@ -20,10 +17,11 @@ gulp.task(
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
"gen-index-hassio-dev",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
"build-locale-data",
|
||||
"copy-locale-data-supervisor",
|
||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||
)
|
||||
);
|
||||
@@ -35,9 +33,10 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
"build-supervisor-translations",
|
||||
"copy-translations-supervisor",
|
||||
"build-locale-data",
|
||||
"copy-locale-data-supervisor",
|
||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||
"gen-index-hassio-prod",
|
||||
...// Don't compress running tests
|
||||
|
74
build-scripts/gulp/locale-data.js
Executable file
74
build-scripts/gulp/locale-data.js
Executable file
@@ -0,0 +1,74 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const del = require("del");
|
||||
const path = require("path");
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const paths = require("../paths");
|
||||
|
||||
const outDir = "build/locale-data";
|
||||
|
||||
gulp.task("clean-locale-data", () => del([outDir]));
|
||||
|
||||
gulp.task("ensure-locale-data-build-dir", (done) => {
|
||||
if (!fs.existsSync(outDir)) {
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
const modules = {
|
||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||
"intl-datetimeformat": "DateTimeFormat",
|
||||
"intl-numberformat": "NumberFormat",
|
||||
};
|
||||
|
||||
gulp.task("create-locale-data", (done) => {
|
||||
const translationMeta = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(paths.translations_src, "translationMetadata.json")
|
||||
)
|
||||
);
|
||||
Object.entries(modules).forEach(([module, className]) => {
|
||||
Object.keys(translationMeta).forEach((lang) => {
|
||||
try {
|
||||
const localeData = String(
|
||||
fs.readFileSync(
|
||||
require.resolve(`@formatjs/${module}/locale-data/${lang}.js`)
|
||||
)
|
||||
)
|
||||
.replace(
|
||||
new RegExp(
|
||||
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
||||
"im"
|
||||
),
|
||||
""
|
||||
)
|
||||
.replace(/\)\s*}/im, "");
|
||||
// make sure we have valid JSON
|
||||
JSON.parse(localeData);
|
||||
if (!fs.existsSync(path.join(outDir, module))) {
|
||||
fs.mkdirSync(path.join(outDir, module), { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(outDir, `${module}/${lang}.json`),
|
||||
localeData
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.code !== "MODULE_NOT_FOUND") {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"build-locale-data",
|
||||
gulp.series(
|
||||
"clean-locale-data",
|
||||
"ensure-locale-data-build-dir",
|
||||
"create-locale-data"
|
||||
)
|
||||
);
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const crypto = require("crypto");
|
||||
const del = require("del");
|
||||
const path = require("path");
|
||||
@@ -15,7 +17,7 @@ const paths = require("../paths");
|
||||
|
||||
const inFrontendDir = "translations/frontend";
|
||||
const inBackendDir = "translations/backend";
|
||||
const workDir = "build-translations";
|
||||
const workDir = "build/translations";
|
||||
const fullDir = workDir + "/full";
|
||||
const coreDir = workDir + "/core";
|
||||
const outDir = workDir + "/output";
|
||||
@@ -26,13 +28,6 @@ gulp.task("translations-enable-merge-backend", (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
String.prototype.rsplit = function (sep, maxsplit) {
|
||||
var split = this.split(sep);
|
||||
return maxsplit
|
||||
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
|
||||
: split;
|
||||
};
|
||||
|
||||
// Panel translations which should be split from the core translations.
|
||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
require("../../src/translations/en.json").ui.panel
|
||||
@@ -40,7 +35,7 @@ const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
|
||||
function recursiveFlatten(prefix, data) {
|
||||
let output = {};
|
||||
Object.keys(data).forEach(function (key) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (typeof data[key] === "object") {
|
||||
output = {
|
||||
...output,
|
||||
@@ -101,15 +96,19 @@ function lokaliseTransform(data, original, file) {
|
||||
if (value instanceof Object) {
|
||||
output[key] = lokaliseTransform(value, original, file);
|
||||
} else {
|
||||
output[key] = value.replace(re_key_reference, (match, key) => {
|
||||
const replace = key.split("::").reduce((tr, k) => {
|
||||
output[key] = value.replace(re_key_reference, (_match, lokalise_key) => {
|
||||
const replace = lokalise_key.split("::").reduce((tr, k) => {
|
||||
if (!tr) {
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
}
|
||||
return tr[k];
|
||||
}, original);
|
||||
if (typeof replace !== "string") {
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
@@ -118,18 +117,16 @@ function lokaliseTransform(data, original, file) {
|
||||
return output;
|
||||
}
|
||||
|
||||
gulp.task("clean-translations", function () {
|
||||
return del([workDir]);
|
||||
});
|
||||
gulp.task("clean-translations", () => del([workDir]));
|
||||
|
||||
gulp.task("ensure-translations-build-dir", (done) => {
|
||||
if (!fs.existsSync(workDir)) {
|
||||
fs.mkdirSync(workDir);
|
||||
fs.mkdirSync(workDir, { recursive: true });
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", function (cb) {
|
||||
gulp.task("create-test-metadata", (cb) => {
|
||||
fs.writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
@@ -143,17 +140,13 @@ gulp.task("create-test-metadata", function (cb) {
|
||||
|
||||
gulp.task(
|
||||
"create-test-translation",
|
||||
gulp.series("create-test-metadata", function createTestTranslation() {
|
||||
return gulp
|
||||
gulp.series("create-test-metadata", () =>
|
||||
gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return recursiveEmpty(data);
|
||||
})
|
||||
)
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir));
|
||||
})
|
||||
.pipe(gulp.dest(workDir))
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -165,7 +158,7 @@ gulp.task(
|
||||
* project is buildable immediately after merging new translation keys, since
|
||||
* the Lokalise update to translations/en.json will not happen immediately.
|
||||
*/
|
||||
gulp.task("build-master-translation", function () {
|
||||
gulp.task("build-master-translation", () => {
|
||||
const src = [path.join(paths.translations_src, "en.json")];
|
||||
|
||||
if (mergeBackend) {
|
||||
@@ -174,11 +167,7 @@ gulp.task("build-master-translation", function () {
|
||||
|
||||
return gulp
|
||||
.src(src)
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
merge({
|
||||
fileName: "translationMaster.json",
|
||||
@@ -187,18 +176,14 @@ gulp.task("build-master-translation", function () {
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-merged-translations", function () {
|
||||
return gulp
|
||||
gulp.task("build-merged-translations", () =>
|
||||
gulp
|
||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
||||
allowEmpty: true,
|
||||
})
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
foreach(function (stream, file) {
|
||||
foreach((stream, file) => {
|
||||
// For each language generate a merged json file. It begins with the master
|
||||
// translation as a failsafe for untranslated strings, and merges all parent
|
||||
// tags into one file for each specific subtag
|
||||
@@ -230,17 +215,17 @@ gulp.task("build-merged-translations", function () {
|
||||
)
|
||||
.pipe(gulp.dest(fullDir));
|
||||
})
|
||||
);
|
||||
});
|
||||
)
|
||||
);
|
||||
|
||||
var taskName;
|
||||
let taskName;
|
||||
|
||||
const splitTasks = [];
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
taskName = "build-translation-fragment-" + fragment;
|
||||
gulp.task(taskName, function () {
|
||||
gulp.task(taskName, () =>
|
||||
// Return only the translations for this fragment.
|
||||
return gulp
|
||||
gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
@@ -251,18 +236,18 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
},
|
||||
}))
|
||||
)
|
||||
.pipe(gulp.dest(workDir + "/" + fragment));
|
||||
});
|
||||
.pipe(gulp.dest(workDir + "/" + fragment))
|
||||
);
|
||||
splitTasks.push(taskName);
|
||||
});
|
||||
|
||||
taskName = "build-translation-core";
|
||||
gulp.task(taskName, function () {
|
||||
gulp.task(taskName, () =>
|
||||
// Remove the fragment translations from the core translation.
|
||||
return gulp
|
||||
gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data, file) => {
|
||||
transform((data, _file) => {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
@@ -270,14 +255,14 @@ gulp.task(taskName, function () {
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(coreDir));
|
||||
});
|
||||
.pipe(gulp.dest(coreDir))
|
||||
);
|
||||
|
||||
splitTasks.push(taskName);
|
||||
|
||||
gulp.task("build-flattened-translations", function () {
|
||||
gulp.task("build-flattened-translations", () =>
|
||||
// Flatten the split versions of our translations, and move them into outDir
|
||||
return gulp
|
||||
gulp
|
||||
.src(
|
||||
TRANSLATION_FRAGMENTS.map(
|
||||
(fragment) => workDir + "/" + fragment + "/*.json"
|
||||
@@ -285,41 +270,45 @@ gulp.task("build-flattened-translations", function () {
|
||||
{ base: workDir }
|
||||
)
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
transform((data) =>
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
})
|
||||
flatten(data)
|
||||
)
|
||||
)
|
||||
.pipe(
|
||||
rename((filePath) => {
|
||||
if (filePath.dirname === "core") {
|
||||
filePath.dirname = "";
|
||||
}
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
|
||||
const fingerprints = {};
|
||||
|
||||
gulp.task(
|
||||
"build-translation-fingerprints",
|
||||
function fingerprintTranslationFiles() {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
gulp.task("build-translation-fingerprints", () => {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (env.isProdBuild()) {
|
||||
mapFiles(outDir, ".json", (filename) => {
|
||||
const parsed = path.parse(filename);
|
||||
|
||||
@@ -335,35 +324,43 @@ gulp.task(
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
}
|
||||
);
|
||||
|
||||
gulp.task("build-translation-fragment-supervisor", function () {
|
||||
return gulp
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-fragment-supervisor", () =>
|
||||
gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(transform((data) => data.supervisor))
|
||||
.pipe(gulp.dest(workDir + "/supervisor"));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-flatten-supervisor", function () {
|
||||
return gulp
|
||||
.src(workDir + "/supervisor/*.json")
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
rename((filePath) => {
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
.pipe(gulp.dest(workDir + "/supervisor"))
|
||||
);
|
||||
|
||||
gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||
return gulp
|
||||
gulp.task("build-translation-flatten-supervisor", () =>
|
||||
gulp
|
||||
.src(workDir + "/supervisor/*.json")
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
|
||||
gulp.task("build-translation-write-metadata", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
@@ -374,13 +371,14 @@ gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform(function (data) {
|
||||
transform((data) => {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (value.nativeName) {
|
||||
newData[key] = value;
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
@@ -396,19 +394,26 @@ gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
.pipe(gulp.dest(workDir))
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"create-translations",
|
||||
gulp.series(
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"build-translations",
|
||||
gulp.series(
|
||||
"clean-translations",
|
||||
"ensure-translations-build-dir",
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations",
|
||||
"create-translations",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
)
|
||||
|
@@ -1,4 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Tasks to run webpack.
|
||||
const fs = require("fs");
|
||||
const gulp = require("gulp");
|
||||
const webpack = require("webpack");
|
||||
const WebpackDevServer = require("webpack-dev-server");
|
||||
@@ -18,6 +20,13 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
createConfigFunc({ ...params, latestBuild: false }),
|
||||
];
|
||||
|
||||
const isWsl =
|
||||
fs.existsSync("/proc/version") &&
|
||||
fs
|
||||
.readFileSync("/proc/version", "utf-8")
|
||||
.toLocaleLowerCase()
|
||||
.includes("microsoft");
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* compiler: import("webpack").Compiler,
|
||||
@@ -26,26 +35,29 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
* listenHost?: string
|
||||
* }}
|
||||
*/
|
||||
const runDevServer = ({
|
||||
const runDevServer = async ({
|
||||
compiler,
|
||||
contentBase,
|
||||
port,
|
||||
listenHost = "localhost",
|
||||
}) =>
|
||||
new WebpackDevServer(compiler, {
|
||||
open: true,
|
||||
watchContentBase: true,
|
||||
contentBase,
|
||||
}).listen(port, listenHost, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
// Server listening
|
||||
log(
|
||||
"[webpack-dev-server]",
|
||||
`Project is running at http://localhost:${port}`
|
||||
);
|
||||
});
|
||||
}) => {
|
||||
const server = new WebpackDevServer(
|
||||
{
|
||||
open: true,
|
||||
host: listenHost,
|
||||
port,
|
||||
static: {
|
||||
directory: contentBase,
|
||||
watch: true,
|
||||
},
|
||||
},
|
||||
compiler
|
||||
);
|
||||
|
||||
await server.start();
|
||||
// Server listening
|
||||
log("[webpack-dev-server]", `Project is running at http://localhost:${port}`);
|
||||
};
|
||||
|
||||
const doneHandler = (done) => (err, stats) => {
|
||||
if (err) {
|
||||
@@ -57,6 +69,7 @@ const doneHandler = (done) => (err, stats) => {
|
||||
}
|
||||
|
||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(stats.toString("minimal"));
|
||||
}
|
||||
|
||||
@@ -78,13 +91,14 @@ const prodBuild = (conf) =>
|
||||
|
||||
gulp.task("webpack-watch-app", () => {
|
||||
// This command will run forever because we don't close compiler
|
||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
||||
{ ignored: /build-translations/ },
|
||||
doneHandler()
|
||||
);
|
||||
webpack(
|
||||
process.env.ES5
|
||||
? bothBuilds(createAppConfig, { isProdBuild: false })
|
||||
: createAppConfig({ isProdBuild: false, latestBuild: true })
|
||||
).watch({ poll: isWsl }, doneHandler());
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("build-translations", "copy-translations-app")
|
||||
gulp.series("create-translations", "copy-translations-app")
|
||||
);
|
||||
});
|
||||
|
||||
@@ -96,13 +110,13 @@ gulp.task("webpack-prod-app", () =>
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-demo", () => {
|
||||
gulp.task("webpack-dev-server-demo", () =>
|
||||
runDevServer({
|
||||
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
||||
contentBase: paths.demo_output_root,
|
||||
port: 8090,
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task("webpack-prod-demo", () =>
|
||||
prodBuild(
|
||||
@@ -112,15 +126,15 @@ gulp.task("webpack-prod-demo", () =>
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-cast", () => {
|
||||
gulp.task("webpack-dev-server-cast", () =>
|
||||
runDevServer({
|
||||
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
||||
contentBase: paths.cast_output_root,
|
||||
port: 8080,
|
||||
// Accessible from the network, because that's how Cast hits it.
|
||||
listenHost: "0.0.0.0",
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task("webpack-prod-cast", () =>
|
||||
prodBuild(
|
||||
@@ -137,7 +151,7 @@ gulp.task("webpack-watch-hassio", () => {
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({ ignored: /build-translations/ }, doneHandler());
|
||||
).watch({ ignored: /build/, poll: isWsl }, doneHandler());
|
||||
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
@@ -153,14 +167,15 @@ gulp.task("webpack-prod-hassio", () =>
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-gallery", () => {
|
||||
gulp.task("webpack-dev-server-gallery", () =>
|
||||
runDevServer({
|
||||
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
||||
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
||||
contentBase: paths.gallery_output_root,
|
||||
port: 8100,
|
||||
});
|
||||
});
|
||||
listenHost: "0.0.0.0",
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task("webpack-prod-gallery", () =>
|
||||
prodBuild(
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
@@ -25,6 +26,7 @@ module.exports = {
|
||||
cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"),
|
||||
|
||||
gallery_dir: path.resolve(__dirname, "../gallery"),
|
||||
gallery_build: path.resolve(__dirname, "../gallery/build"),
|
||||
gallery_output_root: path.resolve(__dirname, "../gallery/dist"),
|
||||
gallery_output_latest: path.resolve(
|
||||
__dirname,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
@@ -32,88 +33,77 @@ const createRollupConfig = ({
|
||||
publicPath,
|
||||
dontHash,
|
||||
isWDS,
|
||||
}) => {
|
||||
return {
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}) => ({
|
||||
/**
|
||||
* @type { import("rollup").InputOptions }
|
||||
*/
|
||||
inputOptions: {
|
||||
input: entry,
|
||||
// Some entry points contain no JavaScript. This setting silences a warning about that.
|
||||
// https://rollupjs.org/guide/en/#preserveentrysignatures
|
||||
preserveEntrySignatures: false,
|
||||
plugins: [
|
||||
ignore({
|
||||
files: bundle.emptyPackages({ latestBuild }),
|
||||
}),
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
resolve({
|
||||
extensions,
|
||||
preferBuiltins: false,
|
||||
browser: true,
|
||||
rootDir: paths.polymer_dir,
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
"js-yaml": ["safeDump", "safeLoad"],
|
||||
},
|
||||
}),
|
||||
json(),
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
exclude: bundle.babelExclude(),
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
|
||||
}),
|
||||
replace(
|
||||
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
||||
),
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames:
|
||||
isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames:
|
||||
isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
};
|
||||
};
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
*/
|
||||
outputOptions: {
|
||||
// https://rollupjs.org/guide/en/#outputdir
|
||||
dir: outputPath,
|
||||
// https://rollupjs.org/guide/en/#outputformat
|
||||
format: latestBuild ? "es" : "systemjs",
|
||||
// https://rollupjs.org/guide/en/#outputexternallivebindings
|
||||
externalLiveBindings: false,
|
||||
// https://rollupjs.org/guide/en/#outputentryfilenames
|
||||
// https://rollupjs.org/guide/en/#outputchunkfilenames
|
||||
// https://rollupjs.org/guide/en/#outputassetfilenames
|
||||
entryFileNames:
|
||||
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
|
||||
chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
|
||||
assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
|
||||
// https://rollupjs.org/guide/en/#outputsourcemap
|
||||
sourcemap: isProdBuild ? true : "inline",
|
||||
},
|
||||
});
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
return createRollupConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
|
||||
createRollupConfig(
|
||||
bundle.config.app({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
@@ -121,31 +111,24 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
isWDS,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createRollupConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createRollupConfig(
|
||||
bundle.config.demo({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
};
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
|
@@ -1,10 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle");
|
||||
const bundle = require("./bundle.js");
|
||||
const log = require("fancy-log");
|
||||
const WebpackBar = require("webpackbar");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
ignoredFirst = false;
|
||||
@@ -46,15 +48,18 @@ const createWebpackConfig = ({
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$|\.ts$/,
|
||||
exclude: bundle.babelExclude(),
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: bundle.babelOptions({ latestBuild }),
|
||||
options: {
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
cacheDirectory: !isProdBuild,
|
||||
cacheCompression: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: "raw-loader",
|
||||
type: "asset/source",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -66,8 +71,11 @@ const createWebpackConfig = ({
|
||||
terserOptions: bundle.terserOptions(latestBuild),
|
||||
}),
|
||||
],
|
||||
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
},
|
||||
plugins: [
|
||||
new WebpackBar({ fancy: !isProdBuild }),
|
||||
new WebpackManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
@@ -94,6 +102,7 @@ const createWebpackConfig = ({
|
||||
? path.resolve(context, resource)
|
||||
: require.resolve(resource);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
"Error in Home Assistant ignore plugin",
|
||||
resource,
|
||||
@@ -111,66 +120,61 @@ const createWebpackConfig = ({
|
||||
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
new RegExp(
|
||||
require.resolve(
|
||||
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
|
||||
)
|
||||
),
|
||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
||||
),
|
||||
!isProdBuild && new LogStartCompilePlugin(),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
alias: {
|
||||
"lit/decorators$": "lit/decorators.js",
|
||||
"lit/directive$": "lit/directive.js",
|
||||
"lit/directives/until$": "lit/directives/until.js",
|
||||
"lit/directives/class-map$": "lit/directives/class-map.js",
|
||||
"lit/directives/style-map$": "lit/directives/style-map.js",
|
||||
"lit/directives/if-defined$": "lit/directives/if-defined.js",
|
||||
"lit/directives/guard$": "lit/directives/guard.js",
|
||||
"lit/directives/cache$": "lit/directives/cache.js",
|
||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) {
|
||||
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) {
|
||||
return `${chunk.name}.js`;
|
||||
}
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js",
|
||||
path: outputPath,
|
||||
publicPath,
|
||||
// To silence warning in worker plugin
|
||||
globalObject: "self",
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
return createWebpackConfig(
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
|
||||
createWebpackConfig(
|
||||
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
|
||||
);
|
||||
};
|
||||
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
};
|
||||
const createCastConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
|
||||
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.hassio({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
return createWebpackConfig(
|
||||
bundle.config.gallery({ isProdBuild, latestBuild })
|
||||
);
|
||||
};
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
|
||||
createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
|
||||
|
||||
module.exports = {
|
||||
createAppConfig,
|
||||
|
@@ -139,7 +139,7 @@
|
||||
Your authentication credentials or Home Assistant url are never sent
|
||||
to the Cloud. You can validate this behavior in
|
||||
<a
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
||||
href="https://github.com/home-assistant/frontend/tree/dev/cast"
|
||||
target="_blank"
|
||||
>the source code</a
|
||||
>.
|
||||
|
46
cast/src/html/media.html.template
Normal file
46
cast/src/html/media.html.template
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
|
||||
<style>
|
||||
body {
|
||||
--logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
||||
--logo-repeat: no-repeat;
|
||||
--playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
||||
--theme-hue: 200;
|
||||
--progress-color: #03a9f4;
|
||||
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
||||
--splash-size: cover;
|
||||
--background-color: #41bdf5;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
|
||||
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
|
||||
s.parentNode.insertBefore(g,s)}(document,'script'));
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<%= renderTemplate('_js_base') %>
|
||||
|
||||
<cast-media-player></cast-media-player>
|
||||
|
||||
<script>
|
||||
import("<%= latestMediaJS %>");
|
||||
window.latestJS = true;
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if (!window.latestJS) {
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5MediaJS %>");
|
||||
};
|
||||
<% } else { %>
|
||||
_ls("<%= es5MediaJS %>");
|
||||
<% } %>
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -1,16 +1,10 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiCast, mdiCastConnected } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
castSendShowLovelaceView,
|
||||
@@ -24,6 +18,7 @@ import {
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
@@ -32,7 +27,6 @@ import {
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
|
||||
@customElement("hc-cast")
|
||||
class HcCast extends LitElement {
|
||||
@@ -42,9 +36,9 @@ class HcCast extends LitElement {
|
||||
|
||||
@property() public castManager!: CastManager;
|
||||
|
||||
@internalProperty() private askWrite = false;
|
||||
@state() private askWrite = false;
|
||||
|
||||
@internalProperty() private lovelaceConfig?: LovelaceConfig | null;
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
@@ -54,9 +48,7 @@ class HcCast extends LitElement {
|
||||
const error =
|
||||
this.castManager.castState === "NO_DEVICES_AVAILABLE"
|
||||
? html`
|
||||
<p>
|
||||
There were no suitable Chromecast devices to cast to found.
|
||||
</p>
|
||||
<p>There were no suitable Chromecast devices to cast to found.</p>
|
||||
`
|
||||
: undefined;
|
||||
|
||||
@@ -83,7 +75,7 @@ class HcCast extends LitElement {
|
||||
? html`
|
||||
<p class="center-item">
|
||||
<mwc-button raised @click=${this._handleLaunch}>
|
||||
<ha-icon icon="hass:cast"></ha-icon>
|
||||
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
|
||||
Start Casting
|
||||
</mwc-button>
|
||||
</p>
|
||||
@@ -121,7 +113,7 @@ class HcCast extends LitElement {
|
||||
${this.castManager.status
|
||||
? html`
|
||||
<mwc-button @click=${this._handleLaunch}>
|
||||
<ha-icon icon="hass:cast-connected"></ha-icon>
|
||||
<ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon>
|
||||
Manage
|
||||
</mwc-button>
|
||||
`
|
||||
@@ -201,12 +193,12 @@ class HcCast extends LitElement {
|
||||
}
|
||||
this.connection.close();
|
||||
location.reload();
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
alert("Unable to log out!");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.center-item {
|
||||
display: flex;
|
||||
@@ -243,7 +235,7 @@ class HcCast extends LitElement {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
mwc-button ha-icon {
|
||||
mwc-button ha-svg-icon {
|
||||
margin-right: 8px;
|
||||
height: 18px;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiCastConnected, mdiCast } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
Auth,
|
||||
@@ -11,22 +12,15 @@ import {
|
||||
getAuth,
|
||||
getAuthOptions,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
loadTokens,
|
||||
saveTokens,
|
||||
} from "../../../../src/common/auth/token_storage";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
||||
import "./hc-layout";
|
||||
@@ -60,19 +54,19 @@ const INTRO = html`
|
||||
|
||||
@customElement("hc-connect")
|
||||
export class HcConnect extends LitElement {
|
||||
@internalProperty() private loading = false;
|
||||
@state() private loading = false;
|
||||
|
||||
// If we had stored credentials but we cannot connect,
|
||||
// show a screen asking retry or logout.
|
||||
@internalProperty() private cannotConnect = false;
|
||||
@state() private cannotConnect = false;
|
||||
|
||||
@internalProperty() private error?: string | TemplateResult;
|
||||
@state() private error?: string | TemplateResult;
|
||||
|
||||
@internalProperty() private auth?: Auth;
|
||||
@state() private auth?: Auth;
|
||||
|
||||
@internalProperty() private connection?: Connection;
|
||||
@state() private connection?: Connection;
|
||||
|
||||
@internalProperty() private castManager?: CastManager | null;
|
||||
@state() private castManager?: CastManager | null;
|
||||
|
||||
private openDemo = false;
|
||||
|
||||
@@ -86,9 +80,7 @@ export class HcConnect extends LitElement {
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href="/">
|
||||
<mwc-button>
|
||||
Retry
|
||||
</mwc-button>
|
||||
<mwc-button> Retry </mwc-button>
|
||||
</a>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
|
||||
@@ -136,11 +128,11 @@ export class HcConnect extends LitElement {
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._handleDemo}>
|
||||
Show Demo
|
||||
<ha-icon
|
||||
.icon=${this.castManager.castState === "CONNECTED"
|
||||
? "hass:cast-connected"
|
||||
: "hass:cast"}
|
||||
></ha-icon>
|
||||
<ha-svg-icon
|
||||
.path=${this.castManager.castState === "CONNECTED"
|
||||
? mdiCastConnected
|
||||
: mdiCast}
|
||||
></ha-svg-icon>
|
||||
</mwc-button>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
|
||||
@@ -221,7 +213,7 @@ export class HcConnect extends LitElement {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(value);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
this.error = "Invalid URL";
|
||||
return;
|
||||
}
|
||||
@@ -249,7 +241,7 @@ export class HcConnect extends LitElement {
|
||||
try {
|
||||
this.loading = true;
|
||||
auth = await getAuth(options);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
|
||||
this.cannotConnect = true;
|
||||
return;
|
||||
@@ -268,7 +260,7 @@ export class HcConnect extends LitElement {
|
||||
|
||||
try {
|
||||
conn = await createConnection({ auth });
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// In case of saved tokens, silently solve problems.
|
||||
if (init === "saved-tokens") {
|
||||
if (err === ERR_CANNOT_CONNECT) {
|
||||
@@ -294,12 +286,12 @@ export class HcConnect extends LitElement {
|
||||
try {
|
||||
saveTokens(null);
|
||||
location.reload();
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
alert("Unable to log out!");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.card-content a {
|
||||
color: var(--primary-color);
|
||||
@@ -316,7 +308,7 @@ export class HcConnect extends LitElement {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
mwc-button ha-icon {
|
||||
mwc-button ha-svg-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
|
@@ -4,15 +4,8 @@ import {
|
||||
getUser,
|
||||
HassUser,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
|
||||
@customElement("hc-layout")
|
||||
@@ -69,7 +62,7 @@ class HcLayout extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
|
22
cast/src/media/entrypoint.ts
Normal file
22
cast/src/media/entrypoint.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
const castContext = cast.framework.CastReceiverContext.getInstance();
|
||||
|
||||
const playerManager = castContext.getPlayerManager();
|
||||
|
||||
playerManager.setMessageInterceptor(
|
||||
cast.framework.messages.MessageType.LOAD,
|
||||
(loadRequestData) => {
|
||||
const media = loadRequestData.media;
|
||||
// Special handling if it came from Google Assistant
|
||||
if (media.entity) {
|
||||
media.contentId = media.entity;
|
||||
media.streamType = cast.framework.messages.StreamType.LIVE;
|
||||
media.contentType = "application/vnd.apple.mpegurl";
|
||||
// @ts-ignore
|
||||
media.hlsVideoSegmentFormat =
|
||||
cast.framework.messages.HlsVideoSegmentFormat.FMP4;
|
||||
}
|
||||
return loadRequestData;
|
||||
}
|
||||
);
|
||||
|
||||
castContext.start();
|
@@ -5,8 +5,8 @@ import {
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
const touchSupported = castContext.getDeviceCapabilities()
|
||||
.touch_input_supported;
|
||||
const touchSupported =
|
||||
castContext.getDeviceCapabilities().touch_input_supported;
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
|
@@ -8,6 +8,9 @@ import { ReceivedMessage } from "./types";
|
||||
|
||||
const lovelaceController = new HcMain();
|
||||
document.body.append(lovelaceController);
|
||||
lovelaceController.addEventListener("cast-view-changed", (ev) => {
|
||||
playDummyMedia(ev.detail.title);
|
||||
});
|
||||
|
||||
const mediaPlayer = document.createElement("cast-media-player");
|
||||
mediaPlayer.style.display = "none";
|
||||
@@ -28,6 +31,31 @@ const setTouchControlsVisibility = (visible: boolean) => {
|
||||
}
|
||||
};
|
||||
|
||||
let timeOut: number | undefined;
|
||||
|
||||
const playDummyMedia = (viewTitle?: string) => {
|
||||
const loadRequestData = new cast.framework.messages.LoadRequestData();
|
||||
loadRequestData.autoplay = true;
|
||||
loadRequestData.media = new cast.framework.messages.MediaInformation();
|
||||
loadRequestData.media.contentId =
|
||||
"https://cast.home-assistant.io/images/google-nest-hub.png";
|
||||
loadRequestData.media.contentType = "image/jpeg";
|
||||
loadRequestData.media.streamType = cast.framework.messages.StreamType.NONE;
|
||||
const metadata = new cast.framework.messages.GenericMediaMetadata();
|
||||
metadata.title = viewTitle;
|
||||
loadRequestData.media.metadata = metadata;
|
||||
|
||||
loadRequestData.requestId = 0;
|
||||
playerManager.load(loadRequestData);
|
||||
if (timeOut) {
|
||||
clearTimeout(timeOut);
|
||||
timeOut = undefined;
|
||||
}
|
||||
if (castContext.getDeviceCapabilities().touch_input_supported) {
|
||||
timeOut = window.setTimeout(() => playDummyMedia(viewTitle), 540000); // repeat every 9 minutes to keep it active (gets deactivated after 10 minutes)
|
||||
}
|
||||
};
|
||||
|
||||
const showLovelaceController = () => {
|
||||
mediaPlayer.style.display = "none";
|
||||
lovelaceController.style.display = "initial";
|
||||
@@ -51,6 +79,7 @@ const showMediaPlayer = () => {
|
||||
--progress-color: #03a9f4;
|
||||
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
||||
--splash-size: cover;
|
||||
--background-color: #41bdf5;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
@@ -63,22 +92,6 @@ options.customNamespaces = {
|
||||
[CAST_NS]: cast.framework.system.MessageType.JSON,
|
||||
};
|
||||
|
||||
// The docs say we need to set options.touchScreenOptimizeApp = true
|
||||
// https://developers.google.com/cast/docs/caf_receiver/customize_ui#accessing_ui_controls
|
||||
// This doesn't work.
|
||||
// @ts-ignore
|
||||
options.touchScreenOptimizedApp = true;
|
||||
|
||||
// The class reference say we can set a uiConfig in options to set it
|
||||
// https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#uiConfig
|
||||
// This doesn't work either.
|
||||
// @ts-ignore
|
||||
options.uiConfig = new cast.framework.ui.UiConfig();
|
||||
// @ts-ignore
|
||||
options.uiConfig.touchScreenOptimizedApp = true;
|
||||
|
||||
castContext.setInactivityTimeout(86400); // 1 day
|
||||
|
||||
castContext.addCustomMessageListener(
|
||||
CAST_NS,
|
||||
// @ts-ignore
|
||||
@@ -103,6 +116,12 @@ const playerManager = castContext.getPlayerManager();
|
||||
playerManager.setMessageInterceptor(
|
||||
cast.framework.messages.MessageType.LOAD,
|
||||
(loadRequestData) => {
|
||||
if (
|
||||
loadRequestData.media.contentId ===
|
||||
"https://cast.home-assistant.io/images/google-nest-hub.png"
|
||||
) {
|
||||
return loadRequestData;
|
||||
}
|
||||
// We received a play media command, hide Lovelace and show media player
|
||||
showMediaPlayer();
|
||||
const media = loadRequestData.media;
|
||||
|
@@ -1,10 +1,5 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import {
|
||||
@@ -21,7 +16,7 @@ import "./hc-lovelace";
|
||||
class HcDemo extends HassElement {
|
||||
@property({ attribute: false }) public lovelacePath!: string;
|
||||
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._lovelaceConfig) {
|
||||
@@ -38,10 +33,10 @@ class HcDemo extends HassElement {
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._initialize();
|
||||
this._initializeHass();
|
||||
}
|
||||
|
||||
private async _initialize() {
|
||||
private async _initializeHass() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
|
@@ -1,12 +1,5 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
@customElement("hc-launch-screen")
|
||||
@@ -29,7 +22,7 @@ class HcLaunchScreen extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
|
@@ -1,12 +1,6 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
@@ -21,7 +15,7 @@ class HcLovelace extends LitElement {
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
|
||||
public urlPath?: string | null;
|
||||
@property() public urlPath: string | null = null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const index = this._viewIndex;
|
||||
@@ -37,7 +31,7 @@ class HcLovelace extends LitElement {
|
||||
config: this.lovelaceConfig,
|
||||
rawConfig: this.lovelaceConfig,
|
||||
editMode: false,
|
||||
urlPath: this.urlPath!,
|
||||
urlPath: this.urlPath,
|
||||
enableFullEditMode: () => undefined,
|
||||
mode: "storage",
|
||||
locale: this.hass.locale,
|
||||
@@ -61,6 +55,21 @@ class HcLovelace extends LitElement {
|
||||
const index = this._viewIndex;
|
||||
|
||||
if (index !== undefined) {
|
||||
const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
|
||||
|
||||
const viewTitle =
|
||||
this.lovelaceConfig.views[index].title ||
|
||||
this.lovelaceConfig.views[index].path;
|
||||
|
||||
fireEvent(this, "cast-view-changed", {
|
||||
title:
|
||||
dashboardTitle || viewTitle
|
||||
? `${dashboardTitle || ""}${
|
||||
dashboardTitle && viewTitle ? ": " : ""
|
||||
}${viewTitle || ""}`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
const configBackground =
|
||||
this.lovelaceConfig.views[index].background ||
|
||||
this.lovelaceConfig.background;
|
||||
@@ -91,7 +100,7 @@ class HcLovelace extends LitElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
min-height: 100vh;
|
||||
@@ -108,8 +117,15 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
export interface CastViewChanged {
|
||||
title: string | undefined;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hc-lovelace": HcLovelace;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"cast-view-changed": CastViewChanged;
|
||||
}
|
||||
}
|
||||
|
@@ -3,12 +3,8 @@ import {
|
||||
getAuth,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { CAST_NS } from "../../../../src/cast/const";
|
||||
import {
|
||||
ConnectMessage,
|
||||
@@ -17,7 +13,11 @@ import {
|
||||
ShowDemoMessage,
|
||||
ShowLovelaceViewMessage,
|
||||
} from "../../../../src/cast/receiver_messages";
|
||||
import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages";
|
||||
import {
|
||||
ReceiverErrorCode,
|
||||
ReceiverErrorMessage,
|
||||
ReceiverStatusMessage,
|
||||
} from "../../../../src/cast/sender_messages";
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import {
|
||||
@@ -36,18 +36,18 @@ let resourcesLoaded = false;
|
||||
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@internalProperty() private _showDemo = false;
|
||||
@state() private _showDemo = false;
|
||||
|
||||
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
|
||||
@state() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
@internalProperty() private _lovelacePath: string | number | null = null;
|
||||
@state() private _lovelacePath: string | number | null = null;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _urlPath?: string | null;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
|
||||
private _urlPath?: string | null;
|
||||
|
||||
public processIncomingMessage(msg: HassMessage) {
|
||||
if (msg.type === "connect") {
|
||||
this._handleConnectMessage(msg);
|
||||
@@ -72,8 +72,10 @@ export class HcMain extends HassElement {
|
||||
!this._lovelaceConfig ||
|
||||
this._lovelacePath === null ||
|
||||
// Guard against part of HA not being loaded yet.
|
||||
(this.hass &&
|
||||
(!this.hass.states || !this.hass.config || !this.hass.services))
|
||||
!this.hass ||
|
||||
!this.hass.states ||
|
||||
!this.hass.config ||
|
||||
!this.hass.services
|
||||
) {
|
||||
return html`
|
||||
<hc-launch-screen
|
||||
@@ -111,6 +113,7 @@ export class HcMain extends HassElement {
|
||||
this._sendStatus();
|
||||
}
|
||||
});
|
||||
this.addEventListener("dialog-closed", this._dialogClosed);
|
||||
}
|
||||
|
||||
private _sendStatus(senderId?: string) {
|
||||
@@ -122,7 +125,7 @@ export class HcMain extends HassElement {
|
||||
|
||||
if (this.hass) {
|
||||
status.hassUrl = this.hass.auth.data.hassUrl;
|
||||
status.lovelacePath = this._lovelacePath!;
|
||||
status.lovelacePath = this._lovelacePath;
|
||||
status.urlPath = this._urlPath;
|
||||
}
|
||||
|
||||
@@ -135,6 +138,30 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _sendError(
|
||||
error_code: number,
|
||||
error_message: string,
|
||||
senderId?: string
|
||||
) {
|
||||
const error: ReceiverErrorMessage = {
|
||||
type: "receiver_error",
|
||||
error_code,
|
||||
error_message,
|
||||
};
|
||||
|
||||
if (senderId) {
|
||||
this.sendMessage(senderId, error);
|
||||
} else {
|
||||
for (const sender of castContext.getSenders()) {
|
||||
this.sendMessage(sender.id, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _dialogClosed = () => {
|
||||
document.body.setAttribute("style", "overflow-y: auto !important");
|
||||
};
|
||||
|
||||
private async _handleGetStatusMessage(msg: GetStatusMessage) {
|
||||
this._sendStatus(msg.senderId!);
|
||||
}
|
||||
@@ -152,15 +179,19 @@ export class HcMain extends HassElement {
|
||||
expires_in: 0,
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
this._error = this._getErrorMessage(err);
|
||||
} catch (err: any) {
|
||||
const errorMessage = this._getErrorMessage(err);
|
||||
this._error = errorMessage;
|
||||
this._sendError(err, errorMessage);
|
||||
return;
|
||||
}
|
||||
let connection;
|
||||
try {
|
||||
connection = await createConnection({ auth });
|
||||
} catch (err) {
|
||||
this._error = this._getErrorMessage(err);
|
||||
} catch (err: any) {
|
||||
const errorMessage = this._getErrorMessage(err);
|
||||
this._error = errorMessage;
|
||||
this._sendError(err, errorMessage);
|
||||
return;
|
||||
}
|
||||
if (this.hass) {
|
||||
@@ -172,24 +203,29 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) {
|
||||
this._showDemo = false;
|
||||
// We should not get this command before we are connected.
|
||||
// Means a client got out of sync. Let's send status to them.
|
||||
if (!this.hass) {
|
||||
this._sendStatus(msg.senderId!);
|
||||
this._error = "Cannot show Lovelace because we're not connected.";
|
||||
this._sendError(ReceiverErrorCode.NOT_CONNECTED, this._error);
|
||||
return;
|
||||
}
|
||||
this._error = undefined;
|
||||
if (msg.urlPath === "lovelace") {
|
||||
msg.urlPath = null;
|
||||
}
|
||||
this._lovelacePath = msg.viewPath;
|
||||
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
|
||||
this._urlPath = msg.urlPath;
|
||||
this._lovelaceConfig = undefined;
|
||||
if (this._unsubLovelace) {
|
||||
this._unsubLovelace();
|
||||
}
|
||||
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
|
||||
? getLovelaceCollection(this.hass!.connection, msg.urlPath)
|
||||
: getLegacyLovelaceCollection(this.hass!.connection);
|
||||
? getLovelaceCollection(this.hass.connection, msg.urlPath)
|
||||
: getLegacyLovelaceCollection(this.hass.connection);
|
||||
// We first do a single refresh because we need to check if there is LL
|
||||
// configuration.
|
||||
try {
|
||||
@@ -197,9 +233,17 @@ export class HcMain extends HassElement {
|
||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||
);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error fetching Lovelace configuration", err, msg);
|
||||
} catch (err: any) {
|
||||
if (
|
||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
||||
err.code !== "config_not_found"
|
||||
) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error fetching Lovelace configuration", err, msg);
|
||||
this._error = `Error fetching Lovelace configuration: ${err.message}`;
|
||||
this._sendError(ReceiverErrorCode.FETCH_CONFIG_FAILED, this._error);
|
||||
return;
|
||||
}
|
||||
// Generate a Lovelace config.
|
||||
this._unsubLovelace = () => undefined;
|
||||
await this._generateLovelaceConfig();
|
||||
@@ -214,8 +258,6 @@ export class HcMain extends HassElement {
|
||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
||||
}
|
||||
}
|
||||
this._showDemo = false;
|
||||
this._lovelacePath = msg.viewPath;
|
||||
|
||||
this._sendStatus();
|
||||
}
|
||||
@@ -236,7 +278,7 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
|
||||
castContext.setApplicationState(lovelaceConfig.title!);
|
||||
castContext.setApplicationState(lovelaceConfig.title || "");
|
||||
this._lovelaceConfig = lovelaceConfig;
|
||||
}
|
||||
|
||||
|
@@ -246,11 +246,15 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
|
||||
"light.living_room_lights": {
|
||||
entity_id: "light.living_room_lights",
|
||||
state: "off",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 175,
|
||||
color_temp: 300,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
friendly_name: "Living Room Lights",
|
||||
color_mode: "color_temp",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
@@ -263,13 +267,27 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
"light.kitchen_lights": {
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
brightness: 200,
|
||||
rgb_color: [255, 175, 96],
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "rgb",
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Garage Lights",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
|
||||
"sensor.plexspy": {
|
||||
entity_id: "sensor.plexspy",
|
||||
state: "0",
|
||||
@@ -482,16 +500,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
icon: "hademo:history",
|
||||
},
|
||||
},
|
||||
"light.lifx5": {
|
||||
entity_id: "light.lifx5",
|
||||
state: "on",
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
friendly_name: "Garage Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
"sensor.alok_to_home": {
|
||||
entity_id: "sensor.alok_to_home",
|
||||
state: "41",
|
||||
|
@@ -29,6 +29,11 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Energy distribution today",
|
||||
type: "energy-distribution",
|
||||
link_dashboard: true,
|
||||
},
|
||||
{
|
||||
type: "thermostat",
|
||||
entity: "climate.upstairs",
|
||||
@@ -113,8 +118,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -196,8 +200,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -277,8 +280,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -315,8 +317,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { energyEntities } from "../stubs/entities";
|
||||
import { DemoConfig } from "./types";
|
||||
|
||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
@@ -12,9 +13,8 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
||||
selectedDemoConfigIndex
|
||||
]();
|
||||
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||
demoConfigs[selectedDemoConfigIndex]();
|
||||
|
||||
export const setDemoConfig = async (
|
||||
hass: MockHomeAssistant,
|
||||
@@ -28,6 +28,7 @@ export const setDemoConfig = async (
|
||||
selectedDemoConfig = confProm;
|
||||
|
||||
hass.addEntities(config.entities(hass.localize), true);
|
||||
hass.addEntities(energyEntities());
|
||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
hass.mockTheme(config.theme());
|
||||
};
|
||||
|
@@ -980,8 +980,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-off",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1005,8 +1004,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-multiple-minus",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1114,6 +1112,9 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
brightness: 63,
|
||||
color_temp: 200,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
friendly_name: "Upstairs lights",
|
||||
supported_features: 63,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1125,6 +1126,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Walk in closet lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1136,6 +1138,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
brightness: 254,
|
||||
friendly_name: "Outdoor lights",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:wall-sconce",
|
||||
},
|
||||
@@ -1148,6 +1152,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
friendly_name: "Downstairs lights",
|
||||
supported_features: 63,
|
||||
@@ -1307,6 +1313,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Bedside Lamp",
|
||||
supported_features: 63,
|
||||
@@ -1320,6 +1327,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
min_mireds: 153,
|
||||
max_mireds: 500,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp Reading Light",
|
||||
supported_features: 43,
|
||||
@@ -1335,6 +1343,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 128,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp", "rgb"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Hallway window light",
|
||||
@@ -1349,6 +1359,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
brightness: 77,
|
||||
is_deconz_group: false,
|
||||
supported_color_modes: ["brightness"],
|
||||
friendly_name: "Isa Ceiling Light",
|
||||
supported_features: 41,
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
@@ -1363,6 +1374,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
max_mireds: 500,
|
||||
brightness: 150,
|
||||
color_temp: 366,
|
||||
supported_color_modes: ["brightness", "color_temp"],
|
||||
color_mode: "color_temp",
|
||||
effect_list: ["colorloop"],
|
||||
is_deconz_group: false,
|
||||
friendly_name: "Floorlamp",
|
||||
@@ -1377,6 +1390,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Bedroom Ceiling Light",
|
||||
supported_features: 41,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1387,6 +1401,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
attributes: {
|
||||
friendly_name: "Nightlight",
|
||||
supported_features: 17,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:lamp",
|
||||
},
|
||||
@@ -1753,6 +1768,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.2,
|
||||
friendly_name: "Upstairs Hallway Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1768,6 +1784,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Dining Room Light",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:ceiling-light",
|
||||
},
|
||||
@@ -1783,6 +1800,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 0,
|
||||
friendly_name: "Living room Spotlights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1799,6 +1817,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 2.5,
|
||||
friendly_name: "Passage Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
@@ -1843,6 +1862,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
power_consumption: 37.4,
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 33,
|
||||
supported_color_modes: ["brightness"],
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
icon: "mdi:track-light",
|
||||
},
|
||||
|
@@ -440,57 +440,43 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
type: "horizontal-stack",
|
||||
},
|
||||
{
|
||||
type: "grid",
|
||||
columns: 2,
|
||||
cards: [
|
||||
{
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_bedroom",
|
||||
},
|
||||
{
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "S's room",
|
||||
entity: "sensor.temperature_stefan",
|
||||
},
|
||||
{
|
||||
cards: [
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
},
|
||||
],
|
||||
type: "horizontal-stack",
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_passage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Bathroom",
|
||||
entity: "sensor.temperature_downstairs_bathroom",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
entity: "sensor.temperature_storage",
|
||||
},
|
||||
{
|
||||
graph: "line",
|
||||
type: "sensor",
|
||||
name: "Refrigerator",
|
||||
entity: "sensor.refrigerator",
|
||||
},
|
||||
],
|
||||
type: "vertical-stack",
|
||||
},
|
||||
{
|
||||
entities: [
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { LitElement } from "lit-element";
|
||||
import { LitElement } from "lit";
|
||||
import "./card-tools";
|
||||
|
||||
class CardModder extends LitElement {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
import { html, LitElement } from "lit-element";
|
||||
import { html, LitElement } from "lit";
|
||||
|
||||
if (!window.cardTools) {
|
||||
const version = 0.2;
|
||||
|
@@ -1,12 +1,6 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mdiTelevision } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../src/cast/receiver_messages";
|
||||
import "../../../src/components/ha-icon";
|
||||
@@ -20,7 +14,7 @@ import { HomeAssistant } from "../../../src/types";
|
||||
class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _castManager?: CastManager | null;
|
||||
@state() private _castManager?: CastManager | null;
|
||||
|
||||
public setConfig(_config: CastConfig): void {
|
||||
// No config possible.
|
||||
@@ -34,7 +28,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-icon icon="hademo:television"></ha-icon>
|
||||
<ha-svg-icon .path=${mdiTelevision}></ha-svg-icon>
|
||||
<div class="flex">
|
||||
<div class="name">Show Chromecast interface</div>
|
||||
<google-cast-launcher></google-cast-launcher>
|
||||
@@ -73,13 +67,13 @@ class CastDemoRow extends LitElement implements LovelaceRow {
|
||||
this.style.display = this._castManager ? "" : "none";
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
ha-icon {
|
||||
ha-svg-icon {
|
||||
padding: 8px;
|
||||
color: var(--paper-item-icon-color);
|
||||
}
|
||||
|
@@ -1,14 +1,7 @@
|
||||
import "@material/mwc-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||
@@ -26,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||
|
||||
@internalProperty() private _switching?: boolean;
|
||||
@state() private _switching = false;
|
||||
|
||||
private _hidden = localStorage.hide_demo_card;
|
||||
|
||||
@@ -34,12 +27,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
return this._hidden ? 0 : 2;
|
||||
}
|
||||
|
||||
public setConfig(
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
config: LovelaceCardConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
) {}
|
||||
public setConfig(_config: LovelaceCardConfig) {}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._hidden) {
|
||||
@@ -56,7 +44,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
(conf) => html`
|
||||
${conf.name}
|
||||
<small>
|
||||
<a target="_blank" href="${conf.authorUrl}">
|
||||
<a target="_blank" href=${conf.authorUrl}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.page-demo.cards.demo.demo_by",
|
||||
"name",
|
||||
@@ -106,14 +94,14 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
this._switching = true;
|
||||
try {
|
||||
await setDemoConfig(this.hass, this.lovelace!, index);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
alert("Failed to switch config :-(");
|
||||
} finally {
|
||||
this._switching = false;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
a {
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import "@polymer/polymer/lib/elements/dom-if";
|
||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
|
@@ -20,11 +20,14 @@ import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
import { mockEnergy } from "./stubs/energy";
|
||||
import { mockConfig } from "./stubs/config";
|
||||
import { energyEntities } from "./stubs/entities";
|
||||
|
||||
class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initialize() {
|
||||
protected async _initializeHass() {
|
||||
const initial: Partial<MockHomeAssistant> = {
|
||||
panelUrl: (this as any).panelUrl,
|
||||
panelUrl: (this as any)._panelUrl,
|
||||
// Override updateHass so that the correct hass lifecycle methods are called
|
||||
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
|
||||
this._updateHass(hassUpdate),
|
||||
@@ -47,8 +50,12 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
mockEvents(hass);
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockEnergy(hass);
|
||||
mockConfig(hass);
|
||||
mockPersistentNotification(hass);
|
||||
|
||||
hass.addEntities(energyEntities());
|
||||
|
||||
// Once config is loaded AND localize, set entities and apply theme.
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([conf, localize]) => {
|
||||
@@ -70,7 +77,7 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
navigate(this, href);
|
||||
navigate(href);
|
||||
},
|
||||
{ capture: true }
|
||||
);
|
||||
|
File diff suppressed because one or more lines are too long
7
demo/src/stubs/area_registry.ts
Normal file
7
demo/src/stubs/area_registry.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { AreaRegistryEntry } from "../../../src/data/area_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockAreaRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: AreaRegistryEntry[] = []
|
||||
) => hass.mockWS("config/area_registry/list", () => data);
|
41
demo/src/stubs/config.ts
Normal file
41
demo/src/stubs/config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockConfig = (hass: MockHomeAssistant) => {
|
||||
hass.mockAPI("config/config_entries/entry", () => [
|
||||
{
|
||||
entry_id: "co2signal",
|
||||
domain: "co2signal",
|
||||
title: "CO2 Signal",
|
||||
source: "user",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
disabled_by: null,
|
||||
reason: null,
|
||||
},
|
||||
]);
|
||||
hass.mockWS("config/entity_registry/list", () => [
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.co2_intensity",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
]);
|
||||
};
|
7
demo/src/stubs/device_registry.ts
Normal file
7
demo/src/stubs/device_registry.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockDeviceRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: DeviceRegistryEntry[] = []
|
||||
) => hass.mockWS("config/device_registry/list", () => data);
|
137
demo/src/stubs/energy.ts
Normal file
137
demo/src/stubs/energy.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||
import { EnergySolarForecasts } from "../../../src/data/energy";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS("energy/get_prefs", () => ({
|
||||
energy_sources: [
|
||||
{
|
||||
type: "grid",
|
||||
flow_from: [
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
flow_to: [
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
cost_adjustment_day: 0,
|
||||
},
|
||||
{
|
||||
type: "solar",
|
||||
stat_energy_from: "sensor.solar_production",
|
||||
config_entry_solar_forecast: ["solar_forecast"],
|
||||
},
|
||||
/* {
|
||||
type: "battery",
|
||||
stat_energy_from: "sensor.battery_output",
|
||||
stat_energy_to: "sensor.battery_input",
|
||||
}, */
|
||||
{
|
||||
type: "gas",
|
||||
stat_energy_from: "sensor.energy_gas",
|
||||
stat_cost: "sensor.energy_gas_cost",
|
||||
entity_energy_from: "sensor.energy_gas",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
device_consumption: [
|
||||
{
|
||||
stat_consumption: "sensor.energy_car",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_ac",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_washing_machine",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_dryer",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_heat_pump",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_boiler",
|
||||
},
|
||||
],
|
||||
}));
|
||||
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
||||
hass.mockWS("energy/fossil_energy_consumption", ({ period }) => ({
|
||||
start: period === "month" ? 250 : period === "day" ? 10 : 2,
|
||||
}));
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"energy/solar_forecast",
|
||||
(): EnergySolarForecasts => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
178
demo/src/stubs/entities.ts
Normal file
178
demo/src/stubs/entities.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
"sensor.solar_production": {
|
||||
entity_id: "sensor.solar_production",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Solar",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_input": {
|
||||
entity_id: "sensor.battery_input",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Input",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_output": {
|
||||
entity_id: "sensor.battery_output",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Output",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1": {
|
||||
entity_id: "sensor.energy_production_tarif_1",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2": {
|
||||
entity_id: "sensor.energy_production_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_1_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_2_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas_cost": {
|
||||
entity_id: "sensor.energy_gas_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas": {
|
||||
entity_id: "sensor.energy_gas",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Gas",
|
||||
unit_of_measurement: "m³",
|
||||
},
|
||||
},
|
||||
"sensor.energy_car": {
|
||||
entity_id: "sensor.energy_car",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Electric car",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_ac": {
|
||||
entity_id: "sensor.energy_ac",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Air conditioning",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_washing_machine": {
|
||||
entity_id: "sensor.energy_washing_machine",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Washing machine",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_dryer": {
|
||||
entity_id: "sensor.energy_dryer",
|
||||
state: "5.5",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Dryer",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_heat_pump": {
|
||||
entity_id: "sensor.energy_heat_pump",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Heat pump",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_boiler": {
|
||||
entity_id: "sensor.energy_boiler",
|
||||
state: "7",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Boiler",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
});
|
7
demo/src/stubs/entity_registry.ts
Normal file
7
demo/src/stubs/entity_registry.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEntityRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: EntityRegistryEntry[] = []
|
||||
) => hass.mockWS("config/entity_registry/list", () => data);
|
59
demo/src/stubs/hassio_supervisor.ts
Normal file
59
demo/src/stubs/hassio_supervisor.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockHassioSupervisor = (hass: MockHomeAssistant) => {
|
||||
hass.config.components.push("hassio");
|
||||
hass.mockWS("supervisor/api", (msg) => {
|
||||
if (msg.endpoint === "/supervisor/info") {
|
||||
const data: HassioSupervisorInfo = {
|
||||
version: "2021.10.dev0805",
|
||||
version_latest: "2021.10.dev0806",
|
||||
update_available: true,
|
||||
channel: "dev",
|
||||
arch: "aarch64",
|
||||
supported: true,
|
||||
healthy: true,
|
||||
ip_address: "172.30.32.2",
|
||||
wait_boot: 5,
|
||||
timezone: "America/Los_Angeles",
|
||||
logging: "info",
|
||||
debug: false,
|
||||
debug_block: false,
|
||||
diagnostics: true,
|
||||
addons: [
|
||||
{
|
||||
name: "Visual Studio Code",
|
||||
slug: "a0d7b954_vscode",
|
||||
description:
|
||||
"Fully featured VSCode experience, to edit your HA config in the browser, including auto-completion!",
|
||||
state: "started",
|
||||
version: "3.6.2",
|
||||
version_latest: "3.6.2",
|
||||
update_available: false,
|
||||
repository: "a0d7b954",
|
||||
icon: true,
|
||||
logo: true,
|
||||
},
|
||||
{
|
||||
name: "Z-Wave JS",
|
||||
slug: "core_zwave_js",
|
||||
description:
|
||||
"Control a ZWave network with Home Assistant Z-Wave JS",
|
||||
state: "started",
|
||||
version: "0.1.45",
|
||||
version_latest: "0.1.45",
|
||||
update_available: false,
|
||||
repository: "core",
|
||||
icon: true,
|
||||
logo: true,
|
||||
},
|
||||
] as any,
|
||||
addons_repositories: [
|
||||
"https://github.com/hassio-addons/repository",
|
||||
] as any,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
return Promise.reject(`${msg.method} ${msg.endpoint} is not implemented`);
|
||||
});
|
||||
};
|
@@ -1,4 +1,12 @@
|
||||
import {
|
||||
addDays,
|
||||
addHours,
|
||||
addMonths,
|
||||
differenceInHours,
|
||||
endOfDay,
|
||||
} from "date-fns";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { StatisticValue } from "../../../src/data/history";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
interface HistoryQueryParams {
|
||||
@@ -64,17 +72,335 @@ const generateHistory = (state, deltas) => {
|
||||
|
||||
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) => {
|
||||
mockHass.mockAPI(
|
||||
new RegExp("history/period/.+"),
|
||||
(
|
||||
hass,
|
||||
// @ts-ignore
|
||||
method,
|
||||
path,
|
||||
// @ts-ignore
|
||||
parameters
|
||||
) => {
|
||||
(hass, _method, path, _parameters) => {
|
||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||
const entities = params.filter_entity_id.split(",");
|
||||
|
||||
@@ -95,7 +421,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
const numberState = Number(state.state);
|
||||
|
||||
if (isNaN(numberState)) {
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
"Ignoring state with unparsable state but with a unit",
|
||||
entityId,
|
||||
@@ -140,4 +466,42 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
return results;
|
||||
}
|
||||
);
|
||||
mockHass.mockWS("history/list_statistic_ids", () => []);
|
||||
mockHass.mockWS(
|
||||
"history/statistics_during_period",
|
||||
({ statistic_ids, start_time, end_time, 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;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@@ -10,10 +10,9 @@ export const mockLovelace = (
|
||||
localizePromise: Promise<LocalizeFunc>
|
||||
) => {
|
||||
hass.mockWS("lovelace/config", () =>
|
||||
Promise.all([
|
||||
selectedDemoConfig,
|
||||
localizePromise,
|
||||
]).then(([config, localize]) => config.lovelace(localize))
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([config, localize]) => config.lovelace(localize)
|
||||
)
|
||||
);
|
||||
|
||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||
@@ -24,9 +23,9 @@ customElements.whenDefined("hui-view").then(() => {
|
||||
// eslint-disable-next-line
|
||||
const HUIView = customElements.get("hui-view");
|
||||
// Patch HUI-VIEW to make the lovelace object available to the demo card
|
||||
const oldCreateCard = HUIView.prototype.createCardElement;
|
||||
const oldCreateCard = HUIView!.prototype.createCardElement;
|
||||
|
||||
HUIView.prototype.createCardElement = function (config) {
|
||||
HUIView!.prototype.createCardElement = function (config) {
|
||||
const el = oldCreateCard.call(this, config);
|
||||
if (el.tagName === "HA-DEMO-CARD") {
|
||||
(el as HADemoCard).lovelace = this.lovelace;
|
||||
|
@@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
|
||||
body: { message: "Template dev tool does not work in the demo." },
|
||||
})
|
||||
);
|
||||
hass.mockWS("render_template", (msg, onChange) => {
|
||||
hass.mockWS("render_template", (msg, _hass, onChange) => {
|
||||
onChange!({
|
||||
result: msg.template,
|
||||
listeners: { all: false, domains: [], entities: [], time: false },
|
||||
|
@@ -3,8 +3,6 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
export const mockTranslations = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS(
|
||||
"frontend/get_translations",
|
||||
(/* msg: {language: string, category: string} */) => {
|
||||
return { resources: {} };
|
||||
}
|
||||
(/* msg: {language: string, category: string} */) => ({ resources: {} })
|
||||
);
|
||||
};
|
||||
|
BIN
gallery/public/images/office.jpg
Normal file
BIN
gallery/public/images/office.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
35
gallery/script/netlify_build_gallery
Executable file
35
gallery/script/netlify_build_gallery
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
TARGET_LABEL="Needs design preview"
|
||||
|
||||
if [[ "$NETLIFY" != "true" ]]; then
|
||||
echo "This script can only be run on Netlify"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function createStatus() {
|
||||
state="$1"
|
||||
description="$2"
|
||||
target_url="$3"
|
||||
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/home-assistant/frontend/statuses/$COMMIT_REF" \
|
||||
-d '{"state": "'"${state}"'", "context": "Netlify/Design Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}'
|
||||
}
|
||||
|
||||
|
||||
if [[ "${PULL_REQUEST}" == "true" ]]; then
|
||||
if [[ "$(curl -sSLf -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/home-assistant/frontend/pulls/${REVIEW_ID}" | jq '.labels[].name' -r)" =~ "$TARGET_LABEL" ]]; then
|
||||
createStatus "pending" "Building design preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
|
||||
gulp build-gallery
|
||||
if [ $? -eq 0 ]; then
|
||||
createStatus "success" "Build complete" "$DEPLOY_URL"
|
||||
else
|
||||
createStatus "error" "Build failed" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
|
||||
fi
|
||||
else
|
||||
createStatus "success" "Build was not requested by PR label"
|
||||
fi
|
||||
elif [[ "$INCOMING_HOOK_BODY" == "NIGHTLY" ]]; then
|
||||
gulp build-gallery
|
||||
fi
|
47
gallery/sidebar.js
Normal file
47
gallery/sidebar.js
Normal file
@@ -0,0 +1,47 @@
|
||||
module.exports = [
|
||||
{
|
||||
// This section has no header and so all page links are shown directly in the sidebar
|
||||
category: "concepts",
|
||||
pages: ["home"],
|
||||
},
|
||||
|
||||
{
|
||||
category: "lovelace",
|
||||
// Label for in the sidebar
|
||||
header: "Lovelace",
|
||||
// Specify order of pages. Any pages in the category folder but not listed here will
|
||||
// automatically be added after the pages listed here.
|
||||
pages: ["introduction"],
|
||||
},
|
||||
{
|
||||
category: "automation",
|
||||
header: "Automation",
|
||||
pages: [
|
||||
"editor-trigger",
|
||||
"editor-condition",
|
||||
"editor-action",
|
||||
"trace",
|
||||
"trace-timeline",
|
||||
],
|
||||
},
|
||||
{
|
||||
category: "components",
|
||||
header: "Components",
|
||||
},
|
||||
{
|
||||
category: "more-info",
|
||||
header: "More Info dialogs",
|
||||
},
|
||||
{
|
||||
category: "misc",
|
||||
header: "Miscelaneous",
|
||||
},
|
||||
{
|
||||
category: "user-test",
|
||||
header: "User Tests",
|
||||
},
|
||||
{
|
||||
category: "design.home-assistant.io",
|
||||
header: "Design Documentation",
|
||||
},
|
||||
];
|
139
gallery/src/components/demo-black-white-row.ts
Normal file
139
gallery/src/components/demo-black-white-row.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { Button } from "@material/mwc-button";
|
||||
import { html, LitElement, css, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
|
||||
@customElement("demo-black-white-row")
|
||||
class DemoBlackWhiteRow extends LitElement {
|
||||
@property() title!: string;
|
||||
|
||||
@property() value!: any;
|
||||
|
||||
@property() disabled = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="row">
|
||||
<div class="content light">
|
||||
<ha-card .header=${this.title}>
|
||||
<div class="card-content">
|
||||
<slot name="light"></slot>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
.disabled=${this.disabled}
|
||||
@click=${this.handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
<div class="content dark">
|
||||
<ha-card .header=${this.title}>
|
||||
<div class="card-content">
|
||||
<slot name="dark"></slot>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
.disabled=${this.disabled}
|
||||
@click=${this.handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), {
|
||||
default_theme: "default",
|
||||
default_dark_theme: "default",
|
||||
themes: {},
|
||||
darkMode: true,
|
||||
theme: "default",
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit(ev) {
|
||||
const content = (ev.target as Button).closest(".content")!;
|
||||
fireEvent(this, "submitted" as any, {
|
||||
slot: content.classList.contains("light") ? "light" : "dark",
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
.content {
|
||||
padding: 50px 0;
|
||||
background-color: var(--primary-background-color);
|
||||
}
|
||||
.light {
|
||||
flex: 1;
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.light ha-card {
|
||||
margin-left: auto;
|
||||
}
|
||||
.dark {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding-left: 50px;
|
||||
box-sizing: border-box;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
ha-card {
|
||||
width: 400px;
|
||||
}
|
||||
pre {
|
||||
width: 300px;
|
||||
margin: 0 16px 0;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.card-actions {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
border-top: none;
|
||||
}
|
||||
@media only screen and (max-width: 1500px) {
|
||||
.light {
|
||||
flex: initial;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.light,
|
||||
.dark {
|
||||
padding: 16px;
|
||||
}
|
||||
.row,
|
||||
.dark {
|
||||
flex-direction: column;
|
||||
}
|
||||
ha-card {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
pre {
|
||||
margin: 16px auto;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-black-white-row": DemoBlackWhiteRow;
|
||||
}
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { safeLoad } from "js-yaml";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
|
||||
|
||||
class DemoCard extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
.root {
|
||||
display: flex;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 20px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
}
|
||||
pre {
|
||||
width: 400px;
|
||||
margin: 0 16px;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.root {
|
||||
flex-direction: column;
|
||||
}
|
||||
pre {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h2>[[config.heading]]</h2>
|
||||
<div class="root">
|
||||
<div id="card"></div>
|
||||
<template is="dom-if" if="[[showConfig]]">
|
||||
<pre>[[_trim(config.config)]]</pre>
|
||||
</template>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
observer: "_hassChanged",
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
observer: "_configChanged",
|
||||
},
|
||||
showConfig: Boolean,
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
}
|
||||
|
||||
_configChanged(config) {
|
||||
const card = this.$.card;
|
||||
while (card.lastChild) {
|
||||
card.removeChild(card.lastChild);
|
||||
}
|
||||
|
||||
const el = this._createCardElement(safeLoad(config.config)[0]);
|
||||
card.appendChild(el);
|
||||
}
|
||||
|
||||
_createCardElement(cardConfig) {
|
||||
const element = createCardElement(cardConfig);
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
}
|
||||
element.addEventListener(
|
||||
"ll-rebuild",
|
||||
(ev) => {
|
||||
ev.stopPropagation();
|
||||
this._rebuildCard(element, cardConfig);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
return element;
|
||||
}
|
||||
|
||||
_rebuildCard(cardElToReplace, config) {
|
||||
const newCardEl = this._createCardElement(config);
|
||||
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
|
||||
}
|
||||
|
||||
_hassChanged(hass) {
|
||||
const card = this.$.card.lastChild;
|
||||
if (card) card.hass = hass;
|
||||
}
|
||||
|
||||
_trim(config) {
|
||||
return config.trim();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-card", DemoCard);
|
129
gallery/src/components/demo-card.ts
Normal file
129
gallery/src/components/demo-card.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { load } from "js-yaml";
|
||||
import { html, css, LitElement, PropertyValues } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
|
||||
export interface DemoCardConfig {
|
||||
heading: string;
|
||||
config: string;
|
||||
}
|
||||
|
||||
@customElement("demo-card")
|
||||
class DemoCard extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public config!: DemoCardConfig;
|
||||
|
||||
@property() public showConfig = false;
|
||||
|
||||
@state() private _size?: number;
|
||||
|
||||
@query("#card") private _card!: HTMLElement;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h2>
|
||||
${this.config.heading}
|
||||
${this._size !== undefined
|
||||
? html`<small>(size ${this._size})</small>`
|
||||
: ""}
|
||||
</h2>
|
||||
<div class="root">
|
||||
<div id="card"></div>
|
||||
${this.showConfig ? html`<pre>${this.config.config.trim()}</pre>` : ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("config")) {
|
||||
const card = this._card;
|
||||
while (card.lastChild) {
|
||||
card.removeChild(card.lastChild);
|
||||
}
|
||||
|
||||
const el = this._createCardElement((load(this.config.config) as any)[0]);
|
||||
card.appendChild(el);
|
||||
this._getSize(el);
|
||||
}
|
||||
|
||||
if (changedProps.has("hass")) {
|
||||
const card = this._card.lastChild;
|
||||
if (card) {
|
||||
(card as any).hass = this.hass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _getSize(el) {
|
||||
await customElements.whenDefined(el.localName);
|
||||
|
||||
if (!("getCardSize" in el)) {
|
||||
this._size = undefined;
|
||||
return;
|
||||
}
|
||||
this._size = await el.getCardSize();
|
||||
}
|
||||
|
||||
_createCardElement(cardConfig) {
|
||||
const element = createCardElement(cardConfig);
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
}
|
||||
element.addEventListener(
|
||||
"ll-rebuild",
|
||||
(ev) => {
|
||||
ev.stopPropagation();
|
||||
this._rebuildCard(element, cardConfig);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
return element;
|
||||
}
|
||||
|
||||
_rebuildCard(cardElToReplace, config) {
|
||||
const newCardEl = this._createCardElement(config);
|
||||
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.root {
|
||||
display: flex;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 20px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
h2 small {
|
||||
font-size: 0.5em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
}
|
||||
pre {
|
||||
width: 400px;
|
||||
margin: 0 16px;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.root {
|
||||
flex-direction: column;
|
||||
}
|
||||
pre {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-card": DemoCard;
|
||||
}
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
import "./demo-card";
|
||||
|
||||
class DemoCards extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
#container {
|
||||
min-height: calc(100vh - 128px);
|
||||
background: var(--primary-background-color);
|
||||
}
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
demo-card {
|
||||
margin: 16px 16px 32px;
|
||||
}
|
||||
app-toolbar {
|
||||
background-color: var(--light-primary-color);
|
||||
}
|
||||
.filters {
|
||||
margin-left: 60px;
|
||||
}
|
||||
ha-formfield {
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
<app-toolbar>
|
||||
<div class="filters">
|
||||
<ha-formfield label="Show config">
|
||||
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Dark theme">
|
||||
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</app-toolbar>
|
||||
<div id="container">
|
||||
<div class="cards">
|
||||
<template is="dom-repeat" items="[[configs]]">
|
||||
<demo-card
|
||||
config="[[item]]"
|
||||
show-config="[[_showConfig]]"
|
||||
hass="[[hass]]"
|
||||
></demo-card>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
configs: Object,
|
||||
hass: Object,
|
||||
_showConfig: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_showConfigToggled(ev) {
|
||||
this._showConfig = ev.target.checked;
|
||||
}
|
||||
|
||||
_darkThemeToggled(ev) {
|
||||
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
|
||||
dark: ev.target.checked,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-cards", DemoCards);
|
88
gallery/src/components/demo-cards.ts
Normal file
88
gallery/src/components/demo-cards.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import "./demo-card";
|
||||
import type { DemoCardConfig } from "./demo-card";
|
||||
|
||||
@customElement("demo-cards")
|
||||
class DemoCards extends LitElement {
|
||||
@property() public configs!: DemoCardConfig[];
|
||||
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@state() private _showConfig = false;
|
||||
|
||||
@query("#container") private _container!: HTMLElement;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<app-toolbar>
|
||||
<div class="filters">
|
||||
<ha-formfield label="Show config">
|
||||
<ha-switch
|
||||
.checked=${this._showConfig}
|
||||
@change=${this._showConfigToggled}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Dark theme">
|
||||
<ha-switch @change=${this._darkThemeToggled}> </ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</app-toolbar>
|
||||
<div id="container">
|
||||
<div class="cards">
|
||||
${this.configs.map(
|
||||
(config) => html`
|
||||
<demo-card
|
||||
.config=${config}
|
||||
.showConfig=${this._showConfig}
|
||||
.hass=${this.hass}
|
||||
></demo-card>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
_showConfigToggled(ev) {
|
||||
this._showConfig = ev.target.checked;
|
||||
}
|
||||
|
||||
_darkThemeToggled(ev) {
|
||||
applyThemesOnElement(this._container, { themes: {} } as any, "default", {
|
||||
dark: ev.target.checked,
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
demo-card {
|
||||
margin: 16px 16px 32px;
|
||||
}
|
||||
app-toolbar {
|
||||
background-color: var(--light-primary-color);
|
||||
}
|
||||
.filters {
|
||||
margin-left: 60px;
|
||||
}
|
||||
ha-formfield {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-cards": DemoCards;
|
||||
}
|
||||
}
|
46
gallery/src/components/page-description.ts
Normal file
46
gallery/src/components/page-description.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import { HaMarkdown } from "../../../src/components/ha-markdown";
|
||||
import { PAGES } from "../../build/import-pages";
|
||||
|
||||
@customElement("page-description")
|
||||
class PageDescription extends HaMarkdown {
|
||||
@property() public page!: string;
|
||||
|
||||
render() {
|
||||
if (!PAGES[this.page].description) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
${until(
|
||||
PAGES[this.page]
|
||||
.description()
|
||||
.then((content) => html`<div class="root">${content}</div>`),
|
||||
""
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
HaMarkdown.styles,
|
||||
css`
|
||||
.root {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.root > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.root > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"page-description": PageDescription;
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import {
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_FLASH,
|
||||
SUPPORT_TRANSITION,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../src/data/light";
|
||||
import "../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Basic Light",
|
||||
}),
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_features: SUPPORT_BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
brightness: 128,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_effectslight", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
white_value: 36,
|
||||
supported_features:
|
||||
SUPPORT_BRIGHTNESS +
|
||||
SUPPORT_EFFECT +
|
||||
SUPPORT_FLASH +
|
||||
SUPPORT_COLOR +
|
||||
SUPPORT_TRANSITION +
|
||||
SUPPORT_WHITE_VALUE,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-light")
|
||||
class DemoMoreInfoLight 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-light": DemoMoreInfoLight;
|
||||
}
|
||||
}
|
@@ -1,221 +0,0 @@
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "../../src/components/ha-card";
|
||||
import "../../src/components/ha-icon";
|
||||
import "../../src/components/ha-icon-button";
|
||||
import "../../src/managers/notification-manager";
|
||||
import "../../src/styles/polymer-ha-style";
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { DEMOS } from "../build/import-demos";
|
||||
|
||||
class HaGallery extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-positioning ha-style">
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
app-header-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
ha-icon-button.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.pickers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.pickers ha-card {
|
||||
width: 400px;
|
||||
display: block;
|
||||
margin: 16px 8px;
|
||||
}
|
||||
|
||||
.pickers ha-card:last-child {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin: -1em 0;
|
||||
}
|
||||
|
||||
p a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-icon-button
|
||||
icon="hass:arrow-left"
|
||||
on-click="_backTapped"
|
||||
class$="[[_computeHeaderButtonClass(_demo)]]"
|
||||
></ha-icon-button>
|
||||
<div main-title>
|
||||
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
||||
</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class="content">
|
||||
<div id="demo"></div>
|
||||
<template is="dom-if" if="[[!_demo]]">
|
||||
<div class="pickers">
|
||||
<ha-card header="Lovelace Card Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
Lovelace has many different cards. Each card allows the user
|
||||
to tell a different story about what is going on in their
|
||||
house. These cards are very customizable, as no household is
|
||||
the same.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This gallery helps our developers and designers to see all
|
||||
the different states that each card can be in.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Check
|
||||
<a href="https://www.home-assistant.io/lovelace"
|
||||
>the official website</a
|
||||
>
|
||||
for instructions on how to get started with Lovelace.
|
||||
</p>
|
||||
</div>
|
||||
<template is="dom-repeat" items="[[_lovelaceDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
</paper-item>
|
||||
</a>
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Other Demos">
|
||||
<div class="card-content intro"></div>
|
||||
<template is="dom-repeat" items="[[_restDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
</paper-item>
|
||||
</a>
|
||||
</template>
|
||||
</ha-card>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
<notification-manager
|
||||
hass="[[_fakeHass]]"
|
||||
id="notifications"
|
||||
></notification-manager>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_fakeHass: {
|
||||
type: Object,
|
||||
// Just enough for computeRTL
|
||||
value: {
|
||||
language: "en",
|
||||
translationMetadata: {
|
||||
translations: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
_demo: {
|
||||
type: String,
|
||||
value: document.location.hash.substr(1),
|
||||
observer: "_demoChanged",
|
||||
},
|
||||
_demos: {
|
||||
type: Array,
|
||||
value: Object.keys(DEMOS),
|
||||
},
|
||||
_lovelaceDemos: {
|
||||
type: Array,
|
||||
computed: "_computeLovelace(_demos)",
|
||||
},
|
||||
_restDemos: {
|
||||
type: Array,
|
||||
computed: "_computeRest(_demos)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
|
||||
this.addEventListener("show-notification", (ev) =>
|
||||
this.$.notifications.showDialog({ message: ev.detail.message })
|
||||
);
|
||||
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this.$.notifications.showDialog({
|
||||
message: `Showing more info for ${ev.detail.entityId}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("hashchange", () => {
|
||||
this._demo = document.location.hash.substr(1);
|
||||
});
|
||||
}
|
||||
|
||||
_withDefault(value, def) {
|
||||
return value || def;
|
||||
}
|
||||
|
||||
_demoChanged(demo) {
|
||||
const root = this.$.demo;
|
||||
|
||||
while (root.lastChild) root.removeChild(root.lastChild);
|
||||
|
||||
if (demo) {
|
||||
DEMOS[demo]();
|
||||
const el = document.createElement(demo);
|
||||
root.appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
_computeHeaderButtonClass(demo) {
|
||||
return demo ? "" : "invisible";
|
||||
}
|
||||
|
||||
_backTapped() {
|
||||
document.location.hash = "";
|
||||
}
|
||||
|
||||
_computeLovelace(demos) {
|
||||
return demos.filter((demo) => demo.includes("hui"));
|
||||
}
|
||||
|
||||
_computeRest(demos) {
|
||||
return demos.filter((demo) => !demo.includes("hui"));
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-gallery", HaGallery);
|
256
gallery/src/ha-gallery.ts
Normal file
256
gallery/src/ha-gallery.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import { mdiMenu } from "@mdi/js";
|
||||
import "@material/mwc-drawer";
|
||||
import "@material/mwc-top-app-bar-fixed";
|
||||
import { html, css, LitElement, PropertyValues } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../src/components/ha-icon-button";
|
||||
import "../../src/managers/notification-manager";
|
||||
import { haStyle } from "../../src/resources/styles";
|
||||
import { PAGES, SIDEBAR } from "../build/import-pages";
|
||||
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
||||
import "./components/page-description";
|
||||
|
||||
const GITHUB_DEMO_URL =
|
||||
"https://github.com/home-assistant/frontend/blob/dev/gallery/src/pages/";
|
||||
|
||||
const FAKE_HASS = {
|
||||
// Just enough for computeRTL for notification-manager
|
||||
language: "en",
|
||||
translationMetadata: {
|
||||
translations: {},
|
||||
},
|
||||
};
|
||||
|
||||
@customElement("ha-gallery")
|
||||
class HaGallery extends LitElement {
|
||||
@property() private _page =
|
||||
document.location.hash.substring(1) ||
|
||||
`${SIDEBAR[0].category}/${SIDEBAR[0].pages![0]}`;
|
||||
|
||||
@query("notification-manager")
|
||||
private _notifications!: HTMLElementTagNameMap["notification-manager"];
|
||||
|
||||
@query("mwc-drawer")
|
||||
private _drawer!: HTMLElementTagNameMap["mwc-drawer"];
|
||||
|
||||
private _narrow = window.matchMedia("(max-width: 600px)").matches;
|
||||
|
||||
render() {
|
||||
const sidebar: unknown[] = [];
|
||||
|
||||
for (const group of SIDEBAR) {
|
||||
const links: unknown[] = [];
|
||||
|
||||
for (const page of group.pages!) {
|
||||
const key = `${group.category}/${page}`;
|
||||
const active = this._page === key;
|
||||
const title = PAGES[key].metadata.title || page;
|
||||
links.push(html`
|
||||
<a ?active=${active} href=${`#${group.category}/${page}`}>${title}</a>
|
||||
`);
|
||||
}
|
||||
|
||||
sidebar.push(
|
||||
group.header
|
||||
? html`
|
||||
<details>
|
||||
<summary class="section">${group.header}</summary>
|
||||
${links}
|
||||
</details>
|
||||
`
|
||||
: links
|
||||
);
|
||||
}
|
||||
|
||||
return html`
|
||||
<mwc-drawer
|
||||
hasHeader
|
||||
.open=${!this._narrow}
|
||||
.type=${this._narrow ? "modal" : "dismissible"}
|
||||
>
|
||||
<span slot="title">Home Assistant Design</span>
|
||||
<!-- <span slot="subtitle">subtitle</span> -->
|
||||
<div class="sidebar">${sidebar}</div>
|
||||
<div slot="appContent">
|
||||
<mwc-top-app-bar-fixed>
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this._menuTapped}
|
||||
.path=${mdiMenu}
|
||||
></ha-icon-button>
|
||||
|
||||
<div slot="title">
|
||||
${PAGES[this._page].metadata.title || this._page.split("/")[1]}
|
||||
</div>
|
||||
</mwc-top-app-bar-fixed>
|
||||
<div class="content">
|
||||
${PAGES[this._page].description
|
||||
? html`
|
||||
<page-description .page=${this._page}></page-description>
|
||||
`
|
||||
: ""}
|
||||
${dynamicElement(`demo-${this._page.replace("/", "-")}`)}
|
||||
</div>
|
||||
<div class="page-footer">
|
||||
${PAGES[this._page].description ||
|
||||
Object.keys(PAGES[this._page].metadata).length > 0
|
||||
? html`
|
||||
<a
|
||||
href=${`${GITHUB_DEMO_URL}${this._page}.markdown`}
|
||||
target="_blank"
|
||||
>
|
||||
Edit text
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${PAGES[this._page].demo
|
||||
? html`
|
||||
<a
|
||||
href=${`${GITHUB_DEMO_URL}${this._page}.ts`}
|
||||
target="_blank"
|
||||
>
|
||||
Edit demo
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</mwc-drawer>
|
||||
<notification-manager
|
||||
.hass=${FAKE_HASS}
|
||||
id="notifications"
|
||||
></notification-manager>
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
this.addEventListener("show-notification", (ev) =>
|
||||
this._notifications.showDialog({ message: ev.detail.message })
|
||||
);
|
||||
this.addEventListener("alert-dismissed-clicked", () =>
|
||||
this._notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||
);
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this._notifications.showDialog({
|
||||
message: `Showing more info for ${ev.detail.entityId}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.location.hash = this._page;
|
||||
|
||||
window.addEventListener("hashchange", () => {
|
||||
this._page = document.location.hash.substring(1);
|
||||
if (this._narrow) {
|
||||
this._drawer.open = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (!changedProps.has("_page")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PAGES[this._page].demo) {
|
||||
PAGES[this._page].demo();
|
||||
}
|
||||
|
||||
const menuItem = this.shadowRoot!.querySelector(
|
||||
`a[href="#${this._page}"]`
|
||||
)!;
|
||||
// Make sure section is expanded
|
||||
if (menuItem.parentElement instanceof HTMLDetailsElement) {
|
||||
menuItem.parentElement.open = true;
|
||||
}
|
||||
}
|
||||
|
||||
_menuTapped() {
|
||||
this._drawer.open = !this._drawer.open;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.sidebar details {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.sidebar summary {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: var(--primary-text-color);
|
||||
display: block;
|
||||
padding: 4px 12px;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar a[active]::before {
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 2px;
|
||||
bottom: 0;
|
||||
left: 2px;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
transition: opacity 15ms linear;
|
||||
will-change: opacity;
|
||||
background-color: var(--sidebar-selected-icon-color);
|
||||
opacity: 0.12;
|
||||
}
|
||||
|
||||
div[slot="appContent"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background: var(--primary-background-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
page-description {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
text-align: center;
|
||||
margin: 16px 0;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.page-footer a {
|
||||
display: inline-block;
|
||||
margin: 0 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-gallery": HaGallery;
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#2157BC" />
|
||||
<title>HAGallery</title>
|
||||
<title>Home Assistant Design</title>
|
||||
|
||||
<script type="module" src="<%= latestGalleryJS %>"></script>
|
||||
<style>
|
||||
|
3
gallery/src/pages/automation/describe-action.markdown
Normal file
3
gallery/src/pages/automation/describe-action.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Describe Action
|
||||
---
|
@@ -1,16 +1,10 @@
|
||||
import { safeDump } from "js-yaml";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeAction } from "../../../src/data/script_i18n";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { describeAction } from "../../../../src/data/script_i18n";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const actions = [
|
||||
{ wait_template: "{{ true }}", alias: "Something with an alias" },
|
||||
@@ -62,7 +56,7 @@ export class DemoAutomationDescribeAction extends LitElement {
|
||||
(conf) => html`
|
||||
<div class="action">
|
||||
<span>${describeAction(this.hass, conf as any)}</span>
|
||||
<pre>${safeDump(conf)}</pre>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
3
gallery/src/pages/automation/describe-condition.markdown
Normal file
3
gallery/src/pages/automation/describe-condition.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Describe Condition
|
||||
---
|
@@ -1,13 +1,8 @@
|
||||
import { safeDump } from "js-yaml";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeCondition } from "../../../src/data/automation_i18n";
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { describeCondition } from "../../../../src/data/automation_i18n";
|
||||
|
||||
const conditions = [
|
||||
{ condition: "and" },
|
||||
@@ -31,7 +26,7 @@ export class DemoAutomationDescribeCondition extends LitElement {
|
||||
(conf) => html`
|
||||
<div class="condition">
|
||||
<span>${describeCondition(conf as any)}</span>
|
||||
<pre>${safeDump(conf)}</pre>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
3
gallery/src/pages/automation/describe-trigger.markdown
Normal file
3
gallery/src/pages/automation/describe-trigger.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Describe Trigger
|
||||
---
|
@@ -1,13 +1,8 @@
|
||||
import { safeDump } from "js-yaml";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import { describeTrigger } from "../../../src/data/automation_i18n";
|
||||
import { dump } from "js-yaml";
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { describeTrigger } from "../../../../src/data/automation_i18n";
|
||||
|
||||
const triggers = [
|
||||
{ platform: "state" },
|
||||
@@ -34,7 +29,7 @@ export class DemoAutomationDescribeTrigger extends LitElement {
|
||||
(conf) => html`
|
||||
<div class="trigger">
|
||||
<span>${describeTrigger(conf as any)}</span>
|
||||
<pre>${safeDump(conf)}</pre>
|
||||
<pre>${dump(conf)}</pre>
|
||||
</div>
|
||||
`
|
||||
)}
|
3
gallery/src/pages/automation/editor-action.markdown
Normal file
3
gallery/src/pages/automation/editor-action.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Actions
|
||||
---
|
91
gallery/src/pages/automation/editor-action.ts
Normal file
91
gallery/src/pages/automation/editor-action.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||
import "../../../../src/panels/config/automation/action/ha-automation-action";
|
||||
import { HaChooseAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-choose";
|
||||
import { HaDelayAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-delay";
|
||||
import { HaDeviceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-device_id";
|
||||
import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event";
|
||||
import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
|
||||
import { HaSceneAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-scene";
|
||||
import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service";
|
||||
import { HaWaitForTriggerAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger";
|
||||
import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
|
||||
import { Action } from "../../../../src/data/script";
|
||||
import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition";
|
||||
|
||||
const SCHEMAS: { name: string; actions: Action[] }[] = [
|
||||
{ name: "Event", actions: [HaEventAction.defaultConfig] },
|
||||
{ name: "Device", actions: [HaDeviceAction.defaultConfig] },
|
||||
{ name: "Service", actions: [HaServiceAction.defaultConfig] },
|
||||
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
||||
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
||||
{ name: "Scene", actions: [HaSceneAction.defaultConfig] },
|
||||
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
||||
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
||||
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
||||
{ name: "Choose", actions: [HaChooseAction.defaultConfig] },
|
||||
{ name: "Variables", actions: [{ variables: { hello: "1" } }] },
|
||||
];
|
||||
|
||||
@customElement("demo-automation-editor-action")
|
||||
class DemoHaAutomationEditorAction extends LitElement {
|
||||
@state() private hass!: HomeAssistant;
|
||||
|
||||
private data: any = SCHEMAS.map((info) => info.actions);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
mockEntityRegistry(hass);
|
||||
mockDeviceRegistry(hass);
|
||||
mockAreaRegistry(hass);
|
||||
mockHassioSupervisor(hass);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valueChanged = (ev) => {
|
||||
const sampleIdx = ev.target.sampleIdx;
|
||||
this.data[sampleIdx] = ev.detail.value;
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
${SCHEMAS.map(
|
||||
(info, sampleIdx) => html`
|
||||
<demo-black-white-row
|
||||
.title=${info.name}
|
||||
.value=${this.data[sampleIdx]}
|
||||
>
|
||||
${["light", "dark"].map(
|
||||
(slot) =>
|
||||
html`
|
||||
<ha-automation-action
|
||||
slot=${slot}
|
||||
.hass=${this.hass}
|
||||
.actions=${this.data[sampleIdx]}
|
||||
.sampleIdx=${sampleIdx}
|
||||
@value-changed=${valueChanged}
|
||||
></ha-automation-action>
|
||||
`
|
||||
)}
|
||||
</demo-black-white-row>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-automation-editor-action": DemoHaAutomationEditorAction;
|
||||
}
|
||||
}
|
3
gallery/src/pages/automation/editor-condition.markdown
Normal file
3
gallery/src/pages/automation/editor-condition.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Conditions
|
||||
---
|
127
gallery/src/pages/automation/editor-condition.ts
Normal file
127
gallery/src/pages/automation/editor-condition.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||
import type { Condition } from "../../../../src/data/automation";
|
||||
import "../../../../src/panels/config/automation/condition/ha-automation-condition";
|
||||
import { HaDeviceCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
|
||||
import { HaLogicalCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-logical";
|
||||
import HaNumericStateCondition from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-numeric_state";
|
||||
import { HaStateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-state";
|
||||
import { HaSunCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-sun";
|
||||
import { HaTemplateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-template";
|
||||
import { HaTimeCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-time";
|
||||
import { HaTriggerCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
|
||||
import { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
|
||||
|
||||
const SCHEMAS: { name: string; conditions: Condition[] }[] = [
|
||||
{
|
||||
name: "State",
|
||||
conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Numeric State",
|
||||
conditions: [
|
||||
{ condition: "numeric_state", ...HaNumericStateCondition.defaultConfig },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sun",
|
||||
conditions: [{ condition: "sun", ...HaSunCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Zone",
|
||||
conditions: [{ condition: "zone", ...HaZoneCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Time",
|
||||
conditions: [{ condition: "time", ...HaTimeCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Template",
|
||||
conditions: [
|
||||
{ condition: "template", ...HaTemplateCondition.defaultConfig },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Device",
|
||||
conditions: [{ condition: "device", ...HaDeviceCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "And",
|
||||
conditions: [{ condition: "and", ...HaLogicalCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Or",
|
||||
conditions: [{ condition: "or", ...HaLogicalCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Not",
|
||||
conditions: [{ condition: "not", ...HaLogicalCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Trigger",
|
||||
conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-automation-editor-condition")
|
||||
class DemoHaAutomationEditorCondition extends LitElement {
|
||||
@state() private hass!: HomeAssistant;
|
||||
|
||||
private data: any = SCHEMAS.map((info) => info.conditions);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
mockEntityRegistry(hass);
|
||||
mockDeviceRegistry(hass);
|
||||
mockAreaRegistry(hass);
|
||||
mockHassioSupervisor(hass);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valueChanged = (ev) => {
|
||||
const sampleIdx = ev.target.sampleIdx;
|
||||
this.data[sampleIdx] = ev.detail.value;
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
${SCHEMAS.map(
|
||||
(info, sampleIdx) => html`
|
||||
<demo-black-white-row
|
||||
.title=${info.name}
|
||||
.value=${this.data[sampleIdx]}
|
||||
>
|
||||
${["light", "dark"].map(
|
||||
(slot) =>
|
||||
html`
|
||||
<ha-automation-condition
|
||||
slot=${slot}
|
||||
.hass=${this.hass}
|
||||
.conditions=${this.data[sampleIdx]}
|
||||
.sampleIdx=${sampleIdx}
|
||||
@value-changed=${valueChanged}
|
||||
></ha-automation-condition>
|
||||
`
|
||||
)}
|
||||
</demo-black-white-row>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-automation-editor-condition": DemoHaAutomationEditorCondition;
|
||||
}
|
||||
}
|
3
gallery/src/pages/automation/editor-trigger.markdown
Normal file
3
gallery/src/pages/automation/editor-trigger.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Triggers
|
||||
---
|
159
gallery/src/pages/automation/editor-trigger.ts
Normal file
159
gallery/src/pages/automation/editor-trigger.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||
import type { Trigger } from "../../../../src/data/automation";
|
||||
import { HaGeolocationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location";
|
||||
import { HaEventTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event";
|
||||
import { HaHassTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant";
|
||||
import { HaNumericStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state";
|
||||
import { HaSunTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-sun";
|
||||
import { HaTagTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-tag";
|
||||
import { HaTemplateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-template";
|
||||
import { HaTimeTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time";
|
||||
import { HaTimePatternTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern";
|
||||
import { HaWebhookTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-webhook";
|
||||
import { HaZoneTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone";
|
||||
import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device";
|
||||
import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state";
|
||||
import { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt";
|
||||
import "../../../../src/panels/config/automation/trigger/ha-automation-trigger";
|
||||
|
||||
const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
|
||||
{
|
||||
name: "State",
|
||||
triggers: [{ platform: "state", ...HaStateTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "MQTT",
|
||||
triggers: [{ platform: "mqtt", ...HaMQTTTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "GeoLocation",
|
||||
triggers: [
|
||||
{ platform: "geo_location", ...HaGeolocationTrigger.defaultConfig },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Home Assistant",
|
||||
triggers: [{ platform: "homeassistant", ...HaHassTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Numeric State",
|
||||
triggers: [
|
||||
{ platform: "numeric_state", ...HaNumericStateTrigger.defaultConfig },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Sun",
|
||||
triggers: [{ platform: "sun", ...HaSunTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Time Pattern",
|
||||
triggers: [
|
||||
{ platform: "time_pattern", ...HaTimePatternTrigger.defaultConfig },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Webhook",
|
||||
triggers: [{ platform: "webhook", ...HaWebhookTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Zone",
|
||||
triggers: [{ platform: "zone", ...HaZoneTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Tag",
|
||||
triggers: [{ platform: "tag", ...HaTagTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Time",
|
||||
triggers: [{ platform: "time", ...HaTimeTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Template",
|
||||
triggers: [{ platform: "template", ...HaTemplateTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Event",
|
||||
triggers: [{ platform: "event", ...HaEventTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Device Trigger",
|
||||
triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-automation-editor-trigger")
|
||||
class DemoHaAutomationEditorTrigger extends LitElement {
|
||||
@state() private hass!: HomeAssistant;
|
||||
|
||||
private data: any = SCHEMAS.map((info) => info.triggers);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
mockEntityRegistry(hass);
|
||||
mockDeviceRegistry(hass);
|
||||
mockAreaRegistry(hass);
|
||||
mockHassioSupervisor(hass);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valueChanged = (ev) => {
|
||||
const sampleIdx = ev.target.sampleIdx;
|
||||
this.data[sampleIdx] = ev.detail.value;
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
${SCHEMAS.map(
|
||||
(info, sampleIdx) => html`
|
||||
<demo-black-white-row
|
||||
.title=${info.name}
|
||||
.value=${this.data[sampleIdx]}
|
||||
>
|
||||
${["light", "dark"].map(
|
||||
(slot) =>
|
||||
html`
|
||||
<ha-automation-trigger
|
||||
slot=${slot}
|
||||
.hass=${this.hass}
|
||||
.triggers=${this.data[sampleIdx]}
|
||||
.sampleIdx=${sampleIdx}
|
||||
@value-changed=${valueChanged}
|
||||
></ha-automation-trigger>
|
||||
`
|
||||
)}
|
||||
</demo-black-white-row>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-automation-editor-trigger": DemoHaAutomationEditorTrigger;
|
||||
}
|
||||
}
|
3
gallery/src/pages/automation/trace-timeline.markdown
Normal file
3
gallery/src/pages/automation/trace-timeline.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Trace Timelines
|
||||
---
|
@@ -1,18 +1,13 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/trace/hat-script-graph";
|
||||
import "../../../../src/components/trace/hat-trace-timeline";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { mockDemoTrace } from "../../data/traces/mock-demo-trace";
|
||||
import { DemoTrace } from "../../data/traces/types";
|
||||
|
||||
const traces: DemoTrace[] = [
|
||||
mockDemoTrace({ state: "running" }),
|
3
gallery/src/pages/automation/trace.markdown
Normal file
3
gallery/src/pages/automation/trace.markdown
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
title: Trace Graphs
|
||||
---
|
@@ -1,20 +1,14 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
import { basicTrace } from "../data/traces/basic_trace";
|
||||
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/trace/hat-script-graph";
|
||||
import "../../../../src/components/trace/hat-trace-timeline";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { DemoTrace } from "../../data/traces/types";
|
||||
import { basicTrace } from "../../data/traces/basic_trace";
|
||||
import { motionLightTrace } from "../../data/traces/motion-light-trace";
|
||||
|
||||
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||
|
||||
@@ -22,7 +16,7 @@ const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||
export class DemoAutomationTrace extends LitElement {
|
||||
@property({ attribute: false }) hass?: HomeAssistant;
|
||||
|
||||
@internalProperty() private _selected = {};
|
||||
@state() private _selected = {};
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user