mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-12 10:49:25 +00:00
Compare commits
617 Commits
vite
...
highlight_
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a5c6ffd1b9 | ||
![]() |
9aaaaae175 | ||
![]() |
7d39b69540 | ||
![]() |
09bad14c3d | ||
![]() |
369c9dc6e2 | ||
![]() |
9676d2cee7 | ||
![]() |
5156c67226 | ||
![]() |
4d48fc3d85 | ||
![]() |
20da329a21 | ||
![]() |
4b664cc142 | ||
![]() |
c9b620fdb2 | ||
![]() |
25c886d401 | ||
![]() |
740805356f | ||
![]() |
ce5fb57577 | ||
![]() |
3e20d2b454 | ||
![]() |
a9e8186491 | ||
![]() |
3bb909b026 | ||
![]() |
b921d91aeb | ||
![]() |
05790954c6 | ||
![]() |
13014c1351 | ||
![]() |
e34c63b830 | ||
![]() |
943100d758 | ||
![]() |
55f40d66f2 | ||
![]() |
593e5ac79c | ||
![]() |
ef31bce5ee | ||
![]() |
3c75eb96f1 | ||
![]() |
f34dfde925 | ||
![]() |
e3b72fe0aa | ||
![]() |
60de74a375 | ||
![]() |
55e58f8d35 | ||
![]() |
a465254418 | ||
![]() |
5d27a138cf | ||
![]() |
22f4b036df | ||
![]() |
03f694922d | ||
![]() |
a841e287e5 | ||
![]() |
5d2afdd825 | ||
![]() |
67240e2339 | ||
![]() |
f84a8eccfa | ||
![]() |
68a058e4f1 | ||
![]() |
d678b42ece | ||
![]() |
2cf63cda08 | ||
![]() |
7bd4eeb0df | ||
![]() |
dc3ee7c779 | ||
![]() |
e8cc97a8e5 | ||
![]() |
3b837e1d54 | ||
![]() |
bb6c2050bc | ||
![]() |
082d4f9691 | ||
![]() |
153d68a9cd | ||
![]() |
0404faa856 | ||
![]() |
afbc2d6b8f | ||
![]() |
89ecc8bd2f | ||
![]() |
7f21a2b319 | ||
![]() |
e2f07f6723 | ||
![]() |
a475e143b7 | ||
![]() |
e50fd80b2e | ||
![]() |
68ea1abc05 | ||
![]() |
2e76b306c4 | ||
![]() |
ca3cac4ed3 | ||
![]() |
41852460e1 | ||
![]() |
9ec4e083d9 | ||
![]() |
9560a1c4a7 | ||
![]() |
4f5a47ace7 | ||
![]() |
01c4d662f2 | ||
![]() |
194024edb9 | ||
![]() |
bef0d3a6a1 | ||
![]() |
47a024b795 | ||
![]() |
39847f9c9d | ||
![]() |
f24f21ca91 | ||
![]() |
c8ea37eec0 | ||
![]() |
b71f452795 | ||
![]() |
7ea1ece169 | ||
![]() |
aece3a37c0 | ||
![]() |
705871f8dc | ||
![]() |
4a11975349 | ||
![]() |
4d3d27f2c4 | ||
![]() |
d784a30d42 | ||
![]() |
35f776284b | ||
![]() |
f659a6fe37 | ||
![]() |
ad53c99fc4 | ||
![]() |
fa0172d00c | ||
![]() |
845411b48c | ||
![]() |
d715867b09 | ||
![]() |
0ca2cdfbed | ||
![]() |
0d1c72386e | ||
![]() |
c91779dffe | ||
![]() |
3853cc9214 | ||
![]() |
a66b3f6b80 | ||
![]() |
c97ec32343 | ||
![]() |
2abba7e445 | ||
![]() |
f887c27ad1 | ||
![]() |
6ee8d74899 | ||
![]() |
f196c72563 | ||
![]() |
419e564441 | ||
![]() |
de97b54c95 | ||
![]() |
07001f7b5c | ||
![]() |
bee17fce64 | ||
![]() |
718904a853 | ||
![]() |
72af4a69d6 | ||
![]() |
fe50f4229c | ||
![]() |
ca4de877c1 | ||
![]() |
1dfecf9618 | ||
![]() |
0a3505ed89 | ||
![]() |
33cbf7eabe | ||
![]() |
935d97ce1a | ||
![]() |
9f73f0ca8d | ||
![]() |
d8cdbac15e | ||
![]() |
03b8c1348c | ||
![]() |
25a0be7672 | ||
![]() |
08f1ce2d54 | ||
![]() |
bea20d0495 | ||
![]() |
5ae10e8516 | ||
![]() |
e3f4a9ce5b | ||
![]() |
cf1fb606fb | ||
![]() |
54ec81b67d | ||
![]() |
f2a9725572 | ||
![]() |
4765114e80 | ||
![]() |
5ff757ad65 | ||
![]() |
1642c68493 | ||
![]() |
f31f10cea9 | ||
![]() |
76e0bbb55d | ||
![]() |
f43af9c0a5 | ||
![]() |
f7a3d2705c | ||
![]() |
22c8af0cc5 | ||
![]() |
f263a5221d | ||
![]() |
3834ab8ede | ||
![]() |
e2e167630d | ||
![]() |
01dd44300b | ||
![]() |
b30160d671 | ||
![]() |
f44d505b41 | ||
![]() |
b58c17e75e | ||
![]() |
ae590d42dc | ||
![]() |
d7917160c0 | ||
![]() |
01e4414d17 | ||
![]() |
0bc2eb530d | ||
![]() |
12b124e5a3 | ||
![]() |
478a4b2593 | ||
![]() |
9752e30eb4 | ||
![]() |
af6e87ba31 | ||
![]() |
64d390ad0f | ||
![]() |
c94bcb6896 | ||
![]() |
97f9df2f2d | ||
![]() |
4e7f68a86c | ||
![]() |
2f7f677549 | ||
![]() |
f44d867d3a | ||
![]() |
6f636187f7 | ||
![]() |
9414f89e50 | ||
![]() |
60bf1a5451 | ||
![]() |
32ba8f4731 | ||
![]() |
81f96de2bd | ||
![]() |
0c417755ed | ||
![]() |
93e5bde797 | ||
![]() |
b6eaf0a7c5 | ||
![]() |
5f1851bade | ||
![]() |
5c66a02711 | ||
![]() |
bde925a0e3 | ||
![]() |
0f574a765b | ||
![]() |
782b941531 | ||
![]() |
f42c0a0717 | ||
![]() |
13ac14d449 | ||
![]() |
db9cea81db | ||
![]() |
7c1fd542da | ||
![]() |
54a2b2534a | ||
![]() |
f5fb6c1e03 | ||
![]() |
781c0701fc | ||
![]() |
742f1f85dc | ||
![]() |
a648e9be49 | ||
![]() |
fd9441dde2 | ||
![]() |
b5ec59c396 | ||
![]() |
60e4594abd | ||
![]() |
79692ef58a | ||
![]() |
ace7ee5622 | ||
![]() |
741ac679a0 | ||
![]() |
d76af2cb61 | ||
![]() |
b7d4c40736 | ||
![]() |
6092af8de6 | ||
![]() |
627424b8b9 | ||
![]() |
e33aff7cf3 | ||
![]() |
ef0bfb237a | ||
![]() |
c042c5568b | ||
![]() |
d84a7ee358 | ||
![]() |
8bfc8ece9d | ||
![]() |
2d3cf7d84d | ||
![]() |
520ef8f1df | ||
![]() |
f251d4267f | ||
![]() |
2052a5351c | ||
![]() |
9807d0aede | ||
![]() |
a41afcd714 | ||
![]() |
d93d2b5945 | ||
![]() |
d54a129605 | ||
![]() |
77911980cb | ||
![]() |
d51fd1e2f9 | ||
![]() |
fe54f8eb16 | ||
![]() |
fc7c4af27a | ||
![]() |
09e7600d86 | ||
![]() |
17410874e3 | ||
![]() |
03d4174163 | ||
![]() |
99eff73b0d | ||
![]() |
acefa39796 | ||
![]() |
c01c0528a6 | ||
![]() |
0ec58007c9 | ||
![]() |
e8daf88729 | ||
![]() |
ab74c7f7eb | ||
![]() |
6b673c7f44 | ||
![]() |
53510a3cb9 | ||
![]() |
d4d38a880d | ||
![]() |
18783d5e3b | ||
![]() |
eb235cb552 | ||
![]() |
435a6b6d53 | ||
![]() |
8d13745c6b | ||
![]() |
14c7cfc64c | ||
![]() |
c7821b9cee | ||
![]() |
a1d66aef0c | ||
![]() |
e275f1f4b9 | ||
![]() |
48de8b0739 | ||
![]() |
b75dc0efe0 | ||
![]() |
1d498349c5 | ||
![]() |
5cdcec699b | ||
![]() |
cd72287d99 | ||
![]() |
c8717bfa32 | ||
![]() |
83de75b689 | ||
![]() |
e5ea762cbc | ||
![]() |
01df01cd66 | ||
![]() |
2c07a2c825 | ||
![]() |
c3f50ba0fb | ||
![]() |
c04419fd09 | ||
![]() |
9c7af0dfce | ||
![]() |
b66d14e980 | ||
![]() |
6a553e9554 | ||
![]() |
4273b72d71 | ||
![]() |
9ccfa79199 | ||
![]() |
fe3d22d4f8 | ||
![]() |
e06642e892 | ||
![]() |
5199e946a1 | ||
![]() |
17aff2f9b8 | ||
![]() |
f7c7ac44f7 | ||
![]() |
62dd0a561e | ||
![]() |
858eacddea | ||
![]() |
471bb5169c | ||
![]() |
9d89aa329c | ||
![]() |
4e4d8bdc5e | ||
![]() |
a30ec32ac1 | ||
![]() |
a9192ae2e1 | ||
![]() |
3d4a0b02e5 | ||
![]() |
3659cf7c87 | ||
![]() |
fbcf35414c | ||
![]() |
d79e5dd8fb | ||
![]() |
92b116c0da | ||
![]() |
da3f911deb | ||
![]() |
9d82ce8ab4 | ||
![]() |
db9597d2e7 | ||
![]() |
1523558f4c | ||
![]() |
bd9f4fe41c | ||
![]() |
6b938b2597 | ||
![]() |
5a8009e46e | ||
![]() |
c92eeb6bb7 | ||
![]() |
8ea6baaf5d | ||
![]() |
1ed03842c0 | ||
![]() |
0e9984413c | ||
![]() |
362b419814 | ||
![]() |
bffcccc1fe | ||
![]() |
b8e9a4ce9f | ||
![]() |
bdff3fd452 | ||
![]() |
1fc51f0087 | ||
![]() |
226013d999 | ||
![]() |
86847263b8 | ||
![]() |
b798523d53 | ||
![]() |
bd59c4fccf | ||
![]() |
566ffe24a4 | ||
![]() |
0e5c1b2041 | ||
![]() |
9a088a21da | ||
![]() |
1160d27004 | ||
![]() |
b4e5740050 | ||
![]() |
12bb3f5796 | ||
![]() |
ff62fdb69d | ||
![]() |
4ebf32cb1f | ||
![]() |
5afb8a77a9 | ||
![]() |
48ed33af95 | ||
![]() |
4a64cd4464 | ||
![]() |
8ae1a1b558 | ||
![]() |
ef1dd8b761 | ||
![]() |
3766f44787 | ||
![]() |
101067d018 | ||
![]() |
448d19bfbb | ||
![]() |
c1caad6d43 | ||
![]() |
a653bf5b0d | ||
![]() |
afdb369e04 | ||
![]() |
f02841409c | ||
![]() |
d4000cf662 | ||
![]() |
e1cf5919fa | ||
![]() |
d33e8d77c2 | ||
![]() |
3a522215a9 | ||
![]() |
7de6ea0879 | ||
![]() |
5ee0250ba5 | ||
![]() |
69d0a22091 | ||
![]() |
4d6c11ce31 | ||
![]() |
178605664e | ||
![]() |
12acb5473b | ||
![]() |
0cf8004b8d | ||
![]() |
6b0a8eae74 | ||
![]() |
00412c7216 | ||
![]() |
6483f23558 | ||
![]() |
4234d51a29 | ||
![]() |
9243d300cc | ||
![]() |
2ce70206c6 | ||
![]() |
b22455d2a5 | ||
![]() |
4f0bb9f6c3 | ||
![]() |
7dfa1b0942 | ||
![]() |
093e23c006 | ||
![]() |
5f003ccbe2 | ||
![]() |
8d0608610f | ||
![]() |
8bfe583a20 | ||
![]() |
0a3172dfdb | ||
![]() |
5c0e151bc2 | ||
![]() |
e69f36047d | ||
![]() |
ed368ddd9d | ||
![]() |
8400e90e34 | ||
![]() |
4cd95b724b | ||
![]() |
46b3836fbd | ||
![]() |
9988227d93 | ||
![]() |
9d289bfa34 | ||
![]() |
048de6b388 | ||
![]() |
9b7d8934da | ||
![]() |
0dd9b21c2d | ||
![]() |
417184525f | ||
![]() |
0a09ec706f | ||
![]() |
cf43b26e14 | ||
![]() |
a8b27e224f | ||
![]() |
dc5d14834d | ||
![]() |
12c935f647 | ||
![]() |
7710cb245c | ||
![]() |
a6b77c0457 | ||
![]() |
748a05f355 | ||
![]() |
831b9da0cf | ||
![]() |
a3339c9d5f | ||
![]() |
d228f38471 | ||
![]() |
fe13853b8b | ||
![]() |
6f4dbdc959 | ||
![]() |
870f0bcbb1 | ||
![]() |
599dd81e3c | ||
![]() |
9e99d158fd | ||
![]() |
136ebb5a07 | ||
![]() |
707338b1aa | ||
![]() |
7e06bd53b6 | ||
![]() |
08c1b864fc | ||
![]() |
16e7a16d12 | ||
![]() |
45200da32f | ||
![]() |
84a9ca59ef | ||
![]() |
40f4c35b42 | ||
![]() |
bb77d34017 | ||
![]() |
9659ebe59b | ||
![]() |
16c914b139 | ||
![]() |
142f26add1 | ||
![]() |
ef7d2aea8d | ||
![]() |
1aa40cb6df | ||
![]() |
cd06b931a9 | ||
![]() |
fd2df92000 | ||
![]() |
b565a8b8f7 | ||
![]() |
1aab656705 | ||
![]() |
6919d0cde6 | ||
![]() |
95fd8de1bc | ||
![]() |
28ca1a5193 | ||
![]() |
a1d07e5a00 | ||
![]() |
131a7f3782 | ||
![]() |
c28a7d6c10 | ||
![]() |
6b20bb967b | ||
![]() |
75f228418d | ||
![]() |
41f8b0d19b | ||
![]() |
cea3b8b010 | ||
![]() |
aba0e1f026 | ||
![]() |
f42587af22 | ||
![]() |
edcb7e87bb | ||
![]() |
96f0ceeb8c | ||
![]() |
a9baa7f1c1 | ||
![]() |
b1483287dc | ||
![]() |
a4657541fc | ||
![]() |
56d88b4c56 | ||
![]() |
02313e4be8 | ||
![]() |
2a14a3a4dc | ||
![]() |
d02a2e8c2e | ||
![]() |
0d281f8437 | ||
![]() |
2b0f43f334 | ||
![]() |
f9d28fc124 | ||
![]() |
da07173471 | ||
![]() |
4deeff7029 | ||
![]() |
1538fbb102 | ||
![]() |
b9259b87eb | ||
![]() |
30997dbc88 | ||
![]() |
a9d926e80a | ||
![]() |
c41369c89c | ||
![]() |
656bef3da9 | ||
![]() |
607eb6d130 | ||
![]() |
f2e9b3577d | ||
![]() |
cb2c6d8560 | ||
![]() |
bfe8346ced | ||
![]() |
88da9bb91b | ||
![]() |
5e2ee1a16c | ||
![]() |
2fdc746392 | ||
![]() |
1667973a66 | ||
![]() |
42f0101440 | ||
![]() |
13b69bff1b | ||
![]() |
2c2226dfd6 | ||
![]() |
a3fdfe0e15 | ||
![]() |
a0de209a55 | ||
![]() |
0208b50ac7 | ||
![]() |
4a61779aba | ||
![]() |
05057ade05 | ||
![]() |
6fb206853c | ||
![]() |
fab68055bf | ||
![]() |
d5a77ef3cd | ||
![]() |
dcb2605de4 | ||
![]() |
e6d38f4539 | ||
![]() |
293b56cfa6 | ||
![]() |
775e93d54b | ||
![]() |
7f840e75df | ||
![]() |
2113ea675e | ||
![]() |
916a5c1a6b | ||
![]() |
f684531315 | ||
![]() |
fe8dda8996 | ||
![]() |
4cd4b328c8 | ||
![]() |
3d7ee6a4df | ||
![]() |
d844c89b94 | ||
![]() |
177ea2b85a | ||
![]() |
50c5c15f49 | ||
![]() |
1810760dc7 | ||
![]() |
4635b92e3f | ||
![]() |
1c652626eb | ||
![]() |
2000cfb1db | ||
![]() |
f4d07828e7 | ||
![]() |
95b552671c | ||
![]() |
ef3bc3efe1 | ||
![]() |
371ad899f5 | ||
![]() |
2c54158d84 | ||
![]() |
5d9e30bbdc | ||
![]() |
e477fd567d | ||
![]() |
6a6c2937fe | ||
![]() |
09e17c4da8 | ||
![]() |
fd00469d11 | ||
![]() |
cbbeb795f3 | ||
![]() |
bba40e0da8 | ||
![]() |
d23165d06a | ||
![]() |
405fef6f03 | ||
![]() |
588f217826 | ||
![]() |
3d8b7cf80e | ||
![]() |
c0ef923ad3 | ||
![]() |
3df44fc71e | ||
![]() |
c1965492d9 | ||
![]() |
1f56ffde80 | ||
![]() |
f335fdc002 | ||
![]() |
0c914b5ec8 | ||
![]() |
d767b06858 | ||
![]() |
d4e49f3944 | ||
![]() |
7dfc5b3faf | ||
![]() |
8a88033ab9 | ||
![]() |
7b06b38c94 | ||
![]() |
5409752817 | ||
![]() |
909f3a3005 | ||
![]() |
4930532c7b | ||
![]() |
8a42e65c6a | ||
![]() |
5d4121a9b4 | ||
![]() |
a70e6c49a1 | ||
![]() |
3d83d5f4b5 | ||
![]() |
f9dece0743 | ||
![]() |
ac0871d0e8 | ||
![]() |
ffc19e591d | ||
![]() |
c53380ca3d | ||
![]() |
7c74a2026a | ||
![]() |
adaed438d9 | ||
![]() |
baf38305cb | ||
![]() |
8254712521 | ||
![]() |
53214781e3 | ||
![]() |
88cbbbdf65 | ||
![]() |
c10dca9c7b | ||
![]() |
7f2ebb4bde | ||
![]() |
f1abb60e4a | ||
![]() |
e014c7aff6 | ||
![]() |
b79c03433e | ||
![]() |
34eb4d974d | ||
![]() |
3264be3c5e | ||
![]() |
655f4f75fb | ||
![]() |
4383f31696 | ||
![]() |
99eb15d15e | ||
![]() |
2682c6e150 | ||
![]() |
3a5d854e6d | ||
![]() |
1e90c6387c | ||
![]() |
2cca25f4d0 | ||
![]() |
565724d201 | ||
![]() |
3e4955becd | ||
![]() |
7b560c727f | ||
![]() |
35abd9dfdb | ||
![]() |
b7ccf3e0e5 | ||
![]() |
0d9ab8fdd0 | ||
![]() |
303f9290a8 | ||
![]() |
e0c4dc08a1 | ||
![]() |
8c655883fe | ||
![]() |
ba90785115 | ||
![]() |
7b392b626b | ||
![]() |
8e4ceb7d48 | ||
![]() |
2ab1c6e9a9 | ||
![]() |
dbdced0971 | ||
![]() |
5e481880bd | ||
![]() |
faec063f34 | ||
![]() |
bbea38d227 | ||
![]() |
a0ef60de49 | ||
![]() |
3313572606 | ||
![]() |
c4f850cb14 | ||
![]() |
3bdab738c6 | ||
![]() |
faaef31b9f | ||
![]() |
ca7b8b8b4c | ||
![]() |
9ca84e0694 | ||
![]() |
daaf2b1796 | ||
![]() |
25f7cbea5a | ||
![]() |
c485ea9d7b | ||
![]() |
295390c8e9 | ||
![]() |
3ebf816ce2 | ||
![]() |
0e362b851b | ||
![]() |
8d7ba19a08 | ||
![]() |
08f4aa9d10 | ||
![]() |
98175d5c72 | ||
![]() |
7d4cad90bc | ||
![]() |
335354d962 | ||
![]() |
fe31d15d27 | ||
![]() |
7ceb6eb50d | ||
![]() |
4c4db46aa8 | ||
![]() |
b5724ed343 | ||
![]() |
cae94175fe | ||
![]() |
0494a9d410 | ||
![]() |
c261b5c1ce | ||
![]() |
c89e17ac00 | ||
![]() |
50e7410002 | ||
![]() |
c5b0ebf76d | ||
![]() |
1d08978d6c | ||
![]() |
fc78b6c933 | ||
![]() |
480a5718fc | ||
![]() |
f093bd115c | ||
![]() |
8a86beff14 | ||
![]() |
6020890384 | ||
![]() |
124aa947e2 | ||
![]() |
e1add14453 | ||
![]() |
e3293837a8 | ||
![]() |
5cb2743780 | ||
![]() |
6f0c79ec25 | ||
![]() |
7de7d1d926 | ||
![]() |
89175f8e85 | ||
![]() |
fc48c59eb0 | ||
![]() |
51332bc7e7 | ||
![]() |
7403405d12 | ||
![]() |
1d13947e71 | ||
![]() |
f6cb1ffe20 | ||
![]() |
6d92b5651a | ||
![]() |
3ea5bb2a6c | ||
![]() |
1d367eca69 | ||
![]() |
d4bf3a2ec3 | ||
![]() |
0ef8881660 | ||
![]() |
d7d1121f7d | ||
![]() |
7f089c309f | ||
![]() |
4dcc0bb66c | ||
![]() |
0049be7feb | ||
![]() |
39ff641be9 | ||
![]() |
e2fed24995 | ||
![]() |
c0aa353f83 | ||
![]() |
d8521be63d | ||
![]() |
6d4569c89d | ||
![]() |
cd07553b59 | ||
![]() |
641bfcc9f7 | ||
![]() |
6c01371958 | ||
![]() |
7b00260b1a | ||
![]() |
875142373e | ||
![]() |
ba505b15ef | ||
![]() |
17d227b142 | ||
![]() |
e7e192ffe3 | ||
![]() |
c53ec6e12d | ||
![]() |
aad6492a6a | ||
![]() |
fd5b125c2d | ||
![]() |
5acee76c70 | ||
![]() |
10916fa82a | ||
![]() |
f69951a523 | ||
![]() |
38ba85e89d | ||
![]() |
97023921b8 | ||
![]() |
f835810f0a | ||
![]() |
46f5589530 | ||
![]() |
ff9840c8ef | ||
![]() |
0c197558a1 | ||
![]() |
c409ba149d | ||
![]() |
0b896ddfb1 | ||
![]() |
45721eb4fe | ||
![]() |
1289bd03b2 | ||
![]() |
c1ba8ba6b8 | ||
![]() |
4973d8f629 | ||
![]() |
3aff4c96c4 | ||
![]() |
4005bc8985 | ||
![]() |
62e9792c39 | ||
![]() |
33183cc595 | ||
![]() |
394d552856 | ||
![]() |
aa4f0929e0 | ||
![]() |
f99b9215e3 | ||
![]() |
c51d621fee | ||
![]() |
7499892bc2 | ||
![]() |
cbddebeaa8 | ||
![]() |
bbe4c95109 | ||
![]() |
4c6f9f0dd8 | ||
![]() |
90f7dba793 | ||
![]() |
7c492338a2 | ||
![]() |
530f494df8 | ||
![]() |
8fd1f35c59 | ||
![]() |
af1518e924 | ||
![]() |
473e381d75 | ||
![]() |
7d3acc747d | ||
![]() |
bf7424a67c | ||
![]() |
3fb35871c7 | ||
![]() |
d6d20cd704 | ||
![]() |
9cc6a6b885 | ||
![]() |
ee0be7b6d0 | ||
![]() |
a856337eae | ||
![]() |
6cf47ba4eb | ||
![]() |
3b7a189708 | ||
![]() |
79c542b76a | ||
![]() |
7e6153ba7d |
13
.devcontainer/Dockerfile
Normal file
13
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.9
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
DEBIAN_FRONTEND=noninteractive \
|
||||||
|
DEVCONTAINER=true \
|
||||||
|
PATH=$PATH:./node_modules/.bin
|
||||||
|
|
||||||
|
# Install nvm
|
||||||
|
COPY .nvmrc /tmp/.nvmrc
|
||||||
|
RUN \
|
||||||
|
su vscode -c \
|
||||||
|
"source /usr/local/share/nvm/nvm.sh && nvm install $(cat /tmp/.nvmrc) 2>&1"
|
34
.devcontainer/devcontainer.json
Normal file
34
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "Home Assistant Frontend",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
"context": ".."
|
||||||
|
},
|
||||||
|
"appPort": "8124:8123",
|
||||||
|
"context": "..",
|
||||||
|
"postCreateCommand": "script/bootstrap",
|
||||||
|
"extensions": [
|
||||||
|
"github.vscode-pull-request-github",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"bierner.lit-html",
|
||||||
|
"runem.lit-plugin",
|
||||||
|
"ms-python.vscode-pylance"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.shell.linux": "/bin/bash",
|
||||||
|
"files.eol": "\n",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.formatOnPaste": false,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnType": true,
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"files.trimTrailingWhitespace": true
|
||||||
|
}
|
||||||
|
}
|
@@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Problem-relevant configuration
|
## Problem-relevant frontend configuration
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
An example configuration that caused the problem for you. Fill this out even
|
An example configuration that caused the problem for you, e.g. the YAML configuration
|
||||||
if it seems unimportant to you. Please be sure to remove personal information
|
of the used cards. Fill this out even if it seems unimportant to you. Please be sure
|
||||||
like passwords, private URLs and other credentials.
|
to remove personal information like passwords, private URLs and other credentials.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
|
|||||||
## Javascript errors shown in your browser console/inspector
|
## Javascript errors shown in your browser console/inspector
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
If you come across any javascript or other error logs, e.g., in your browser
|
If you come across any Javascript or other error logs, e.g. in your browser
|
||||||
console/inspector please provide them.
|
console/inspector please provide them.
|
||||||
-->
|
-->
|
||||||
|
|
138
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
138
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
name: Report a bug with the UI, Frontend or Lovelace
|
||||||
|
about: Report an issue related to the Home Assistant frontend.
|
||||||
|
labels: bug
|
||||||
|
title: ""
|
||||||
|
issue_body: true
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue.
|
||||||
|
|
||||||
|
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
|
||||||
|
|
||||||
|
**Please not not report issues for custom Lovelace cards.**
|
||||||
|
|
||||||
|
[fr]: https://github.com/home-assistant/frontend/discussions
|
||||||
|
[releases]: https://github.com/home-assistant/home-assistant/releases
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: Please verify that you've followed these steps
|
||||||
|
options:
|
||||||
|
- label: I have updated to the latest available Home Assistant version.
|
||||||
|
required: true
|
||||||
|
- label: I have cleared the cache of my browser.
|
||||||
|
required: true
|
||||||
|
- label: I have tried a different browser to see if it is related to my browser.
|
||||||
|
required: true
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## The problem
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Describe the issue you are experiencing
|
||||||
|
description: Provide a clear and concise description of what the bug is.
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Describe the behavior you expected
|
||||||
|
description: Describe what you expected to happen or it should look/behave.
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce the issue
|
||||||
|
description: |
|
||||||
|
Please tell us exactly how to reproduce your issue.
|
||||||
|
Provide clear and concise step by step instructions and add code snippets if needed.
|
||||||
|
value: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
...
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Environment
|
||||||
|
- type: input
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: What version of Home Assistant Core has the issue?
|
||||||
|
placeholder: core-
|
||||||
|
description: >
|
||||||
|
Can be found in the Configuration panel -> Info.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What was the last working version of Home Assistant Core?
|
||||||
|
placeholder: core-
|
||||||
|
description: >
|
||||||
|
If known, otherwise leave blank.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: In which browser are you experiencing the issue with?
|
||||||
|
placeholder: Google Chrome 88.0.4324.150
|
||||||
|
description: >
|
||||||
|
Provide the full name and don't forget to add the version!
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Which operating system are you using to run this browser?
|
||||||
|
placeholder: macOS Big Sur (1.11)
|
||||||
|
description: >
|
||||||
|
Don't forget to add the version!
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# Details
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: State of relevant entities
|
||||||
|
description: >
|
||||||
|
If your issue is about how an entity is shown in the UI, please add the
|
||||||
|
state and attributes for all situations. You can find this information
|
||||||
|
at Developer Tools -> States.
|
||||||
|
value: |
|
||||||
|
```yaml
|
||||||
|
# Paste your state here.
|
||||||
|
|
||||||
|
```
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Problem-relevant frontend configuration
|
||||||
|
description: >
|
||||||
|
An example configuration that caused the problem for you, e.g., the YAML
|
||||||
|
configuration of the used cards. Fill this out even if it seems
|
||||||
|
unimportant to you. Please be sure to remove personal information like
|
||||||
|
passwords, private URLs and other credentials.
|
||||||
|
value: |
|
||||||
|
```yaml
|
||||||
|
# Paste your YAML here.
|
||||||
|
|
||||||
|
```
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Javascript errors shown in your browser console/inspector
|
||||||
|
description: >
|
||||||
|
If you come across any Javascript or other error logs, e.g., in your
|
||||||
|
browser console/inspector please provide them.
|
||||||
|
value: |
|
||||||
|
```txt
|
||||||
|
# Paste your logs here.
|
||||||
|
|
||||||
|
```
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Additional information
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
If you have any additional information for us, use the field below.
|
||||||
|
Please note, you can attach screenshots or screen recordings here,
|
||||||
|
by dragging and dropping files in the field below.
|
19
.github/workflows/netflify.yml
vendored
Normal file
19
.github/workflows/netflify.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Netlify
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
trigger_builds:
|
||||||
|
name: Trigger netlify build preview
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- name: Trigger Cast build
|
||||||
|
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_CAST_DEV_BUILD_HOOK }}
|
||||||
|
|
||||||
|
- 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 }}
|
81
.github/workflows/release.yaml
vendored
Normal file
81
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
env:
|
||||||
|
WHEELS_TAG: 3.7-alpine3.11
|
||||||
|
PYTHON_VERSION: 3.7
|
||||||
|
NODE_VERSION: 12.1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Verify version
|
||||||
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
|
|
||||||
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
|
||||||
|
- name: Build and release package
|
||||||
|
run: |
|
||||||
|
python3 -m pip install twine
|
||||||
|
export TWINE_USERNAME="__token__"
|
||||||
|
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
|
||||||
|
|
||||||
|
script/release
|
||||||
|
|
||||||
|
wheels-init:
|
||||||
|
name: Init wheels build
|
||||||
|
needs: release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Generate requirements.txt
|
||||||
|
run: |
|
||||||
|
# Sleep to give pypi time to populate the new version across mirrors
|
||||||
|
sleep 240
|
||||||
|
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
||||||
|
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||||
|
|
||||||
|
- name: Upload requirements.txt
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: requirements
|
||||||
|
path: ./requirements.txt
|
||||||
|
|
||||||
|
build-wheels:
|
||||||
|
name: Build wheels for ${{ matrix.arch }}
|
||||||
|
needs: wheels-init
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||||
|
steps:
|
||||||
|
- name: Download requirements.txt
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: requirements
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
uses: home-assistant/wheels@master
|
||||||
|
with:
|
||||||
|
tag: ${{ env.WHEELS_TAG }}
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
wheels-host: ${{ secrets.WHEELS_HOST }}
|
||||||
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
|
wheels-user: wheels
|
||||||
|
requirements: "requirements.txt"
|
65
.github/workflows/translations.yaml
vendored
Normal file
65
.github/workflows/translations.yaml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Translations
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "30 0 * * *"
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
paths:
|
||||||
|
- src/translations/en.json
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: 12
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload:
|
||||||
|
name: Upload
|
||||||
|
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: 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
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -35,3 +35,6 @@ yarn-error.log
|
|||||||
|
|
||||||
#asdf
|
#asdf
|
||||||
.tool-versions
|
.tool-versions
|
||||||
|
|
||||||
|
# Home Assistant config
|
||||||
|
/config
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
jshint:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
eslint:
|
|
||||||
enabled: true
|
|
||||||
config_file: .eslintrc-hound.json
|
|
71
.vscode/tasks.json
vendored
71
.vscode/tasks.json
vendored
@@ -37,6 +37,37 @@
|
|||||||
"instanceLimit": 1
|
"instanceLimit": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Develop Supervisor panel",
|
||||||
|
"type": "gulp",
|
||||||
|
"task": "develop-hassio",
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "ha-build",
|
||||||
|
"source": "ha-build",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"severity": "error",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "(SyntaxError): (.+): (.+) \\((\\d+):(\\d+)\\)",
|
||||||
|
"severity": 1,
|
||||||
|
"file": 2,
|
||||||
|
"message": 3,
|
||||||
|
"line": 4,
|
||||||
|
"column": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": "Changes detected. Starting compilation",
|
||||||
|
"endsPattern": "Build done @"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isBackground": true,
|
||||||
|
"group": "build",
|
||||||
|
"runOptions": {
|
||||||
|
"instanceLimit": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Develop Gallery",
|
"label": "Develop Gallery",
|
||||||
"type": "gulp",
|
"type": "gulp",
|
||||||
@@ -133,5 +164,45 @@
|
|||||||
"instanceLimit": 1
|
"instanceLimit": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Run HA Core in devcontainer",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "script/core",
|
||||||
|
"isBackground": true,
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"runOptions": {
|
||||||
|
"instanceLimit": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Run HA Core for Supervisor in devcontainer",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "HASSIO=${input:supervisorHost} HASSIO_TOKEN=${input:supervisorToken} script/core",
|
||||||
|
"isBackground": true,
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": [],
|
||||||
|
"runOptions": {
|
||||||
|
"instanceLimit": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"id": "supervisorHost",
|
||||||
|
"type": "promptString",
|
||||||
|
"description": "The IP of the Supervisor host running the Remote API proxy add-on"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "supervisorToken",
|
||||||
|
"type": "promptString",
|
||||||
|
"description": "The token for the Remote API proxy add-on"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant.
|
|||||||
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
|
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
|
||||||
- Production build: `script/build_frontend`
|
- Production build: `script/build_frontend`
|
||||||
- Gallery: `cd gallery && script/develop_gallery`
|
- Gallery: `cd gallery && script/develop_gallery`
|
||||||
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
|
- Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing)
|
||||||
|
|
||||||
## Frontend development
|
## Frontend development
|
||||||
|
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
# Vite
|
|
||||||
|
|
||||||
Vite is a new type of compiler that compiles each file as they come in.
|
|
||||||
|
|
||||||
## Running Vite
|
|
||||||
|
|
||||||
- Checkout this branch
|
|
||||||
- `yarn`
|
|
||||||
- Run `script/develop` until it finishes first webpack build. Then turn it off. We use this right now to prepare the static files + auth/onboarding pages.
|
|
||||||
- Update `hass_frontend/index.html`, find where we import the scripts and replace with:
|
|
||||||
```html
|
|
||||||
<script>
|
|
||||||
// Define in vite config doesn't work.
|
|
||||||
window.__DEV__ = true;
|
|
||||||
window.__DEMO__ = false;
|
|
||||||
window.__BACKWARDS_COMPAT__ = false;
|
|
||||||
window.__BUILD__ = "latest";
|
|
||||||
window.__VERSION__ = "dev";
|
|
||||||
// Temporary to stop an error
|
|
||||||
document.adoptedStyleSheets = [];
|
|
||||||
// Load scripts from Vite dev server
|
|
||||||
import("http://localhost:3000/src/entrypoints/core.ts");
|
|
||||||
import("http://localhost:3000/src/entrypoints/app.ts");
|
|
||||||
```
|
|
||||||
If Vite transforms would work correctly, we would just have to drop the "use-credentials" part in dev and update the import URLs to import from Vite dev server.
|
|
||||||
- Start vite `vite serve -c build-scripts/vite/vite.config.ts`
|
|
||||||
- Open Home Assistant as usual.
|
|
@@ -1,30 +0,0 @@
|
|||||||
# https://dev.azure.com/home-assistant
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr: none
|
|
||||||
schedules:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
displayName: "build preview"
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- dev
|
|
||||||
always: true
|
|
||||||
variables:
|
|
||||||
- group: netlify
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
- job: 'Netlify_preview'
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-latest'
|
|
||||||
steps:
|
|
||||||
- script: |
|
|
||||||
# Cast
|
|
||||||
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_CAST}
|
|
||||||
|
|
||||||
# Demo
|
|
||||||
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO}
|
|
||||||
|
|
||||||
# Gallery
|
|
||||||
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY}
|
|
||||||
displayName: 'Trigger netlify build preview'
|
|
@@ -1,59 +0,0 @@
|
|||||||
# https://dev.azure.com/home-assistant
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
batch: true
|
|
||||||
tags:
|
|
||||||
include:
|
|
||||||
- "*"
|
|
||||||
pr: none
|
|
||||||
variables:
|
|
||||||
- name: versionWheels
|
|
||||||
value: '1.10.1-3.7-alpine3.11'
|
|
||||||
- name: versionNode
|
|
||||||
value: '12.1'
|
|
||||||
- group: twine
|
|
||||||
resources:
|
|
||||||
repositories:
|
|
||||||
- repository: azure
|
|
||||||
type: github
|
|
||||||
name: 'home-assistant/ci-azure'
|
|
||||||
endpoint: 'home-assistant'
|
|
||||||
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- stage: "Validate"
|
|
||||||
jobs:
|
|
||||||
- template: templates/azp-job-version.yaml@azure
|
|
||||||
|
|
||||||
- stage: "Build"
|
|
||||||
jobs:
|
|
||||||
- job: "ReleasePython"
|
|
||||||
pool:
|
|
||||||
vmImage: "ubuntu-latest"
|
|
||||||
steps:
|
|
||||||
- task: UsePythonVersion@0
|
|
||||||
displayName: "Use Python 3.7"
|
|
||||||
inputs:
|
|
||||||
versionSpec: "3.7"
|
|
||||||
- task: NodeTool@0
|
|
||||||
displayName: "Use Node $(versionNode)"
|
|
||||||
inputs:
|
|
||||||
versionSpec: "$(versionNode)"
|
|
||||||
- script: pip install twine wheel
|
|
||||||
displayName: "Install tools"
|
|
||||||
- script: |
|
|
||||||
export TWINE_USERNAME="$(twineUser)"
|
|
||||||
export TWINE_PASSWORD="$(twinePassword)"
|
|
||||||
|
|
||||||
script/release
|
|
||||||
displayName: "Build and release package"
|
|
||||||
- stage: "Wheels"
|
|
||||||
jobs:
|
|
||||||
- template: templates/azp-job-wheels.yaml@azure
|
|
||||||
parameters:
|
|
||||||
builderVersion: '$(versionWheels)'
|
|
||||||
wheelsRequirement: 'requirement.txt'
|
|
||||||
preBuild:
|
|
||||||
- script: |
|
|
||||||
sleep 240
|
|
||||||
echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt
|
|
@@ -1,70 +0,0 @@
|
|||||||
# https://dev.azure.com/home-assistant
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
batch: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- dev
|
|
||||||
paths:
|
|
||||||
include:
|
|
||||||
- translations/en.json
|
|
||||||
pr: none
|
|
||||||
schedules:
|
|
||||||
- cron: "30 0 * * *"
|
|
||||||
displayName: "frontend translation update"
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- dev
|
|
||||||
always: true
|
|
||||||
variables:
|
|
||||||
- group: translation
|
|
||||||
resources:
|
|
||||||
repositories:
|
|
||||||
- repository: azure
|
|
||||||
type: github
|
|
||||||
name: 'home-assistant/ci-azure'
|
|
||||||
endpoint: 'home-assistant'
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
- job: 'Upload'
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-latest'
|
|
||||||
steps:
|
|
||||||
- task: NodeTool@0
|
|
||||||
displayName: 'Use Node 12.x'
|
|
||||||
inputs:
|
|
||||||
versionSpec: '12.x'
|
|
||||||
- script: |
|
|
||||||
export LOKALISE_TOKEN="$(lokaliseToken)"
|
|
||||||
export AZURE_BRANCH="$(Build.SourceBranchName)"
|
|
||||||
|
|
||||||
./script/translations_upload_base
|
|
||||||
displayName: 'Upload Translation'
|
|
||||||
|
|
||||||
- job: 'Download'
|
|
||||||
dependsOn:
|
|
||||||
- 'Upload'
|
|
||||||
condition: or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual'))
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-latest'
|
|
||||||
steps:
|
|
||||||
- task: NodeTool@0
|
|
||||||
displayName: 'Use Node 12.x'
|
|
||||||
inputs:
|
|
||||||
versionSpec: '12.x'
|
|
||||||
- template: templates/azp-step-git-init.yaml@azure
|
|
||||||
- script: |
|
|
||||||
export LOKALISE_TOKEN="$(lokaliseToken)"
|
|
||||||
export AZURE_BRANCH="$(Build.SourceBranchName)"
|
|
||||||
|
|
||||||
npm install
|
|
||||||
./script/translations_download
|
|
||||||
displayName: 'Download Translation'
|
|
||||||
- script: |
|
|
||||||
git checkout dev
|
|
||||||
git add translation
|
|
||||||
git commit -am "[ci skip] Translation update"
|
|
||||||
git push
|
|
||||||
displayName: 'Update translation'
|
|
39
build-scripts/README.md
Normal file
39
build-scripts/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Bundling Home Assistant Frontend
|
||||||
|
|
||||||
|
The Home Assistant build pipeline contains various steps to prepare a build.
|
||||||
|
|
||||||
|
- Generating icon files to be included
|
||||||
|
- Generating translation files to be included
|
||||||
|
- Converting TypeScript, CSS and JSON files to JavaScript
|
||||||
|
- Bundling
|
||||||
|
- Minifying the files
|
||||||
|
- Generating the HTML entrypoint files
|
||||||
|
- Generating the service worker
|
||||||
|
- Compressing the files
|
||||||
|
|
||||||
|
## Converting files
|
||||||
|
|
||||||
|
Currently in Home Assistant we use a bundler to convert TypeScript, CSS and JSON files to JavaScript files that the browser understands.
|
||||||
|
|
||||||
|
We currently rely on Webpack but also have experimental Rollup support. Both of these programs bundle the converted files in both production and development.
|
||||||
|
|
||||||
|
For development, bundling is optional. We just want to get the right files in the browser.
|
||||||
|
|
||||||
|
Responsibilities of the converter during development:
|
||||||
|
|
||||||
|
- Convert TypeScript to JavaScript
|
||||||
|
- Convert CSS to JavaScript that sets the content as the default export
|
||||||
|
- Convert JSON to JavaScript that sets the content as the default export
|
||||||
|
- Make sure import, dynamic import and web worker references work
|
||||||
|
- Add extensions where missing
|
||||||
|
- Resolve absolute package imports
|
||||||
|
- Filter out specific imports/packages
|
||||||
|
- Replace constants with values
|
||||||
|
|
||||||
|
In production, the following responsibilities are added:
|
||||||
|
|
||||||
|
- Minify HTML
|
||||||
|
- Bundle multiple imports so that the browser can fetch less files
|
||||||
|
- Generate a second version that is ES5 compatible
|
||||||
|
|
||||||
|
Configuration for all these steps are specified in [bundle.js](bundle.js).
|
@@ -44,7 +44,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
module.exports.terserOptions = (latestBuild) => ({
|
module.exports.terserOptions = (latestBuild) => ({
|
||||||
safari10: true,
|
safari10: !latestBuild,
|
||||||
ecma: latestBuild ? undefined : 5,
|
ecma: latestBuild ? undefined : 5,
|
||||||
output: { comments: false },
|
output: { comments: false },
|
||||||
});
|
});
|
||||||
@@ -117,7 +117,7 @@ BundleConfig {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports.config = {
|
module.exports.config = {
|
||||||
app({ isProdBuild, latestBuild, isStatsBuild }) {
|
app({ isProdBuild, latestBuild, isStatsBuild, isWDS }) {
|
||||||
return {
|
return {
|
||||||
entry: {
|
entry: {
|
||||||
service_worker: "./src/entrypoints/service_worker.ts",
|
service_worker: "./src/entrypoints/service_worker.ts",
|
||||||
@@ -132,6 +132,7 @@ module.exports.config = {
|
|||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
isStatsBuild,
|
isStatsBuild,
|
||||||
|
isWDS,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -6,6 +6,9 @@ module.exports = {
|
|||||||
useRollup() {
|
useRollup() {
|
||||||
return process.env.ROLLUP === "1";
|
return process.env.ROLLUP === "1";
|
||||||
},
|
},
|
||||||
|
useWDS() {
|
||||||
|
return process.env.WDS === "1";
|
||||||
|
},
|
||||||
isProdBuild() {
|
isProdBuild() {
|
||||||
return (
|
return (
|
||||||
process.env.NODE_ENV === "production" || module.exports.isStatsBuild()
|
process.env.NODE_ENV === "production" || module.exports.isStatsBuild()
|
||||||
|
@@ -12,6 +12,7 @@ require("./webpack.js");
|
|||||||
require("./service-worker.js");
|
require("./service-worker.js");
|
||||||
require("./entry-html.js");
|
require("./entry-html.js");
|
||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
|
require("./wds.js");
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"develop-app",
|
"develop-app",
|
||||||
@@ -28,7 +29,11 @@ gulp.task(
|
|||||||
"build-translations"
|
"build-translations"
|
||||||
),
|
),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useRollup() ? "rollup-watch-app" : "webpack-watch-app"
|
env.useWDS()
|
||||||
|
? "wds-watch-app"
|
||||||
|
: env.useRollup()
|
||||||
|
? "rollup-watch-app"
|
||||||
|
: "webpack-watch-app"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@ const renderTemplate = (pth, data = {}, pathFunc = templatePath) => {
|
|||||||
return compiled({
|
return compiled({
|
||||||
...data,
|
...data,
|
||||||
useRollup: env.useRollup(),
|
useRollup: env.useRollup(),
|
||||||
|
useWDS: env.useWDS(),
|
||||||
renderTemplate,
|
renderTemplate,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -90,10 +91,23 @@ gulp.task("gen-pages-prod", (done) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("gen-index-app-dev", (done) => {
|
gulp.task("gen-index-app-dev", (done) => {
|
||||||
|
let latestAppJS, latestCoreJS, latestCustomPanelJS;
|
||||||
|
|
||||||
|
if (env.useWDS()) {
|
||||||
|
latestAppJS = "http://localhost:8000/src/entrypoints/app.ts";
|
||||||
|
latestCoreJS = "http://localhost:8000/src/entrypoints/core.ts";
|
||||||
|
latestCustomPanelJS =
|
||||||
|
"http://localhost:8000/src/entrypoints/custom-panel.ts";
|
||||||
|
} else {
|
||||||
|
latestAppJS = "/frontend_latest/app.js";
|
||||||
|
latestCoreJS = "/frontend_latest/core.js";
|
||||||
|
latestCustomPanelJS = "/frontend_latest/custom-panel.js";
|
||||||
|
}
|
||||||
|
|
||||||
const content = renderTemplate("index", {
|
const content = renderTemplate("index", {
|
||||||
latestAppJS: "/frontend_latest/app.js",
|
latestAppJS,
|
||||||
latestCoreJS: "/frontend_latest/core.js",
|
latestCoreJS,
|
||||||
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
|
latestCustomPanelJS,
|
||||||
|
|
||||||
es5AppJS: "/frontend_es5/app.js",
|
es5AppJS: "/frontend_es5/app.js",
|
||||||
es5CoreJS: "/frontend_es5/core.js",
|
es5CoreJS: "/frontend_es5/core.js",
|
||||||
|
@@ -85,6 +85,11 @@ gulp.task("copy-translations-app", async () => {
|
|||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("copy-translations-supervisor", async () => {
|
||||||
|
const staticDir = paths.hassio_output_static;
|
||||||
|
copyTranslations(staticDir);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("copy-static-app", async () => {
|
gulp.task("copy-static-app", async () => {
|
||||||
const staticDir = paths.app_output_static;
|
const staticDir = paths.app_output_static;
|
||||||
// Basic static files
|
// Basic static files
|
||||||
|
@@ -10,6 +10,8 @@ require("./gen-icons-json.js");
|
|||||||
require("./webpack.js");
|
require("./webpack.js");
|
||||||
require("./compress.js");
|
require("./compress.js");
|
||||||
require("./rollup.js");
|
require("./rollup.js");
|
||||||
|
require("./gather-static.js");
|
||||||
|
require("./translations.js");
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"develop-hassio",
|
"develop-hassio",
|
||||||
@@ -20,6 +22,8 @@ gulp.task(
|
|||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"gen-index-hassio-dev",
|
"gen-index-hassio-dev",
|
||||||
|
"build-supervisor-translations",
|
||||||
|
"copy-translations-supervisor",
|
||||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -32,6 +36,8 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
|
"build-supervisor-translations",
|
||||||
|
"copy-translations-supervisor",
|
||||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||||
"gen-index-hassio-prod",
|
"gen-index-hassio-prod",
|
||||||
...// Don't compress running tests
|
...// Don't compress running tests
|
||||||
|
@@ -33,21 +33,10 @@ String.prototype.rsplit = function (sep, maxsplit) {
|
|||||||
: split;
|
: split;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Panel translations which should be split from the core translations. These
|
// Panel translations which should be split from the core translations.
|
||||||
// should mirror the fragment definitions in polymer.json, so that we load
|
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||||
// additional resources at equivalent points.
|
require("../../src/translations/en.json").ui.panel
|
||||||
const TRANSLATION_FRAGMENTS = [
|
);
|
||||||
"config",
|
|
||||||
"history",
|
|
||||||
"logbook",
|
|
||||||
"mailbox",
|
|
||||||
"profile",
|
|
||||||
"shopping-list",
|
|
||||||
"page-authorize",
|
|
||||||
"page-demo",
|
|
||||||
"page-onboarding",
|
|
||||||
"developer-tools",
|
|
||||||
];
|
|
||||||
|
|
||||||
function recursiveFlatten(prefix, data) {
|
function recursiveFlatten(prefix, data) {
|
||||||
let output = {};
|
let output = {};
|
||||||
@@ -277,6 +266,7 @@ gulp.task(taskName, function () {
|
|||||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||||
delete data.ui.panel[fragment];
|
delete data.ui.panel[fragment];
|
||||||
});
|
});
|
||||||
|
delete data.supervisor;
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -353,18 +343,26 @@ gulp.task(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("build-translation-fragment-supervisor", function () {
|
||||||
"build-translations",
|
return gulp
|
||||||
gulp.series(
|
.src(fullDir + "/*.json")
|
||||||
"clean-translations",
|
.pipe(transform((data) => data.supervisor))
|
||||||
"ensure-translations-build-dir",
|
.pipe(gulp.dest(workDir + "/supervisor"));
|
||||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
});
|
||||||
"build-master-translation",
|
|
||||||
"build-merged-translations",
|
gulp.task("build-translation-flatten-supervisor", function () {
|
||||||
gulp.parallel(...splitTasks),
|
return gulp
|
||||||
"build-flattened-translations",
|
.src(workDir + "/supervisor/*.json")
|
||||||
"build-translation-fingerprints",
|
.pipe(
|
||||||
function writeMetadata() {
|
transform(function (data) {
|
||||||
|
// Polymer.AppLocalizeBehavior requires flattened json
|
||||||
|
return flatten(data);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(outDir));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||||
return gulp
|
return gulp
|
||||||
.src(
|
.src(
|
||||||
[
|
[
|
||||||
@@ -380,14 +378,13 @@ gulp.task(
|
|||||||
const newData = {};
|
const newData = {};
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
// Filter out translations without native name.
|
// Filter out translations without native name.
|
||||||
if (data[key].nativeName) {
|
if (value.nativeName) {
|
||||||
newData[key] = data[key];
|
newData[key] = value;
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Skipping language ${key}. Native name was not translated.`
|
`Skipping language ${key}. Native name was not translated.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (data[key]) newData[key] = value;
|
|
||||||
});
|
});
|
||||||
return newData;
|
return newData;
|
||||||
})
|
})
|
||||||
@@ -400,6 +397,33 @@ gulp.task(
|
|||||||
)
|
)
|
||||||
.pipe(rename("translationMetadata.json"))
|
.pipe(rename("translationMetadata.json"))
|
||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(workDir));
|
||||||
}
|
});
|
||||||
|
|
||||||
|
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",
|
||||||
|
"build-translation-fingerprints",
|
||||||
|
"build-translation-write-metadata"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
"build-supervisor-translations",
|
||||||
|
gulp.series(
|
||||||
|
"clean-translations",
|
||||||
|
"ensure-translations-build-dir",
|
||||||
|
"build-master-translation",
|
||||||
|
"build-merged-translations",
|
||||||
|
"build-translation-fragment-supervisor",
|
||||||
|
"build-translation-flatten-supervisor",
|
||||||
|
"build-translation-fingerprints",
|
||||||
|
"build-translation-write-metadata"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
11
build-scripts/gulp/wds.js
Normal file
11
build-scripts/gulp/wds.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Tasks to run Rollup
|
||||||
|
const gulp = require("gulp");
|
||||||
|
const { startDevServer } = require("@web/dev-server");
|
||||||
|
|
||||||
|
gulp.task("wds-watch-app", () => {
|
||||||
|
startDevServer({
|
||||||
|
config: {
|
||||||
|
watch: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
@@ -47,7 +47,7 @@ const runDevServer = ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handler = (done) => (err, stats) => {
|
const doneHandler = (done) => (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
log.error(err.stack || err);
|
log.error(err.stack || err);
|
||||||
if (err.details) {
|
if (err.details) {
|
||||||
@@ -67,11 +67,20 @@ const handler = (done) => (err, stats) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const prodBuild = (conf) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
webpack(
|
||||||
|
conf,
|
||||||
|
// Resolve promise when done. Because we pass a callback, webpack closes itself
|
||||||
|
doneHandler(resolve)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("webpack-watch-app", () => {
|
gulp.task("webpack-watch-app", () => {
|
||||||
// we are not calling done, so this command will run forever
|
// This command will run forever because we don't close compiler
|
||||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
||||||
{ ignored: /build-translations/ },
|
{ ignored: /build-translations/ },
|
||||||
handler()
|
doneHandler()
|
||||||
);
|
);
|
||||||
gulp.watch(
|
gulp.watch(
|
||||||
path.join(paths.translations_src, "en.json"),
|
path.join(paths.translations_src, "en.json"),
|
||||||
@@ -79,14 +88,11 @@ gulp.task("webpack-watch-app", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("webpack-prod-app", () =>
|
||||||
"webpack-prod-app",
|
prodBuild(
|
||||||
() =>
|
bothBuilds(createAppConfig, {
|
||||||
new Promise((resolve) =>
|
isProdBuild: true,
|
||||||
webpack(
|
})
|
||||||
bothBuilds(createAppConfig, { isProdBuild: true }),
|
|
||||||
handler(resolve)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -98,16 +104,11 @@ gulp.task("webpack-dev-server-demo", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("webpack-prod-demo", () =>
|
||||||
"webpack-prod-demo",
|
prodBuild(
|
||||||
() =>
|
|
||||||
new Promise((resolve) =>
|
|
||||||
webpack(
|
|
||||||
bothBuilds(createDemoConfig, {
|
bothBuilds(createDemoConfig, {
|
||||||
isProdBuild: true,
|
isProdBuild: true,
|
||||||
}),
|
})
|
||||||
handler(resolve)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -121,40 +122,34 @@ gulp.task("webpack-dev-server-cast", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("webpack-prod-cast", () =>
|
||||||
"webpack-prod-cast",
|
prodBuild(
|
||||||
() =>
|
|
||||||
new Promise((resolve) =>
|
|
||||||
webpack(
|
|
||||||
bothBuilds(createCastConfig, {
|
bothBuilds(createCastConfig, {
|
||||||
isProdBuild: true,
|
isProdBuild: true,
|
||||||
}),
|
})
|
||||||
|
|
||||||
handler(resolve)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-watch-hassio", () => {
|
gulp.task("webpack-watch-hassio", () => {
|
||||||
// we are not calling done, so this command will run forever
|
// This command will run forever because we don't close compiler
|
||||||
webpack(
|
webpack(
|
||||||
createHassioConfig({
|
createHassioConfig({
|
||||||
isProdBuild: false,
|
isProdBuild: false,
|
||||||
latestBuild: true,
|
latestBuild: true,
|
||||||
})
|
})
|
||||||
).watch({}, handler());
|
).watch({ ignored: /build-translations/ }, doneHandler());
|
||||||
|
|
||||||
|
gulp.watch(
|
||||||
|
path.join(paths.translations_src, "en.json"),
|
||||||
|
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("webpack-prod-hassio", () =>
|
||||||
"webpack-prod-hassio",
|
prodBuild(
|
||||||
() =>
|
|
||||||
new Promise((resolve) =>
|
|
||||||
webpack(
|
|
||||||
bothBuilds(createHassioConfig, {
|
bothBuilds(createHassioConfig, {
|
||||||
isProdBuild: true,
|
isProdBuild: true,
|
||||||
}),
|
})
|
||||||
handler(resolve)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -167,17 +162,11 @@ gulp.task("webpack-dev-server-gallery", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("webpack-prod-gallery", () =>
|
||||||
"webpack-prod-gallery",
|
prodBuild(
|
||||||
() =>
|
|
||||||
new Promise((resolve) =>
|
|
||||||
webpack(
|
|
||||||
createGalleryConfig({
|
createGalleryConfig({
|
||||||
isProdBuild: true,
|
isProdBuild: true,
|
||||||
latestBuild: true,
|
latestBuild: true,
|
||||||
}),
|
})
|
||||||
|
|
||||||
handler(resolve)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -34,6 +34,7 @@ module.exports = {
|
|||||||
|
|
||||||
hassio_dir: path.resolve(__dirname, "../hassio"),
|
hassio_dir: path.resolve(__dirname, "../hassio"),
|
||||||
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
||||||
|
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
|
||||||
hassio_output_latest: path.resolve(
|
hassio_output_latest: path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
"../hassio/build/frontend_latest"
|
"../hassio/build/frontend_latest"
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
const path = require("path");
|
|
||||||
|
|
||||||
module.exports = function (userOptions = {}) {
|
module.exports = function (userOptions = {}) {
|
||||||
// Files need to be absolute paths.
|
// Files need to be absolute paths.
|
||||||
// This only works if the file has no exports
|
// This only works if the file has no exports
|
||||||
|
@@ -3,7 +3,7 @@ const path = require("path");
|
|||||||
const commonjs = require("@rollup/plugin-commonjs");
|
const commonjs = require("@rollup/plugin-commonjs");
|
||||||
const resolve = require("@rollup/plugin-node-resolve");
|
const resolve = require("@rollup/plugin-node-resolve");
|
||||||
const json = require("@rollup/plugin-json");
|
const json = require("@rollup/plugin-json");
|
||||||
const babel = require("rollup-plugin-babel");
|
const babel = require("@rollup/plugin-babel").babel;
|
||||||
const replace = require("@rollup/plugin-replace");
|
const replace = require("@rollup/plugin-replace");
|
||||||
const visualizer = require("rollup-plugin-visualizer");
|
const visualizer = require("rollup-plugin-visualizer");
|
||||||
const { string } = require("rollup-plugin-string");
|
const { string } = require("rollup-plugin-string");
|
||||||
@@ -31,6 +31,7 @@ const createRollupConfig = ({
|
|||||||
isStatsBuild,
|
isStatsBuild,
|
||||||
publicPath,
|
publicPath,
|
||||||
dontHash,
|
dontHash,
|
||||||
|
isWDS,
|
||||||
}) => {
|
}) => {
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
@@ -61,6 +62,7 @@ const createRollupConfig = ({
|
|||||||
...bundle.babelOptions({ latestBuild }),
|
...bundle.babelOptions({ latestBuild }),
|
||||||
extensions,
|
extensions,
|
||||||
exclude: bundle.babelExclude(),
|
exclude: bundle.babelExclude(),
|
||||||
|
babelHelpers: isWDS ? "inline" : "bundled",
|
||||||
}),
|
}),
|
||||||
string({
|
string({
|
||||||
// Import certain extensions as strings
|
// Import certain extensions as strings
|
||||||
@@ -69,19 +71,21 @@ const createRollupConfig = ({
|
|||||||
replace(
|
replace(
|
||||||
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
||||||
),
|
),
|
||||||
|
!isWDS &&
|
||||||
manifest({
|
manifest({
|
||||||
publicPath,
|
publicPath,
|
||||||
}),
|
}),
|
||||||
worker(),
|
!isWDS && worker(),
|
||||||
dontHashPlugin({ dontHash }),
|
!isWDS && dontHashPlugin({ dontHash }),
|
||||||
isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||||
|
!isWDS &&
|
||||||
isStatsBuild &&
|
isStatsBuild &&
|
||||||
visualizer({
|
visualizer({
|
||||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||||
open: true,
|
open: true,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
}),
|
}),
|
||||||
],
|
].filter(Boolean),
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @type { import("rollup").OutputOptions }
|
* @type { import("rollup").OutputOptions }
|
||||||
@@ -108,12 +112,13 @@ const createRollupConfig = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||||
return createRollupConfig(
|
return createRollupConfig(
|
||||||
bundle.config.app({
|
bundle.config.app({
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
isStatsBuild,
|
isStatsBuild,
|
||||||
|
isWDS,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,61 +0,0 @@
|
|||||||
import * as path from "path";
|
|
||||||
import * as vite from "vite";
|
|
||||||
|
|
||||||
// https://github.com/vitejs/vite/blob/master/src/node/config.ts
|
|
||||||
|
|
||||||
const ignore = new Set(["/src/resources/compatibility.ts"]);
|
|
||||||
|
|
||||||
const conf: vite.ServerConfig = {
|
|
||||||
root: path.resolve(__dirname, "../.."),
|
|
||||||
optimizeDeps: {
|
|
||||||
// We don't automatically optimize dependencies because
|
|
||||||
// that causes duplicate imports of custom elements
|
|
||||||
auto: false,
|
|
||||||
},
|
|
||||||
resolvers: [
|
|
||||||
// This resolver is meant to filter out files that we don't
|
|
||||||
// need in latest build, like compatibility.
|
|
||||||
// But resolving it to an empty file doesn't yield expected
|
|
||||||
// results.
|
|
||||||
// {
|
|
||||||
// requestToFile(publicPath: string, root: string) {
|
|
||||||
// console.log("requestToFile", {
|
|
||||||
// publicPath,
|
|
||||||
// root,
|
|
||||||
// match: ignore.has(publicPath),
|
|
||||||
// resolved:
|
|
||||||
// ignore.has(publicPath) &&
|
|
||||||
// path.resolve(conf.root!, "src/util/empty.js"),
|
|
||||||
// });
|
|
||||||
// if (ignore.has(publicPath)) {
|
|
||||||
// return path.resolve(conf.root!, "src/util/empty.js");
|
|
||||||
// }
|
|
||||||
// return undefined;
|
|
||||||
// },
|
|
||||||
// fileToRequest(filePath: string, root: string) {
|
|
||||||
// if (!filePath.endsWith("/src/util/empty.js")) {
|
|
||||||
// return undefined;
|
|
||||||
// }
|
|
||||||
// console.log("fileToRequest", {
|
|
||||||
// filePath,
|
|
||||||
// root,
|
|
||||||
// match: filePath.endsWith("/src/util/empty.js"),
|
|
||||||
// });
|
|
||||||
// return "/src/util/empty.js";
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
],
|
|
||||||
// These don't seem to be picked up. Workaround is to manually
|
|
||||||
// add them to hass_frontend/index.html for now.
|
|
||||||
define: {
|
|
||||||
__DEV__: true,
|
|
||||||
__BUILD__: "latest",
|
|
||||||
__VERSION__: "dev",
|
|
||||||
__DEMO__: false,
|
|
||||||
__BACKWARDS_COMPAT__: false,
|
|
||||||
__STATIC_PATH__: "/static/",
|
|
||||||
},
|
|
||||||
cors: true,
|
|
||||||
};
|
|
||||||
console.log(conf);
|
|
||||||
export default conf;
|
|
@@ -1,7 +1,7 @@
|
|||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||||
const paths = require("./paths.js");
|
const paths = require("./paths.js");
|
||||||
const bundle = require("./bundle");
|
const bundle = require("./bundle");
|
||||||
const log = require("fancy-log");
|
const log = require("fancy-log");
|
||||||
@@ -36,6 +36,7 @@ const createWebpackConfig = ({
|
|||||||
const ignorePackages = bundle.ignorePackages({ latestBuild });
|
const ignorePackages = bundle.ignorePackages({ latestBuild });
|
||||||
return {
|
return {
|
||||||
mode: isProdBuild ? "production" : "development",
|
mode: isProdBuild ? "production" : "development",
|
||||||
|
target: ["web", latestBuild ? "es2017" : "es5"],
|
||||||
devtool: isProdBuild
|
devtool: isProdBuild
|
||||||
? "cheap-module-source-map"
|
? "cheap-module-source-map"
|
||||||
: "eval-cheap-module-source-map",
|
: "eval-cheap-module-source-map",
|
||||||
@@ -67,7 +68,7 @@ const createWebpackConfig = ({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new ManifestPlugin({
|
new WebpackManifestPlugin({
|
||||||
// Only include the JS of entrypoints
|
// Only include the JS of entrypoints
|
||||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||||
}),
|
}),
|
||||||
@@ -131,22 +132,6 @@ const createWebpackConfig = ({
|
|||||||
}
|
}
|
||||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||||
},
|
},
|
||||||
environment: {
|
|
||||||
// The environment supports arrow functions ('() => { ... }').
|
|
||||||
arrowFunction: latestBuild,
|
|
||||||
// The environment supports BigInt as literal (123n).
|
|
||||||
bigIntLiteral: false,
|
|
||||||
// The environment supports const and let for variable declarations.
|
|
||||||
const: latestBuild,
|
|
||||||
// The environment supports destructuring ('{ a, b } = obj').
|
|
||||||
destructuring: latestBuild,
|
|
||||||
// The environment supports an async import() function to import EcmaScript modules.
|
|
||||||
dynamicImport: latestBuild,
|
|
||||||
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
|
|
||||||
forOf: latestBuild,
|
|
||||||
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
|
|
||||||
module: latestBuild,
|
|
||||||
},
|
|
||||||
chunkFilename:
|
chunkFilename:
|
||||||
isProdBuild && !isStatsBuild
|
isProdBuild && !isStatsBuild
|
||||||
? "chunk.[chunkhash].js"
|
? "chunk.[chunkhash].js"
|
||||||
|
@@ -48,7 +48,7 @@ class HcCast extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (this.lovelaceConfig === undefined) {
|
if (this.lovelaceConfig === undefined) {
|
||||||
return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `;
|
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const error =
|
const error =
|
||||||
|
@@ -98,8 +98,12 @@ class HcLayout extends LitElement {
|
|||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
padding: 24px 16px 16px;
|
padding: 24px 16px 16px;
|
||||||
display: block;
|
display: block;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
property,
|
|
||||||
internalProperty,
|
internalProperty,
|
||||||
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||||
|
@@ -94,6 +94,7 @@ class HcLovelace extends LitElement {
|
|||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import "web-animations-js/web-animations-next-lite.min";
|
import "web-animations-js/web-animations-next-lite.min";
|
||||||
import "../../../src/resources/roboto";
|
|
||||||
import "../../../src/resources/ha-style";
|
import "../../../src/resources/ha-style";
|
||||||
|
import "../../../src/resources/roboto";
|
||||||
import "./layout/hc-lovelace";
|
import "./layout/hc-lovelace";
|
||||||
|
@@ -54,6 +54,8 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
state: "21",
|
state: "21",
|
||||||
attributes: {
|
attributes: {
|
||||||
friendly_name: "Living room temperature",
|
friendly_name: "Living room temperature",
|
||||||
|
device_class: "temperature",
|
||||||
|
unit_of_measurement: "°C",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sensor.study_temp_rounded": {
|
"sensor.study_temp_rounded": {
|
||||||
@@ -61,6 +63,8 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
state: "23",
|
state: "23",
|
||||||
attributes: {
|
attributes: {
|
||||||
friendly_name: "Study temperature",
|
friendly_name: "Study temperature",
|
||||||
|
device_class: "temperature",
|
||||||
|
unit_of_measurement: "°C",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sensor.living_room": {
|
"sensor.living_room": {
|
||||||
@@ -261,7 +265,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
entity_id: "light.kitchen_lights",
|
entity_id: "light.kitchen_lights",
|
||||||
state: "off",
|
state: "off",
|
||||||
attributes: {
|
attributes: {
|
||||||
friendly_name: "Kitchen lights",
|
friendly_name: "Kitchen Lights",
|
||||||
supported_features: 1,
|
supported_features: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -484,7 +488,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
min_mireds: 111,
|
min_mireds: 111,
|
||||||
max_mireds: 400,
|
max_mireds: 400,
|
||||||
friendly_name: "Garage lights",
|
friendly_name: "Garage Lights",
|
||||||
supported_features: 55,
|
supported_features: 55,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -12,6 +12,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
{
|
{
|
||||||
type: "entities",
|
type: "entities",
|
||||||
title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"),
|
title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"),
|
||||||
|
state_color: true,
|
||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
entity: "light.kitchen_lights",
|
entity: "light.kitchen_lights",
|
||||||
|
@@ -3,22 +3,10 @@ import { Lovelace } from "../../../src/panels/lovelace/types";
|
|||||||
import { DemoConfig } from "./types";
|
import { DemoConfig } from "./types";
|
||||||
|
|
||||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||||
() =>
|
() => import("./arsaboo").then((mod) => mod.demoArsaboo),
|
||||||
import(/* webpackChunkName: "arsaboo" */ "./arsaboo").then(
|
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds),
|
||||||
(mod) => mod.demoArsaboo
|
() => import("./kernehed").then((mod) => mod.demoKernehed),
|
||||||
),
|
() => import("./jimpower").then((mod) => mod.demoJimpower),
|
||||||
() =>
|
|
||||||
import(/* webpackChunkName: "teachingbirds" */ "./teachingbirds").then(
|
|
||||||
(mod) => mod.demoTeachingbirds
|
|
||||||
),
|
|
||||||
() =>
|
|
||||||
import(/* webpackChunkName: "kernehed" */ "./kernehed").then(
|
|
||||||
(mod) => mod.demoKernehed
|
|
||||||
),
|
|
||||||
() =>
|
|
||||||
import(/* webpackChunkName: "jimpower" */ "./jimpower").then(
|
|
||||||
(mod) => mod.demoJimpower
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-mutable-exports
|
// eslint-disable-next-line import/no-mutable-exports
|
||||||
|
@@ -653,7 +653,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
|||||||
entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7",
|
entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7",
|
||||||
state: "off",
|
state: "off",
|
||||||
attributes: {
|
attributes: {
|
||||||
Density: 0,
|
density: 0,
|
||||||
battery_level: 59,
|
battery_level: 59,
|
||||||
friendly_name: "Downstairs Smoke Detector",
|
friendly_name: "Downstairs Smoke Detector",
|
||||||
device_class: "smoke",
|
device_class: "smoke",
|
||||||
@@ -663,7 +663,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
|||||||
entity_id: "binary_sensor.smoke_sensor_158d0001b8deba",
|
entity_id: "binary_sensor.smoke_sensor_158d0001b8deba",
|
||||||
state: "off",
|
state: "off",
|
||||||
attributes: {
|
attributes: {
|
||||||
Density: 0,
|
density: 0,
|
||||||
battery_level: 65,
|
battery_level: 65,
|
||||||
friendly_name: "Upstairs Smoke Detector",
|
friendly_name: "Upstairs Smoke Detector",
|
||||||
device_class: "smoke",
|
device_class: "smoke",
|
||||||
|
@@ -3,49 +3,7 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||||
name: "Kingia Castle",
|
name: "Kingia Castle",
|
||||||
resources: [
|
resources: [],
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/dark-sky-weather-card.js?v=4",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/mini-media-player-bundle.js?v=0.9.8",
|
|
||||||
// type: "module",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/tracker-card.js?v=0.1.5",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/surveillance-card.js?v=0.0.1",
|
|
||||||
// type: "module",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/mini-graph-card-bundle.js?v=0.1.0",
|
|
||||||
// type: "module",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/slider-entity-row.js?v=d6da75",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url:
|
|
||||||
// "/local/custom_ui/compact-custom-header/compact-custom-header.js?v=0.2.7",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/waze-card.js?v=1.1.1",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/circle-sensor-card.js?v=1.2.0",
|
|
||||||
// type: "module",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom_ui/monster-card.js?v=0.2.3",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
],
|
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
cards: [
|
cards: [
|
||||||
@@ -603,89 +561,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
cards: [
|
cards: [
|
||||||
// {
|
|
||||||
// style: {
|
|
||||||
// "background-image": 'url("/assets/jimpower/cardbackK.png")',
|
|
||||||
// "background-size": "100% 400px",
|
|
||||||
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
|
|
||||||
// "background-repeat": "no-repeat",
|
|
||||||
// color: "#999999",
|
|
||||||
// "border-radius": "20px",
|
|
||||||
// border: "solid 1px rgba(100,100,100,0.3)",
|
|
||||||
// "background-color": "rgba(50,50,50,0.3)",
|
|
||||||
// },
|
|
||||||
// type: "custom:card-modder",
|
|
||||||
// card: {
|
|
||||||
// entity_visibility: "sensor.dark_sky_visibility",
|
|
||||||
// entity_sun: "sun.sun",
|
|
||||||
// entity_daily_summary:
|
|
||||||
// "sensor.bom_gc_forecast_detailed_summary_0",
|
|
||||||
// entity_temperature: "sensor.bom_temp",
|
|
||||||
// entity_forecast_high_temp_3:
|
|
||||||
// "sensor.bom_gc_forecast_max_temp_c_3",
|
|
||||||
// entity_forecast_high_temp_2:
|
|
||||||
// "sensor.bom_gc_forecast_max_temp_c_2",
|
|
||||||
// entity_forecast_high_temp_5:
|
|
||||||
// "sensor.bom_gc_forecast_max_temp_c_5",
|
|
||||||
// entity_forecast_high_temp_4:
|
|
||||||
// "sensor.bom_gc_forecast_max_temp_c_4",
|
|
||||||
// entity_wind_speed: "sensor.bom_wind_sp",
|
|
||||||
// entity_forecast_icon_4: "sensor.dark_sky_icon_4",
|
|
||||||
// entity_forecast_icon_5: "sensor.dark_sky_icon_5",
|
|
||||||
// entity_forecast_icon_2: "sensor.dark_sky_icon_2",
|
|
||||||
// entity_forecast_icon_3: "sensor.dark_sky_icon_3",
|
|
||||||
// entity_forecast_icon_1: "sensor.dark_sky_icon_1",
|
|
||||||
// entity_forecast_high_temp_1:
|
|
||||||
// "sensor.bom_gc_forecast_max_temp_c_1",
|
|
||||||
// entity_wind_bearing: "sensor.bom_wind_bear",
|
|
||||||
// entity_forecast_low_temp_2:
|
|
||||||
// "sensor.bom_gc_forecast_min_temp_c_2",
|
|
||||||
// entity_forecast_low_temp_3:
|
|
||||||
// "sensor.bom_gc_forecast_min_temp_c_3",
|
|
||||||
// entity_pressure: "sensor.bom_pres",
|
|
||||||
// entity_forecast_low_temp_1:
|
|
||||||
// "sensor.bom_gc_forecast_min_temp_c_1",
|
|
||||||
// entity_forecast_low_temp_4:
|
|
||||||
// "sensor.bom_gc_forecast_min_temp_c_4",
|
|
||||||
// entity_forecast_low_temp_5:
|
|
||||||
// "sensor.bom_gc_forecast_min_temp_c_5",
|
|
||||||
// entity_humidity: "sensor.bom_humd",
|
|
||||||
// type: "custom:dark-sky-weather-card",
|
|
||||||
// entity_current_conditions: "sensor.dark_sky_icon",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// style: {
|
|
||||||
// "background-image": 'url("/assets/jimpower/home/waze_5.png")',
|
|
||||||
// "background-size": "100% 400px",
|
|
||||||
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
|
|
||||||
// "background-repeat": "no-repeat",
|
|
||||||
// "border-radius": "20px",
|
|
||||||
// border: "solid 1px rgba(100,100,100,0.3)",
|
|
||||||
// "background-color": "rgba(50,50,50,0.3)",
|
|
||||||
// },
|
|
||||||
// type: "custom:card-modder",
|
|
||||||
// card: {
|
|
||||||
// entities: [
|
|
||||||
// {
|
|
||||||
// name: "James",
|
|
||||||
// zone: "zone.home",
|
|
||||||
// entity: "sensor.james_to_home",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "Tina",
|
|
||||||
// zone: "zone.home",
|
|
||||||
// entity: "sensor.tina_to_home",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "Work",
|
|
||||||
// zone: "zone.powertec",
|
|
||||||
// entity: "sensor.commute_to_work",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// type: "custom:waze-card",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
"border-radius": "20px",
|
"border-radius": "20px",
|
||||||
@@ -722,46 +597,8 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
],
|
],
|
||||||
type: "vertical-stack",
|
type: "vertical-stack",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// cards: [
|
|
||||||
// {
|
|
||||||
// style: {
|
|
||||||
// "border-radius": "20px",
|
|
||||||
// color: "#999999",
|
|
||||||
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
|
|
||||||
// border: "solid 1px rgba(100,100,100,0.3)",
|
|
||||||
// },
|
|
||||||
// type: "custom:card-modder",
|
|
||||||
// card: {
|
|
||||||
// type: "picture-entity",
|
|
||||||
// entity: "camera.bom_radar",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// // {
|
|
||||||
// // style: {
|
|
||||||
// // "background-image": 'url("/assets/jimpower/cardbackK.png")',
|
|
||||||
// // "background-size": "100% 525px",
|
|
||||||
// // "box-shadow": "3px 3px rgba(0,0,0,0.4)",
|
|
||||||
// // "background-repeat": "no-repeat",
|
|
||||||
// // color: "#999999",
|
|
||||||
// // "border-radius": "20px",
|
|
||||||
// // border: "solid 1px rgba(100,100,100,0.3)",
|
|
||||||
// // "background-color": "rgba(50,50,50,0.3)",
|
|
||||||
// // },
|
|
||||||
// // type: "custom:card-modder",
|
|
||||||
// // card: {
|
|
||||||
// // title: null,
|
|
||||||
// // type: "custom:tracker-card",
|
|
||||||
// // trackers: [
|
|
||||||
// // "sensor.custom_card_tracker",
|
|
||||||
// // "sensor.custom_component_tracker",
|
|
||||||
// // ],
|
|
||||||
// // },
|
|
||||||
// // },
|
|
||||||
// ],
|
|
||||||
// type: "vertical-stack",
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
|
path: "home",
|
||||||
icon: "mdi:castle",
|
icon: "mdi:castle",
|
||||||
name: "Home",
|
name: "Home",
|
||||||
background:
|
background:
|
||||||
@@ -881,26 +718,13 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
card: {
|
card: {
|
||||||
image: "/assets/jimpower/security/air_8.jpg",
|
image: "/assets/jimpower/security/air_8.jpg",
|
||||||
elements: [
|
elements: [
|
||||||
{
|
|
||||||
image:
|
|
||||||
"https://www.airvisual.com/assets/aqi/ic-face-1-green.svg",
|
|
||||||
type: "image",
|
|
||||||
style: {
|
|
||||||
width: "80px",
|
|
||||||
top: "30%",
|
|
||||||
left: "12%",
|
|
||||||
transform: "none",
|
|
||||||
height: "80px",
|
|
||||||
},
|
|
||||||
entity: "sensor.us_air_pollution_level_2",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
color: "hsl(120, 41%, 39%)",
|
color: "hsl(120, 41%, 39%)",
|
||||||
top: "50%",
|
top: "50%",
|
||||||
"font-weight": 600,
|
"font-weight": 600,
|
||||||
"font-size": "20px",
|
"font-size": "50px",
|
||||||
left: "44%",
|
left: "30%",
|
||||||
},
|
},
|
||||||
type: "state-label",
|
type: "state-label",
|
||||||
entity: "sensor.us_air_pollution_level_2",
|
entity: "sensor.us_air_pollution_level_2",
|
||||||
@@ -920,7 +744,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
top: "80%",
|
top: "80%",
|
||||||
left: "52%",
|
left: "48%",
|
||||||
},
|
},
|
||||||
type: "state-icon",
|
type: "state-icon",
|
||||||
entity: "sensor.us_main_pollutant_2",
|
entity: "sensor.us_main_pollutant_2",
|
||||||
@@ -1411,6 +1235,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
type: "vertical-stack",
|
type: "vertical-stack",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
path: "security",
|
||||||
icon: "hass:shield-home",
|
icon: "hass:shield-home",
|
||||||
name: "Security",
|
name: "Security",
|
||||||
background:
|
background:
|
||||||
|
@@ -101,7 +101,12 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
"sensor.zwave_battery_front_door": {
|
"sensor.zwave_battery_front_door": {
|
||||||
entity_id: "sensor.zwave_battery_front_door",
|
entity_id: "sensor.zwave_battery_front_door",
|
||||||
state: "63",
|
state: "63",
|
||||||
attributes: { friendly_name: "Battery", icon: "mdi:battery-60" },
|
attributes: {
|
||||||
|
friendly_name: "Battery",
|
||||||
|
icon: "mdi:battery-60",
|
||||||
|
unit_of_measurement: "%",
|
||||||
|
device_class: "battery",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"sensor.oskar_devices": {
|
"sensor.oskar_devices": {
|
||||||
entity_id: "sensor.oskar_devices",
|
entity_id: "sensor.oskar_devices",
|
||||||
@@ -164,7 +169,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
},
|
},
|
||||||
"input_select.christmas_pattern": {
|
"input_select.christmas_pattern": {
|
||||||
entity_id: "input_select.christmas_pattern",
|
entity_id: "input_select.christmas_pattern",
|
||||||
state: "None",
|
state: "Rainbow",
|
||||||
attributes: {
|
attributes: {
|
||||||
options: [
|
options: [
|
||||||
"None",
|
"None",
|
||||||
@@ -186,7 +191,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
},
|
},
|
||||||
"input_select.christmas_palette": {
|
"input_select.christmas_palette": {
|
||||||
entity_id: "input_select.christmas_palette",
|
entity_id: "input_select.christmas_palette",
|
||||||
state: "None",
|
state: "Party",
|
||||||
attributes: {
|
attributes: {
|
||||||
options: [
|
options: [
|
||||||
"None",
|
"None",
|
||||||
@@ -457,7 +462,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
state: "0.0",
|
state: "0.0",
|
||||||
attributes: {
|
attributes: {
|
||||||
unit_of_measurement: "kB/s",
|
unit_of_measurement: "kB/s",
|
||||||
friendly_name: "Nedladdning",
|
friendly_name: "Downloading",
|
||||||
icon: "mdi:file-download",
|
icon: "mdi:file-download",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -471,7 +476,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
state: "0.0",
|
state: "0.0",
|
||||||
attributes: {
|
attributes: {
|
||||||
unit_of_measurement: "kB/s",
|
unit_of_measurement: "kB/s",
|
||||||
friendly_name: "Uppladdning",
|
friendly_name: "Uploading",
|
||||||
icon: "mdi:file-upload",
|
icon: "mdi:file-upload",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -2,44 +2,7 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||||
name: "Hem",
|
name: "Hem",
|
||||||
resources: [
|
resources: [],
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/monster-card.js",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/mini-media-player-bundle.js?v=0.9.8",
|
|
||||||
// type: "module",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/slideshow-card.js?=1.1.0",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/fold-entity-row.js?v=3ae2c4",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/swipe-card/swipe-card.js?v=2.0.0",
|
|
||||||
// type: "module",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/upcoming-media-card/upcoming-media-card.js",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/tracker-card.js?v=0.1.5",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/card-tools.js?v=6ce5d0",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// url: "/local/custom-lovelace/krisinfo.js?=0.0.1",
|
|
||||||
// type: "js",
|
|
||||||
// },
|
|
||||||
],
|
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
cards: [
|
cards: [
|
||||||
@@ -64,7 +27,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
top: "93%",
|
top: "93%",
|
||||||
left: "90%",
|
left: "85%",
|
||||||
},
|
},
|
||||||
type: "state-label",
|
type: "state-label",
|
||||||
entity: "sensor.battery_oskar",
|
entity: "sensor.battery_oskar",
|
||||||
@@ -87,7 +50,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
top: "92%",
|
top: "93%",
|
||||||
left: "20%",
|
left: "20%",
|
||||||
},
|
},
|
||||||
type: "state-label",
|
type: "state-label",
|
||||||
@@ -96,8 +59,8 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
top: "92%",
|
top: "93%",
|
||||||
left: "90%",
|
left: "85%",
|
||||||
},
|
},
|
||||||
type: "state-label",
|
type: "state-label",
|
||||||
entity: "sensor.battery_bella",
|
entity: "sensor.battery_bella",
|
||||||
@@ -105,7 +68,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
{
|
{
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
top: "92%",
|
top: "93%",
|
||||||
left: "55%",
|
left: "55%",
|
||||||
},
|
},
|
||||||
type: "state-label",
|
type: "state-label",
|
||||||
@@ -131,78 +94,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
type: "entities",
|
type: "entities",
|
||||||
title: "Lock",
|
title: "Lock",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// filter: {
|
|
||||||
// exclude: [
|
|
||||||
// {
|
|
||||||
// state: "not_home",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// include: [
|
|
||||||
// {
|
|
||||||
// entity_id: "device_tracker.annasiphone",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "device_tracker.iphone_2",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// type: "custom:monster-card",
|
|
||||||
// card: {
|
|
||||||
// show_header_toggle: false,
|
|
||||||
// type: "entities",
|
|
||||||
// title: "G\u00e4ster",
|
|
||||||
// },
|
|
||||||
// show_empty: false,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// filter: {
|
|
||||||
// exclude: [
|
|
||||||
// {
|
|
||||||
// state: "Inget",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// state: "i.u.",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// include: [
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_al",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_alm",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_salg_vide",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_bjork",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_bok",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_ek",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_grabo",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_gras",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id: "sensor.pollen_hassel",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// type: "custom:monster-card",
|
|
||||||
// card: {
|
|
||||||
// show_header_toggle: false,
|
|
||||||
// type: "entities",
|
|
||||||
// title: "Pollenniv\u00e5er",
|
|
||||||
// },
|
|
||||||
// show_empty: false,
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
cards: [
|
cards: [
|
||||||
{
|
{
|
||||||
@@ -226,10 +117,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
],
|
],
|
||||||
type: "vertical-stack",
|
type: "vertical-stack",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// url: "https://embed.windy.com/embed2.html",
|
|
||||||
// type: "iframe",
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
@@ -263,6 +150,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
],
|
],
|
||||||
type: "glance",
|
type: "glance",
|
||||||
show_state: false,
|
show_state: false,
|
||||||
|
columns: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
entities: ["sensor.oskar_bluetooth"],
|
entities: ["sensor.oskar_bluetooth"],
|
||||||
@@ -270,32 +158,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
type: "entities",
|
type: "entities",
|
||||||
title: "Occupancy",
|
title: "Occupancy",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// filter: {
|
|
||||||
// exclude: [
|
|
||||||
// {
|
|
||||||
// state: false,
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// include: [
|
|
||||||
// {
|
|
||||||
// entity_id:
|
|
||||||
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_2",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// entity_id:
|
|
||||||
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_3",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// type: "custom:monster-card",
|
|
||||||
// card: {
|
|
||||||
// show_header_toggle: false,
|
|
||||||
// type: "entities",
|
|
||||||
// title: "Brandvarnare",
|
|
||||||
// },
|
|
||||||
// show_empty: false,
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
type: "weather-forecast",
|
type: "weather-forecast",
|
||||||
entity: "weather.smhi_vader",
|
entity: "weather.smhi_vader",
|
||||||
@@ -378,41 +240,9 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
"binary_sensor.windows_server",
|
"binary_sensor.windows_server",
|
||||||
"binary_sensor.teamspeak",
|
"binary_sensor.teamspeak",
|
||||||
"binary_sensor.harmony_hub",
|
"binary_sensor.harmony_hub",
|
||||||
// {
|
|
||||||
// style: {
|
|
||||||
// height: "1px",
|
|
||||||
// width: "85%",
|
|
||||||
// "margin-left": "auto",
|
|
||||||
// background: "#62717b",
|
|
||||||
// "margin-right": "auto",
|
|
||||||
// },
|
|
||||||
// type: "divider",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// items: ["sensor.uptime_router", "sensor.installerad_routeros"],
|
|
||||||
// head: {
|
|
||||||
// entity: "binary_sensor.router",
|
|
||||||
// },
|
|
||||||
// type: "custom:fold-entity-row",
|
|
||||||
// group_config: {
|
|
||||||
// icon: "mdi:router",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// items: [
|
|
||||||
// "sensor.uptime_router_server",
|
|
||||||
// "sensor.installerad_routeros_server",
|
|
||||||
// ],
|
|
||||||
// head: {
|
|
||||||
// entity: "binary_sensor.router_server",
|
|
||||||
// },
|
|
||||||
// type: "custom:fold-entity-row",
|
|
||||||
// group_config: {
|
|
||||||
// icon: "mdi:router",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
show_header_toggle: false,
|
show_header_toggle: false,
|
||||||
|
state_color: true,
|
||||||
type: "entities",
|
type: "entities",
|
||||||
title: "Network",
|
title: "Network",
|
||||||
},
|
},
|
||||||
@@ -422,29 +252,10 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
|||||||
"binary_sensor.ubiquiti_switch",
|
"binary_sensor.ubiquiti_switch",
|
||||||
"binary_sensor.ubiquiti_nvr",
|
"binary_sensor.ubiquiti_nvr",
|
||||||
"binary_sensor.entre_kamera",
|
"binary_sensor.entre_kamera",
|
||||||
// {
|
|
||||||
// items: ["sensor.uptime_ap_1"],
|
|
||||||
// head: {
|
|
||||||
// entity: "binary_sensor.accesspunkt_1",
|
|
||||||
// },
|
|
||||||
// type: "custom:fold-entity-row",
|
|
||||||
// group_config: {
|
|
||||||
// icon: "router-wireless",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// items: ["sensor.uptime_ap_2"],
|
|
||||||
// head: {
|
|
||||||
// entity: "binary_sensor.accesspunkt_2",
|
|
||||||
// },
|
|
||||||
// type: "custom:fold-entity-row",
|
|
||||||
// group_config: {
|
|
||||||
// icon: "router-wireless",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
"sensor.total_clients_wireless",
|
"sensor.total_clients_wireless",
|
||||||
],
|
],
|
||||||
show_header_toggle: false,
|
show_header_toggle: false,
|
||||||
|
state_color: true,
|
||||||
type: "entities",
|
type: "entities",
|
||||||
title: "Ubiquiti",
|
title: "Ubiquiti",
|
||||||
},
|
},
|
||||||
|
@@ -215,6 +215,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
card: {
|
card: {
|
||||||
type: "glance",
|
type: "glance",
|
||||||
show_state: false,
|
show_state: false,
|
||||||
|
columns: 4,
|
||||||
},
|
},
|
||||||
state_filter: ["on"],
|
state_filter: ["on"],
|
||||||
},
|
},
|
||||||
@@ -808,67 +809,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
],
|
],
|
||||||
type: "vertical-stack",
|
type: "vertical-stack",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// cards: [
|
|
||||||
// {
|
|
||||||
// entities: [
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: true,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Bedside",
|
|
||||||
// entity: "light.bedside_lamp",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: true,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Bedroom",
|
|
||||||
// entity: "light.bedroom_ceiling_light",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: true,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Isa",
|
|
||||||
// entity: "light.isa_ceiling_light",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: true,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Upstairs hallway",
|
|
||||||
// entity: "light.upstairs_hallway_ceiling_light_level",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: true,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Nightlight",
|
|
||||||
// entity: "light.gateway_light_34ce008bfc4b",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: true,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Walk in closet",
|
|
||||||
// entity: "light.walk_in_closet_lights",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// hide_when_off: true,
|
|
||||||
// toggle: false,
|
|
||||||
// type: "custom:slider-entity-row",
|
|
||||||
// name: "Stefan",
|
|
||||||
// entity: "light.stefan_lightstrip",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// show_header_toggle: false,
|
|
||||||
// type: "entities",
|
|
||||||
// title: "Upstairs",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// type: "vertical-stack",
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
path: "lights",
|
path: "lights",
|
||||||
title: "Lights",
|
title: "Lights",
|
||||||
@@ -918,10 +858,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
name: "Dafang",
|
name: "Dafang",
|
||||||
icon: "mdi:webcam",
|
icon: "mdi:webcam",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "IR Hallway",
|
|
||||||
entity: "sensor.system_ir_blaster",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "IR Bedroom",
|
name: "IR Bedroom",
|
||||||
entity: "sensor.system_ir_blaster_bedroom",
|
entity: "sensor.system_ir_blaster_bedroom",
|
||||||
@@ -940,7 +876,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
"sensor.system_ring_chime",
|
"sensor.system_ring_chime",
|
||||||
],
|
],
|
||||||
type: "glance",
|
type: "glance",
|
||||||
columns: 5,
|
columns: 4,
|
||||||
show_state: false,
|
show_state: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -9,5 +9,5 @@ export interface DemoConfig {
|
|||||||
authorUrl: string;
|
authorUrl: string;
|
||||||
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
|
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
|
||||||
entities: (localize: LocalizeFunc) => Entity[];
|
entities: (localize: LocalizeFunc) => Entity[];
|
||||||
theme: () => { [key: string]: string } | null;
|
theme: () => Record<string, string> | null;
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,8 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
LitElement,
|
|
||||||
internalProperty,
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { CastManager } from "../../../src/cast/cast_manager";
|
import { CastManager } from "../../../src/cast/cast_manager";
|
||||||
|
@@ -3,9 +3,9 @@ import {
|
|||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
|
||||||
import "@polymer/polymer/lib/elements/dom-if";
|
import "@polymer/polymer/lib/elements/dom-if";
|
||||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
import "@polymer/polymer/lib/elements/dom-repeat";
|
||||||
import "../../src/resources/ha-style";
|
import "../../src/resources/ha-style";
|
||||||
import "../../src/resources/roboto";
|
import "../../src/resources/roboto";
|
||||||
|
import "../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "./ha-demo";
|
import "./ha-demo";
|
||||||
|
|
||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
import(
|
import("web-animations-js/web-animations-next-lite.min");
|
||||||
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
|
||||||
);
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
// Compat needs to be first import
|
||||||
import "../../src/resources/compatibility";
|
import "../../src/resources/compatibility";
|
||||||
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
||||||
import { navigate } from "../../src/common/navigate";
|
import { navigate } from "../../src/common/navigate";
|
||||||
|
@@ -21,15 +21,16 @@ class DemoCard extends PolymerElement {
|
|||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
margin: 16px;
|
margin: 0 16px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
@media only screen and (max-width: 800px) {
|
@media only screen and (max-width: 800px) {
|
||||||
.root {
|
.root {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
margin-left: 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -2,10 +2,10 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
/* eslint-plugin-disable lit */
|
/* eslint-plugin-disable lit */
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "../../../src/components/ha-switch";
|
|
||||||
import "../../../src/components/ha-formfield";
|
|
||||||
import "./demo-card";
|
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_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 {
|
class DemoCards extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
@@ -2,43 +2,45 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|||||||
/* eslint-plugin-disable lit */
|
/* eslint-plugin-disable lit */
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/state-summary/state-card-content";
|
|
||||||
import "../../../src/dialogs/more-info/more-info-content";
|
import "../../../src/dialogs/more-info/more-info-content";
|
||||||
|
import "../../../src/state-summary/state-card-content";
|
||||||
|
|
||||||
class DemoMoreInfo extends PolymerElement {
|
class DemoMoreInfo extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: start;
|
|
||||||
}
|
}
|
||||||
|
#card {
|
||||||
|
max-width: 400px;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
width: 333px;
|
width: 352px;
|
||||||
padding: 20px 24px;
|
padding: 20px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
state-card-content {
|
state-card-content {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
margin: 16px;
|
margin: 0 16px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 800px) {
|
@media only screen and (max-width: 800px) {
|
||||||
:host {
|
.root {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
margin-left: 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<div class="root">
|
||||||
|
<div id="card">
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<state-card-content
|
<state-card-content
|
||||||
state-obj="[[_stateObj]]"
|
state-obj="[[_stateObj]]"
|
||||||
@@ -51,9 +53,11 @@ class DemoMoreInfo extends PolymerElement {
|
|||||||
state-obj="[[_stateObj]]"
|
state-obj="[[_stateObj]]"
|
||||||
></more-info-content>
|
></more-info-content>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
</div>
|
||||||
<template is="dom-if" if="[[showConfig]]">
|
<template is="dom-if" if="[[showConfig]]">
|
||||||
<pre>[[_jsonEntity(_stateObj)]]</pre>
|
<pre>[[_jsonEntity(_stateObj)]]</pre>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,6 +2,8 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
/* eslint-plugin-disable lit */
|
/* eslint-plugin-disable lit */
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
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 "../../../src/components/ha-switch";
|
||||||
import "./demo-more-info";
|
import "./demo-more-info";
|
||||||
|
|
||||||
@@ -9,6 +11,10 @@ class DemoMoreInfos extends PolymerElement {
|
|||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
|
#container {
|
||||||
|
min-height: calc(100vh - 128px);
|
||||||
|
background: var(--primary-background-color);
|
||||||
|
}
|
||||||
.cards {
|
.cards {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -23,12 +29,22 @@ class DemoMoreInfos extends PolymerElement {
|
|||||||
.filters {
|
.filters {
|
||||||
margin-left: 60px;
|
margin-left: 60px;
|
||||||
}
|
}
|
||||||
|
ha-formfield {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<ha-switch checked="{{_showConfig}}">Show entity</ha-switch>
|
<ha-formfield label="Show entities">
|
||||||
|
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
||||||
|
</ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
<ha-formfield label="Dark theme">
|
||||||
|
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
</div>
|
</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
<div id="container">
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
<template is="dom-repeat" items="[[entities]]">
|
<template is="dom-repeat" items="[[entities]]">
|
||||||
<demo-more-info
|
<demo-more-info
|
||||||
@@ -38,6 +54,7 @@ class DemoMoreInfos extends PolymerElement {
|
|||||||
></demo-more-info>
|
></demo-more-info>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +68,16 @@ class DemoMoreInfos extends PolymerElement {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showConfigToggled(ev) {
|
||||||
|
this._showConfig = ev.target.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
_darkThemeToggled(ev) {
|
||||||
|
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
|
||||||
|
dark: ev.target.checked,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-more-infos", DemoMoreInfos);
|
customElements.define("demo-more-infos", DemoMoreInfos);
|
||||||
|
@@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [
|
|||||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||||
media_artist: "Technohead",
|
media_artist: "Technohead",
|
||||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
|
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||||
supported_features: 195135,
|
supported_features: 64063,
|
||||||
entity_picture: "/images/album_cover_2.jpg",
|
entity_picture: "/images/album_cover_2.jpg",
|
||||||
media_duration: 300,
|
media_duration: 300,
|
||||||
media_position: 50,
|
media_position: 50,
|
||||||
@@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [
|
|||||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||||
media_artist: "Technohead",
|
media_artist: "Technohead",
|
||||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
|
||||||
supported_features: 64063,
|
supported_features: 195135,
|
||||||
entity_picture: "/images/album_cover.jpg",
|
entity_picture: "/images/album_cover.jpg",
|
||||||
media_duration: 300,
|
media_duration: 300,
|
||||||
media_position: 0,
|
media_position: 0,
|
||||||
|
72
gallery/src/data/plants.ts
Normal file
72
gallery/src/data/plants.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
|
|
||||||
|
export const createPlantEntities = () => [
|
||||||
|
getEntity("plant", "lemon_tree", "ok", {
|
||||||
|
problem: "none",
|
||||||
|
sensors: {
|
||||||
|
moisture: "sensor.lemon_tree_moisture",
|
||||||
|
battery: "sensor.lemon_tree_battery",
|
||||||
|
temperature: "sensor.lemon_tree_temperature",
|
||||||
|
conductivity: "sensor.lemon_tree_conductivity",
|
||||||
|
brightness: "sensor.lemon_tree_brightness",
|
||||||
|
},
|
||||||
|
unit_of_measurement_dict: {
|
||||||
|
temperature: "°C",
|
||||||
|
moisture: "%",
|
||||||
|
brightness: "lx",
|
||||||
|
battery: "%",
|
||||||
|
conductivity: "μS/cm",
|
||||||
|
},
|
||||||
|
moisture: 54,
|
||||||
|
battery: 95,
|
||||||
|
temperature: 15.6,
|
||||||
|
conductivity: 1,
|
||||||
|
brightness: 12,
|
||||||
|
max_brightness: 20,
|
||||||
|
friendly_name: "Lemon Tree",
|
||||||
|
}),
|
||||||
|
getEntity("plant", "apple_tree", "ok", {
|
||||||
|
problem: "brightness",
|
||||||
|
sensors: {
|
||||||
|
moisture: "sensor.apple_tree_moisture",
|
||||||
|
battery: "sensor.apple_tree_battery",
|
||||||
|
temperature: "sensor.apple_tree_temperature",
|
||||||
|
conductivity: "sensor.apple_tree_conductivity",
|
||||||
|
brightness: "sensor.apple_tree_brightness",
|
||||||
|
},
|
||||||
|
unit_of_measurement_dict: {
|
||||||
|
temperature: "°C",
|
||||||
|
moisture: "%",
|
||||||
|
brightness: "lx",
|
||||||
|
battery: "%",
|
||||||
|
conductivity: "μS/cm",
|
||||||
|
},
|
||||||
|
moisture: 54,
|
||||||
|
battery: 2,
|
||||||
|
temperature: 15.6,
|
||||||
|
conductivity: 1,
|
||||||
|
brightness: 25,
|
||||||
|
max_brightness: 20,
|
||||||
|
friendly_name: "Apple Tree",
|
||||||
|
}),
|
||||||
|
getEntity("plant", "sunflowers", "ok", {
|
||||||
|
problem: "moisture, temperature, conductivity",
|
||||||
|
sensors: {
|
||||||
|
moisture: "sensor.sunflowers_moisture",
|
||||||
|
temperature: "sensor.sunflowers_temperature",
|
||||||
|
conductivity: "sensor.sunflowers_conductivity",
|
||||||
|
brightness: "sensor.sunflowers_brightness",
|
||||||
|
},
|
||||||
|
unit_of_measurement_dict: {
|
||||||
|
temperature: "°C",
|
||||||
|
moisture: "%",
|
||||||
|
brightness: "lx",
|
||||||
|
conductivity: "μS/cm",
|
||||||
|
},
|
||||||
|
moisture: 54,
|
||||||
|
temperature: 15.6,
|
||||||
|
conductivity: 1,
|
||||||
|
brightness: 25,
|
||||||
|
entity_picture: "/images/sunflowers.jpg",
|
||||||
|
}),
|
||||||
|
];
|
271
gallery/src/data/traces/basic_trace.ts
Normal file
271
gallery/src/data/traces/basic_trace.ts
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
import { DemoTrace } from "./types";
|
||||||
|
|
||||||
|
export const basicTrace: DemoTrace = {
|
||||||
|
trace: {
|
||||||
|
last_action: "action/0/choose/0/sequence/0",
|
||||||
|
last_condition: "condition/0",
|
||||||
|
run_id: "0",
|
||||||
|
state: "stopped",
|
||||||
|
timestamp: {
|
||||||
|
start: "2021-03-22T19:17:09.519178+00:00",
|
||||||
|
finish: "2021-03-22T19:17:09.556129+00:00",
|
||||||
|
},
|
||||||
|
trigger: "state of input_boolean.toggle_1",
|
||||||
|
domain: "automation",
|
||||||
|
item_id: "1615419646544",
|
||||||
|
action_trace: {
|
||||||
|
"action/0": [
|
||||||
|
{
|
||||||
|
path: "action/0",
|
||||||
|
timestamp: "2021-03-22T19:17:09.526794+00:00",
|
||||||
|
changed_variables: {
|
||||||
|
trigger: {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
from_state: {
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
editable: true,
|
||||||
|
friendly_name: "Toggle 1",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-22T19:11:24.418709+00:00",
|
||||||
|
last_updated: "2021-03-22T19:11:24.418709+00:00",
|
||||||
|
context: {
|
||||||
|
id: "55daa6c47a7613b0800fe0ec81090a84",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_state: {
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
state: "off",
|
||||||
|
attributes: {
|
||||||
|
editable: true,
|
||||||
|
friendly_name: "Toggle 1",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-22T19:17:09.516874+00:00",
|
||||||
|
last_updated: "2021-03-22T19:17:09.516874+00:00",
|
||||||
|
context: {
|
||||||
|
id: "116d7a6562d594b114f7efe728619a3f",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
for: null,
|
||||||
|
attribute: null,
|
||||||
|
description: "state of input_boolean.toggle_1",
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
id: "54a7371cff31be0f4010c9fde2317322",
|
||||||
|
parent_id: "116d7a6562d594b114f7efe728619a3f",
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
choice: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"action/0/choose/0": [
|
||||||
|
{
|
||||||
|
path: "action/0/choose/0",
|
||||||
|
timestamp: "2021-03-22T19:17:09.530176+00:00",
|
||||||
|
result: {
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"action/0/choose/0/conditions/0": [
|
||||||
|
{
|
||||||
|
path: "action/0/choose/0/conditions/0",
|
||||||
|
timestamp: "2021-03-22T19:17:09.539155+00:00",
|
||||||
|
result: {
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"action/0/choose/0/sequence/0": [
|
||||||
|
{
|
||||||
|
path: "action/0/choose/0/sequence/0",
|
||||||
|
timestamp: "2021-03-22T19:17:09.542769+00:00",
|
||||||
|
result: {
|
||||||
|
params: {
|
||||||
|
domain: "input_boolean",
|
||||||
|
service: "toggle",
|
||||||
|
service_data: {},
|
||||||
|
target: {
|
||||||
|
entity_id: ["input_boolean.toggle_2"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
running_script: false,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
condition_trace: {
|
||||||
|
"condition/0": [
|
||||||
|
{
|
||||||
|
path: "condition/0",
|
||||||
|
timestamp: "2021-03-22T19:17:09.520267+00:00",
|
||||||
|
changed_variables: {
|
||||||
|
trigger: {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
from_state: {
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
editable: true,
|
||||||
|
friendly_name: "Toggle 1",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-22T19:11:24.418709+00:00",
|
||||||
|
last_updated: "2021-03-22T19:11:24.418709+00:00",
|
||||||
|
context: {
|
||||||
|
id: "55daa6c47a7613b0800fe0ec81090a84",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_state: {
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
state: "off",
|
||||||
|
attributes: {
|
||||||
|
editable: true,
|
||||||
|
friendly_name: "Toggle 1",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-22T19:17:09.516874+00:00",
|
||||||
|
last_updated: "2021-03-22T19:17:09.516874+00:00",
|
||||||
|
context: {
|
||||||
|
id: "116d7a6562d594b114f7efe728619a3f",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
for: null,
|
||||||
|
attribute: null,
|
||||||
|
description: "state of input_boolean.toggle_1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
id: "1615419646544",
|
||||||
|
alias: "Ensure Party mode",
|
||||||
|
description: "",
|
||||||
|
trigger: [
|
||||||
|
{
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
condition: [
|
||||||
|
{
|
||||||
|
condition: "template",
|
||||||
|
alias: "Test if Paulus is home",
|
||||||
|
value_template: "{{ true }}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
action: [
|
||||||
|
{
|
||||||
|
choose: [
|
||||||
|
{
|
||||||
|
alias: "If toggle 3 is on",
|
||||||
|
conditions: "{{ is_state('input_boolean.toggle_3', 'on') }}",
|
||||||
|
sequence: [
|
||||||
|
{
|
||||||
|
service: "input_boolean.toggle",
|
||||||
|
alias: "Toggle 2 while 3 is on",
|
||||||
|
target: {
|
||||||
|
entity_id: "input_boolean.toggle_2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: [
|
||||||
|
{
|
||||||
|
service: "input_boolean.toggle",
|
||||||
|
alias: "Toggle 2",
|
||||||
|
target: {
|
||||||
|
entity_id: "input_boolean.toggle_2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mode: "single",
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
id: "54a7371cff31be0f4010c9fde2317322",
|
||||||
|
parent_id: "116d7a6562d594b114f7efe728619a3f",
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
variables: {
|
||||||
|
trigger: {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
from_state: {
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
editable: true,
|
||||||
|
friendly_name: "Toggle 1",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-22T19:11:24.418709+00:00",
|
||||||
|
last_updated: "2021-03-22T19:11:24.418709+00:00",
|
||||||
|
context: {
|
||||||
|
id: "55daa6c47a7613b0800fe0ec81090a84",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_state: {
|
||||||
|
entity_id: "input_boolean.toggle_1",
|
||||||
|
state: "off",
|
||||||
|
attributes: {
|
||||||
|
editable: true,
|
||||||
|
friendly_name: "Toggle 1",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-22T19:17:09.516874+00:00",
|
||||||
|
last_updated: "2021-03-22T19:17:09.516874+00:00",
|
||||||
|
context: {
|
||||||
|
id: "116d7a6562d594b114f7efe728619a3f",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: "d1b4e89da01445fa8bc98e39fac477ca",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
for: null,
|
||||||
|
attribute: null,
|
||||||
|
description: "state of input_boolean.toggle_1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
logbookEntries: [
|
||||||
|
{
|
||||||
|
name: "Ensure Party mode",
|
||||||
|
message: "has been triggered by state of input_boolean.toggle_1",
|
||||||
|
source: "state of input_boolean.toggle_1",
|
||||||
|
entity_id: "automation.toggle_toggles",
|
||||||
|
context_id: "54a7371cff31be0f4010c9fde2317322",
|
||||||
|
when: "2021-03-22T19:17:09.523041+00:00",
|
||||||
|
domain: "automation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
when: "2021-03-22T19:17:09.549346+00:00",
|
||||||
|
name: "Toggle 2",
|
||||||
|
state: "on",
|
||||||
|
entity_id: "input_boolean.toggle_2",
|
||||||
|
context_entity_id: "automation.toggle_toggles",
|
||||||
|
context_entity_id_name: "Ensure Party mode",
|
||||||
|
context_event_type: "automation_triggered",
|
||||||
|
context_domain: "automation",
|
||||||
|
context_name: "Ensure Party mode",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
248
gallery/src/data/traces/motion-light-trace.ts
Normal file
248
gallery/src/data/traces/motion-light-trace.ts
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import { DemoTrace } from "./types";
|
||||||
|
|
||||||
|
export const motionLightTrace: DemoTrace = {
|
||||||
|
trace: {
|
||||||
|
last_action: "action/3",
|
||||||
|
last_condition: null,
|
||||||
|
run_id: "1",
|
||||||
|
state: "stopped",
|
||||||
|
timestamp: {
|
||||||
|
start: "2021-03-14T06:07:01.768006+00:00",
|
||||||
|
finish: "2021-03-14T06:07:53.287525+00:00",
|
||||||
|
},
|
||||||
|
trigger: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
domain: "automation",
|
||||||
|
item_id: "1614732497392",
|
||||||
|
action_trace: {
|
||||||
|
"action/0": [
|
||||||
|
{
|
||||||
|
path: "action/0",
|
||||||
|
timestamp: "2021-03-14T06:07:01.771038+00:00",
|
||||||
|
changed_variables: {
|
||||||
|
trigger: {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
from_state: {
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
state: "off",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||||
|
icon: "mdi:camera-off",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-14T06:06:29.235325+00:00",
|
||||||
|
last_updated: "2021-03-14T06:06:29.235325+00:00",
|
||||||
|
context: {
|
||||||
|
id: "ad4864c5ce957c38a07b50378eeb245d",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_state: {
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||||
|
icon: "mdi:camera",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||||
|
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||||
|
context: {
|
||||||
|
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
for: null,
|
||||||
|
attribute: null,
|
||||||
|
description:
|
||||||
|
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
id: "43b6ee9293a551c5cc14e8eb60af54ba",
|
||||||
|
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"action/1": [
|
||||||
|
{ path: "action/1", timestamp: "2021-03-14T06:07:01.875316+00:00" },
|
||||||
|
],
|
||||||
|
"action/2": [
|
||||||
|
{
|
||||||
|
path: "action/2",
|
||||||
|
timestamp: "2021-03-14T06:07:53.195013+00:00",
|
||||||
|
changed_variables: {
|
||||||
|
wait: {
|
||||||
|
remaining: null,
|
||||||
|
trigger: {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
from_state: {
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||||
|
icon: "mdi:camera",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||||
|
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||||
|
context: {
|
||||||
|
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_state: {
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
state: "off",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||||
|
icon: "mdi:camera-off",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-14T06:07:53.186755+00:00",
|
||||||
|
last_updated: "2021-03-14T06:07:53.186755+00:00",
|
||||||
|
context: {
|
||||||
|
id: "b2308cc91d509ea8e0c623331ab178d6",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
for: null,
|
||||||
|
attribute: null,
|
||||||
|
description:
|
||||||
|
"state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"action/3": [
|
||||||
|
{
|
||||||
|
path: "action/3",
|
||||||
|
timestamp: "2021-03-14T06:07:53.196014+00:00",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
condition_trace: {},
|
||||||
|
config: {
|
||||||
|
mode: "restart",
|
||||||
|
max_exceeded: "silent",
|
||||||
|
trigger: [
|
||||||
|
{
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
from: "off",
|
||||||
|
to: "on",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
action: [
|
||||||
|
{
|
||||||
|
service: "light.turn_on",
|
||||||
|
target: {
|
||||||
|
entity_id: "light.elgato_key_light_air",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
wait_for_trigger: [
|
||||||
|
{
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
from: "on",
|
||||||
|
to: "off",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
delay: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: "light.turn_off",
|
||||||
|
target: {
|
||||||
|
entity_id: "light.elgato_key_light_air",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
id: "1614732497392",
|
||||||
|
alias: "Auto Elgato",
|
||||||
|
description: "",
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
id: "43b6ee9293a551c5cc14e8eb60af54ba",
|
||||||
|
parent_id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
variables: {
|
||||||
|
trigger: {
|
||||||
|
platform: "state",
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
from_state: {
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
state: "off",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||||
|
icon: "mdi:camera-off",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-14T06:06:29.235325+00:00",
|
||||||
|
last_updated: "2021-03-14T06:06:29.235325+00:00",
|
||||||
|
context: {
|
||||||
|
id: "ad4864c5ce957c38a07b50378eeb245d",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
to_state: {
|
||||||
|
entity_id: "binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
state: "on",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Paulus’s MacBook Pro Camera In Use",
|
||||||
|
icon: "mdi:camera",
|
||||||
|
},
|
||||||
|
last_changed: "2021-03-14T06:07:01.762009+00:00",
|
||||||
|
last_updated: "2021-03-14T06:07:01.762009+00:00",
|
||||||
|
context: {
|
||||||
|
id: "e22ddfd5f11dc4aad9a52fc10dab613b",
|
||||||
|
parent_id: null,
|
||||||
|
user_id: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
for: null,
|
||||||
|
attribute: null,
|
||||||
|
description: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
logbookEntries: [
|
||||||
|
{
|
||||||
|
name: "Auto Elgato",
|
||||||
|
message:
|
||||||
|
"has been triggered by state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
source: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
|
entity_id: "automation.auto_elgato",
|
||||||
|
when: "2021-03-14T06:07:01.768492+00:00",
|
||||||
|
domain: "automation",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
when: "2021-03-14T06:07:01.872187+00:00",
|
||||||
|
name: "Elgato Key Light Air",
|
||||||
|
state: "on",
|
||||||
|
entity_id: "light.elgato_key_light_air",
|
||||||
|
context_entity_id: "automation.auto_elgato",
|
||||||
|
context_entity_id_name: "Auto Elgato",
|
||||||
|
context_event_type: "automation_triggered",
|
||||||
|
context_domain: "automation",
|
||||||
|
context_name: "Auto Elgato",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
when: "2021-03-14T06:07:53.284505+00:00",
|
||||||
|
name: "Elgato Key Light Air",
|
||||||
|
state: "off",
|
||||||
|
entity_id: "light.elgato_key_light_air",
|
||||||
|
context_entity_id: "automation.auto_elgato",
|
||||||
|
context_entity_id_name: "Auto Elgato",
|
||||||
|
context_event_type: "automation_triggered",
|
||||||
|
context_domain: "automation",
|
||||||
|
context_name: "Auto Elgato",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
7
gallery/src/data/traces/types.ts
Normal file
7
gallery/src/data/traces/types.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { AutomationTraceExtended } from "../../../../src/data/trace";
|
||||||
|
import { LogbookEntry } from "../../../../src/data/logbook";
|
||||||
|
|
||||||
|
export interface DemoTrace {
|
||||||
|
trace: AutomationTraceExtended;
|
||||||
|
logbookEntries: LogbookEntry[];
|
||||||
|
}
|
64
gallery/src/demos/demo-automation-trace.ts
Normal file
64
gallery/src/demos/demo-automation-trace.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/trace/hat-trace";
|
||||||
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
import { DemoTrace } from "../data/traces/types";
|
||||||
|
import { basicTrace } from "../data/traces/basic_trace";
|
||||||
|
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
||||||
|
|
||||||
|
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||||
|
|
||||||
|
@customElement("demo-automation-trace")
|
||||||
|
export class DemoAutomationTrace extends LitElement {
|
||||||
|
@property({ attribute: false }) hass?: HomeAssistant;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
${traces.map(
|
||||||
|
(trace) => html`
|
||||||
|
<ha-card .heading=${trace.trace.config.alias}>
|
||||||
|
<div class="card-content">
|
||||||
|
<hat-trace
|
||||||
|
.hass=${this.hass}
|
||||||
|
.trace=${trace.trace}
|
||||||
|
.logbookEntries=${trace.logbookEntries}
|
||||||
|
></hat-trace>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-automation-trace": DemoAutomationTrace;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -71,35 +76,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoAlarmPanelEntity extends PolymerElement {
|
@customElement("demo-hui-alarm-panel-card")
|
||||||
static get template() {
|
class DemoAlarmPanelEntity extends LitElement {
|
||||||
return html`
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
<demo-cards
|
|
||||||
id="demos"
|
protected render(): TemplateResult {
|
||||||
hass="[[hass]]"
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
configs="[[_configs]]"
|
|
||||||
></demo-cards>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
hass.updateTranslations(null, "en");
|
||||||
value: CONFIGS,
|
hass.updateTranslations("lovelace", "en");
|
||||||
},
|
|
||||||
hass: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
this._setupDemo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _setupDemo() {
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
await hass.updateTranslations(null, "en");
|
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -53,31 +58,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoConditional extends PolymerElement {
|
@customElement("demo-hui-conditional-card")
|
||||||
static get template() {
|
class DemoConditional extends LitElement {
|
||||||
return html`
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
<demo-cards
|
|
||||||
id="demos"
|
protected render(): TemplateResult {
|
||||||
hass="[[hass]]"
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
configs="[[_configs]]"
|
|
||||||
></demo-cards>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
hass: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -217,24 +222,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoEntities extends PolymerElement {
|
@customElement("demo-hui-entities-card")
|
||||||
static get template() {
|
class DemoEntities extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -20,10 +25,10 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "With Name",
|
heading: "With Name (defined in card)",
|
||||||
config: `
|
config: `
|
||||||
- type: button
|
- type: button
|
||||||
name: Bedroom
|
name: Custom Name
|
||||||
entity: light.bed_light
|
entity: light.bed_light
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@@ -32,7 +37,7 @@ const CONFIGS = [
|
|||||||
config: `
|
config: `
|
||||||
- type: button
|
- type: button
|
||||||
entity: light.bed_light
|
entity: light.bed_light
|
||||||
icon: mdi:hotel
|
icon: mdi:tools
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -69,31 +74,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoButtonEntity extends PolymerElement {
|
@customElement("demo-hui-entity-button-card")
|
||||||
static get template() {
|
class DemoButtonEntity extends LitElement {
|
||||||
return html`
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
<demo-cards
|
|
||||||
id="demos"
|
protected render(): TemplateResult {
|
||||||
hass="[[hass]]"
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
configs="[[_configs]]"
|
|
||||||
></demo-cards>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
hass: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -43,7 +48,7 @@ const ENTITIES = [
|
|||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
{
|
{
|
||||||
heading: "Controller",
|
heading: "Unfiltered controller",
|
||||||
config: `
|
config: `
|
||||||
- type: entities
|
- type: entities
|
||||||
entities:
|
entities:
|
||||||
@@ -53,7 +58,7 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Basic",
|
heading: "Filtered entities card",
|
||||||
config: `
|
config: `
|
||||||
- type: entity-filter
|
- type: entity-filter
|
||||||
entities:
|
entities:
|
||||||
@@ -69,7 +74,27 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "With card config",
|
heading: 'With "entities" card config',
|
||||||
|
config: `
|
||||||
|
- type: entity-filter
|
||||||
|
entities:
|
||||||
|
- device_tracker.demo_anne_therese
|
||||||
|
- device_tracker.demo_home_boy
|
||||||
|
- device_tracker.demo_paulus
|
||||||
|
- light.bed_light
|
||||||
|
- light.ceiling_lights
|
||||||
|
- light.kitchen_lights
|
||||||
|
state_filter:
|
||||||
|
- "on"
|
||||||
|
- not_home
|
||||||
|
card:
|
||||||
|
type: entities
|
||||||
|
title: Custom Title
|
||||||
|
show_header_toggle: false
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'With "glance" card config',
|
||||||
config: `
|
config: `
|
||||||
- type: entity-filter
|
- type: entity-filter
|
||||||
entities:
|
entities:
|
||||||
@@ -84,31 +109,27 @@ const CONFIGS = [
|
|||||||
- not_home
|
- not_home
|
||||||
card:
|
card:
|
||||||
type: glance
|
type: glance
|
||||||
show_state: false
|
show_state: true
|
||||||
|
title: Custom Title
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoFilter extends PolymerElement {
|
@customElement("demo-hui-entity-filter-card")
|
||||||
static get template() {
|
class DemoEntityFilter extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-entity-filter-card", DemoFilter);
|
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
|
||||||
|
@@ -1,12 +1,19 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
|
|
||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
getEntity("sensor", "brightness", "12", {}),
|
getEntity("sensor", "brightness", "12", {}),
|
||||||
|
getEntity("sensor", "brightness_medium", "53", {}),
|
||||||
|
getEntity("sensor", "brightness_high", "87", {}),
|
||||||
getEntity("plant", "bonsai", "ok", {}),
|
getEntity("plant", "bonsai", "ok", {}),
|
||||||
getEntity("sensor", "not_working", "unavailable", {}),
|
getEntity("sensor", "not_working", "unavailable", {}),
|
||||||
getEntity("sensor", "outside_humidity", "54", {
|
getEntity("sensor", "outside_humidity", "54", {
|
||||||
@@ -21,16 +28,10 @@ const CONFIGS = [
|
|||||||
{
|
{
|
||||||
heading: "Basic example",
|
heading: "Basic example",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
|
||||||
entity: sensor.brightness
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "With title",
|
|
||||||
config: `
|
|
||||||
- type: gauge
|
- type: gauge
|
||||||
title: Humidity
|
title: Humidity
|
||||||
entity: sensor.outside_humidity
|
entity: sensor.outside_humidity
|
||||||
|
name: Outside Humidity
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -39,6 +40,7 @@ const CONFIGS = [
|
|||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.outside_temperature
|
entity: sensor.outside_temperature
|
||||||
unit_of_measurement: C
|
unit_of_measurement: C
|
||||||
|
name: Outside Temperature
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -46,19 +48,45 @@ const CONFIGS = [
|
|||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.brightness
|
entity: sensor.brightness
|
||||||
|
name: Brightness Low
|
||||||
severity:
|
severity:
|
||||||
red: 32
|
red: 75
|
||||||
green: 0
|
green: 0
|
||||||
yellow: 23
|
yellow: 50
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Setting Min and Max Values",
|
heading: "Setting Severity Levels",
|
||||||
|
config: `
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.brightness_medium
|
||||||
|
name: Brightness Medium
|
||||||
|
severity:
|
||||||
|
red: 75
|
||||||
|
green: 0
|
||||||
|
yellow: 50
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Setting Severity Levels",
|
||||||
|
config: `
|
||||||
|
- type: gauge
|
||||||
|
entity: sensor.brightness_high
|
||||||
|
name: Brightness High
|
||||||
|
severity:
|
||||||
|
red: 75
|
||||||
|
green: 0
|
||||||
|
yellow: 50
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Setting Min (0) and Max (15) Values",
|
||||||
config: `
|
config: `
|
||||||
- type: gauge
|
- type: gauge
|
||||||
entity: sensor.brightness
|
entity: sensor.brightness
|
||||||
|
name: Brightness
|
||||||
min: 0
|
min: 0
|
||||||
max: 38
|
max: 15
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84,24 +112,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoGaugeEntity extends PolymerElement {
|
@customElement("demo-hui-gauge-card")
|
||||||
static get template() {
|
class DemoGaugeEntity extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -77,7 +82,8 @@ const CONFIGS = [
|
|||||||
heading: "With title",
|
heading: "With title",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
title: This is glance
|
title: Custom title
|
||||||
|
columns: 4
|
||||||
entities:
|
entities:
|
||||||
- device_tracker.demo_paulus
|
- device_tracker.demo_paulus
|
||||||
- media_player.living_room
|
- media_player.living_room
|
||||||
@@ -104,9 +110,10 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "No name",
|
heading: "No entity names",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
|
columns: 4
|
||||||
show_name: false
|
show_name: false
|
||||||
entities:
|
entities:
|
||||||
- device_tracker.demo_paulus
|
- device_tracker.demo_paulus
|
||||||
@@ -119,9 +126,10 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "No state",
|
heading: "No state labels",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
|
columns: 4
|
||||||
show_state: false
|
show_state: false
|
||||||
entities:
|
entities:
|
||||||
- device_tracker.demo_paulus
|
- device_tracker.demo_paulus
|
||||||
@@ -134,9 +142,10 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "No name and no state",
|
heading: "No names and no state labels",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
|
columns: 4
|
||||||
show_name: false
|
show_name: false
|
||||||
show_state: false
|
show_state: false
|
||||||
entities:
|
entities:
|
||||||
@@ -150,47 +159,24 @@ const CONFIGS = [
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Custom name, custom icon",
|
heading: "Custom name + custom icon",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
|
columns: 4
|
||||||
entities:
|
entities:
|
||||||
- entity: device_tracker.demo_paulus
|
- entity: device_tracker.demo_paulus
|
||||||
name: ¯\\_(ツ)_/¯
|
name: ¯\\_(ツ)_/¯
|
||||||
icon: mdi:home-assistant
|
icon: mdi:home-assistant
|
||||||
- media_player.living_room
|
- entity: media_player.living_room
|
||||||
- sun.sun
|
name: ¯\\_(ツ)_/¯
|
||||||
- cover.kitchen_window
|
icon: mdi:home-assistant
|
||||||
- entity: light.kitchen_lights
|
|
||||||
icon: mdi:alarm-light
|
|
||||||
- lock.kitchen_door
|
|
||||||
- light.ceiling_lights
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "Custom tap action",
|
|
||||||
config: `
|
|
||||||
- type: glance
|
|
||||||
entities:
|
|
||||||
- entity: lock.kitchen_door
|
|
||||||
tap_action:
|
|
||||||
type: toggle
|
|
||||||
- entity: light.ceiling_lights
|
|
||||||
tap_action:
|
|
||||||
action: call-service
|
|
||||||
service: light.turn_on
|
|
||||||
service_data:
|
|
||||||
entity_id: light.ceiling_lights
|
|
||||||
- device_tracker.demo_paulus
|
|
||||||
- media_player.living_room
|
|
||||||
- sun.sun
|
|
||||||
- cover.kitchen_window
|
|
||||||
- light.kitchen_lights
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Selectively hidden name",
|
heading: "Selectively hidden name",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
|
columns: 4
|
||||||
entities:
|
entities:
|
||||||
- device_tracker.demo_paulus
|
- device_tracker.demo_paulus
|
||||||
- entity: media_player.living_room
|
- entity: media_player.living_room
|
||||||
@@ -199,45 +185,51 @@ const CONFIGS = [
|
|||||||
- entity: cover.kitchen_window
|
- entity: cover.kitchen_window
|
||||||
name:
|
name:
|
||||||
- light.kitchen_lights
|
- light.kitchen_lights
|
||||||
|
- entity: lock.kitchen_door
|
||||||
|
name:
|
||||||
|
- light.ceiling_lights
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Primary theme",
|
heading: "Custom tap action",
|
||||||
config: `
|
config: `
|
||||||
- type: glance
|
- type: glance
|
||||||
theming: primary
|
columns: 4
|
||||||
entities:
|
entities:
|
||||||
- device_tracker.demo_paulus
|
- entity: lock.kitchen_door
|
||||||
- media_player.living_room
|
name: Custom
|
||||||
- sun.sun
|
tap_action:
|
||||||
- cover.kitchen_window
|
type: toggle
|
||||||
- light.kitchen_lights
|
- entity: light.ceiling_lights
|
||||||
- lock.kitchen_door
|
name: Custom
|
||||||
- light.ceiling_lights
|
tap_action:
|
||||||
|
action: call-service
|
||||||
|
service: light.turn_on
|
||||||
|
service_data:
|
||||||
|
entity_id: light.ceiling_lights
|
||||||
|
- entity: sun.sun
|
||||||
|
name: Regular
|
||||||
|
- entity: light.kitchen_lights
|
||||||
|
name: Regular
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoPicEntity extends PolymerElement {
|
@customElement("demo-hui-glance-card")
|
||||||
static get template() {
|
class DemoGlanceEntity extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-glance-card", DemoPicEntity);
|
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
@@ -37,18 +35,10 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoIframe extends PolymerElement {
|
@customElement("demo-hui-iframe-card")
|
||||||
static get template() {
|
class DemoIframe extends LitElement {
|
||||||
return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
|
protected render(): TemplateResult {
|
||||||
}
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
_configs: {
|
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -8,29 +13,43 @@ import "../components/demo-cards";
|
|||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
getEntity("light", "bed_light", "on", {
|
getEntity("light", "bed_light", "on", {
|
||||||
friendly_name: "Bed Light",
|
friendly_name: "Bed Light",
|
||||||
brightness: 130,
|
brightness: 255,
|
||||||
}),
|
}),
|
||||||
getEntity("light", "dim", "off", {
|
getEntity("light", "dim_on", "on", {
|
||||||
|
friendly_name: "Dining Room",
|
||||||
|
supported_features: 1,
|
||||||
|
brightness: 100,
|
||||||
|
}),
|
||||||
|
getEntity("light", "dim_off", "off", {
|
||||||
|
friendly_name: "Dining Room",
|
||||||
supported_features: 1,
|
supported_features: 1,
|
||||||
}),
|
}),
|
||||||
getEntity("light", "unavailable", "unavailable", {
|
getEntity("light", "unavailable", "unavailable", {
|
||||||
|
friendly_name: "Lost Light",
|
||||||
supported_features: 1,
|
supported_features: 1,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
{
|
{
|
||||||
heading: "Basic example",
|
heading: "Switchable Light",
|
||||||
config: `
|
config: `
|
||||||
- type: light
|
- type: light
|
||||||
entity: light.bed_light
|
entity: light.bed_light
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "Dim",
|
heading: "Dimmable Light On",
|
||||||
config: `
|
config: `
|
||||||
- type: light
|
- type: light
|
||||||
entity: light.dim
|
entity: light.dim_on
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Dimmable Light Off",
|
||||||
|
config: `
|
||||||
|
- type: light
|
||||||
|
entity: light.dim_off
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -49,24 +68,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoLightEntity extends PolymerElement {
|
@customElement("demo-hui-light-card")
|
||||||
static get template() {
|
class DemoLightEntity extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -161,31 +166,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoMap extends PolymerElement {
|
@customElement("demo-hui-map-card")
|
||||||
static get template() {
|
class DemoMap extends LitElement {
|
||||||
return html`
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
<demo-cards
|
|
||||||
id="demos"
|
protected render(): TemplateResult {
|
||||||
hass="[[hass]]"
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
configs="[[_configs]]"
|
|
||||||
></demo-cards>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
hass: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { mockTemplate } from "../../../demo/src/stubs/template";
|
import { mockTemplate } from "../../../demo/src/stubs/template";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -254,23 +259,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoMarkdown extends PolymerElement {
|
@customElement("demo-hui-markdown-card")
|
||||||
static get template() {
|
class DemoMarkdown extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
hass.updateTranslations(null, "en");
|
||||||
value: CONFIGS,
|
hass.updateTranslations("lovelace", "en");
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
mockTemplate(hass);
|
mockTemplate(hass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
import { createMediaPlayerEntities } from "../data/media_players";
|
import { createMediaPlayerEntities } from "../data/media_players";
|
||||||
@@ -146,35 +151,33 @@ const CONFIGS = [
|
|||||||
entity: media_player.receiver_off
|
entity: media_player.receiver_off
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
heading: "Grid Full Size",
|
||||||
|
config: `
|
||||||
|
- type: grid
|
||||||
|
columns: 1
|
||||||
|
cards:
|
||||||
|
- type: media-control
|
||||||
|
entity: media_player.music_paused
|
||||||
|
`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoHuiMediControlCard extends PolymerElement {
|
@customElement("demo-hui-media-control-card")
|
||||||
static get template() {
|
class DemoHuiMediaControlCard extends LitElement {
|
||||||
return html`
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
<demo-cards
|
|
||||||
id="demos"
|
protected render(): TemplateResult {
|
||||||
hass="[[hass]]"
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
configs="[[_configs]]"
|
|
||||||
></demo-cards>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
hass: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(createMediaPlayerEntities());
|
hass.addEntities(createMediaPlayerEntities());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard);
|
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
import { createMediaPlayerEntities } from "../data/media_players";
|
import { createMediaPlayerEntities } from "../data/media_players";
|
||||||
@@ -55,33 +60,21 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoHuiMediaPlayerRows extends PolymerElement {
|
@customElement("demo-hui-media-player-row")
|
||||||
static get template() {
|
class DemoHuiMediaPlayerRow extends LitElement {
|
||||||
return html`
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
<demo-cards
|
|
||||||
id="demos"
|
protected render(): TemplateResult {
|
||||||
hass="[[hass]]"
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
configs="[[_configs]]"
|
|
||||||
></demo-cards>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
hass: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(createMediaPlayerEntities());
|
hass.addEntities(createMediaPlayerEntities());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows);
|
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -125,26 +130,21 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoPicElements extends PolymerElement {
|
@customElement("demo-hui-picture-elements-card")
|
||||||
static get template() {
|
class DemoPictureElements extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-picture-elements-card", DemoPicElements);
|
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -80,26 +85,21 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoPicEntity extends PolymerElement {
|
@customElement("demo-hui-picture-entity-card")
|
||||||
static get template() {
|
class DemoPictureEntity extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-picture-entity-card", DemoPicEntity);
|
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -121,26 +126,21 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoPicGlance extends PolymerElement {
|
@customElement("demo-hui-picture-glance-card")
|
||||||
static get template() {
|
class DemoPictureGlance extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-hui-picture-glance-card", DemoPicGlance);
|
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
|
||||||
|
55
gallery/src/demos/demo-hui-plant-card.ts
Normal file
55
gallery/src/demos/demo-hui-plant-card.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
|
import "../components/demo-cards";
|
||||||
|
import { createPlantEntities } from "../data/plants";
|
||||||
|
|
||||||
|
const CONFIGS = [
|
||||||
|
{
|
||||||
|
heading: "Basic example",
|
||||||
|
config: `
|
||||||
|
- type: plant-status
|
||||||
|
entity: plant.lemon_tree
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Problem (too bright) + low battery",
|
||||||
|
config: `
|
||||||
|
- type: plant-status
|
||||||
|
entity: plant.apple_tree
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "With picture + multiple problems",
|
||||||
|
config: `
|
||||||
|
- type: plant-status
|
||||||
|
entity: plant.sunflowers
|
||||||
|
name: Sunflowers Name Overwrite
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-hui-plant-card")
|
||||||
|
export class DemoPlantEntity extends LitElement {
|
||||||
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
|
hass.addEntities(createPlantEntities());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("demo-hui-plant-card", DemoPlantEntity);
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
|
|
||||||
@@ -20,24 +25,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoShoppingListEntity extends PolymerElement {
|
@customElement("demo-hui-shopping-list-card")
|
||||||
static get template() {
|
class DemoShoppingListEntity extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
|
|
||||||
hass.mockAPI("shopping_list", () => [
|
hass.mockAPI("shopping_list", () => [
|
||||||
{ name: "list", id: 1, complete: false },
|
{ name: "list", id: 1, complete: false },
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { mockHistory } from "../../../demo/src/stubs/history";
|
import { mockHistory } from "../../../demo/src/stubs/history";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
@@ -132,24 +137,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoStack extends PolymerElement {
|
@customElement("demo-hui-stack-card")
|
||||||
static get template() {
|
class DemoStack extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
mockHistory(hass);
|
mockHistory(hass);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
import { getEntity } from "../../../src/fake_data/entity";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import "../components/demo-cards";
|
import "../components/demo-cards";
|
||||||
@@ -74,24 +79,19 @@ const CONFIGS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoThermostatEntity extends PolymerElement {
|
@customElement("demo-hui-thermostat-card")
|
||||||
static get template() {
|
class DemoThermostatEntity extends LitElement {
|
||||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_configs: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Object,
|
|
||||||
value: CONFIGS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
const hass = provideHass(this.$.demos);
|
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,29 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import {
|
||||||
/* eslint-plugin-disable lit */
|
customElement,
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
|
import {
|
||||||
import { getEntity } from "../../../src/fake_data/entity";
|
SUPPORT_BRIGHTNESS,
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
SUPPORT_COLOR,
|
||||||
import "../components/demo-more-infos";
|
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 "../../../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 = [
|
const ENTITIES = [
|
||||||
getEntity("light", "bed_light", "on", {
|
getEntity("light", "bed_light", "on", {
|
||||||
@@ -14,40 +31,58 @@ const ENTITIES = [
|
|||||||
}),
|
}),
|
||||||
getEntity("light", "kitchen_light", "on", {
|
getEntity("light", "kitchen_light", "on", {
|
||||||
friendly_name: "Brightness Light",
|
friendly_name: "Brightness Light",
|
||||||
brightness: 80,
|
brightness: 200,
|
||||||
supported_features: SUPPORT_BRIGHTNESS,
|
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"],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
class DemoMoreInfoLight extends PolymerElement {
|
@customElement("demo-more-info-light")
|
||||||
static get template() {
|
class DemoMoreInfoLight extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<demo-more-infos
|
<demo-more-infos
|
||||||
hass="[[hass]]"
|
.hass=${this.hass}
|
||||||
entities="[[_entities]]"
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
></demo-more-infos>
|
></demo-more-infos>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
return {
|
super.firstUpdated(changedProperties);
|
||||||
_entities: {
|
const hass = provideHass(this._demoRoot);
|
||||||
type: Array,
|
hass.updateTranslations(null, "en");
|
||||||
value: ENTITIES.map((ent) => ent.entityId),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public ready() {
|
|
||||||
super.ready();
|
|
||||||
this._setupDemo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _setupDemo() {
|
|
||||||
const hass = provideHass(this);
|
|
||||||
await hass.updateTranslations(null, "en");
|
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-more-info-light", DemoMoreInfoLight);
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-light": DemoMoreInfoLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { html, LitElement, TemplateResult } from "lit-element";
|
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
||||||
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
|
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||||
|
|
||||||
|
@customElement("demo-util-long-press")
|
||||||
export class DemoUtilLongPress extends LitElement {
|
export class DemoUtilLongPress extends LitElement {
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
@@ -20,7 +21,7 @@ export class DemoUtilLongPress extends LitElement {
|
|||||||
|
|
||||||
<textarea></textarea>
|
<textarea></textarea>
|
||||||
|
|
||||||
<div>(try pressing and scrolling too!)</div>
|
<div>Try pressing and scrolling too!</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -62,5 +63,3 @@ export class DemoUtilLongPress extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("demo-util-long-press", DemoUtilLongPress);
|
|
||||||
|
@@ -14,8 +14,6 @@ import "../../src/styles/polymer-ha-style";
|
|||||||
// eslint-disable-next-line import/extensions
|
// eslint-disable-next-line import/extensions
|
||||||
import { DEMOS } from "../build/import-demos";
|
import { DEMOS } from "../build/import-demos";
|
||||||
|
|
||||||
const fixPath = (path) => path.substr(2, path.length - 5);
|
|
||||||
|
|
||||||
class HaGallery extends PolymerElement {
|
class HaGallery extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
@@ -61,7 +59,6 @@ class HaGallery extends PolymerElement {
|
|||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-header-layout>
|
<app-header-layout>
|
||||||
@@ -70,32 +67,42 @@ class HaGallery extends PolymerElement {
|
|||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
icon="hass:arrow-left"
|
icon="hass:arrow-left"
|
||||||
on-click="_backTapped"
|
on-click="_backTapped"
|
||||||
class$='[[_computeHeaderButtonClass(_demo)]]'
|
class$="[[_computeHeaderButtonClass(_demo)]]"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<div main-title>[[_withDefault(_demo, "Home Assistant Gallery")]]</div>
|
<div main-title>
|
||||||
|
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
||||||
|
</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
<div class='content'>
|
<div class="content">
|
||||||
<div id='demo'></div>
|
<div id="demo"></div>
|
||||||
<template is='dom-if' if='[[!_demo]]'>
|
<template is="dom-if" if="[[!_demo]]">
|
||||||
<div class='pickers'>
|
<div class="pickers">
|
||||||
<ha-card header="Lovelace card demos">
|
<ha-card header="Lovelace Card Demos">
|
||||||
<div class='card-content intro'>
|
<div class="card-content intro">
|
||||||
<p>
|
<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.
|
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>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
This gallery helps our developers and designers to see all the different states that each card can be in.
|
This gallery helps our developers and designers to see all
|
||||||
|
the different states that each card can be in.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Check <a href='https://www.home-assistant.io/lovelace'>the official website</a> for instructions on how to get started with Lovelace.</a>.
|
Check
|
||||||
|
<a href="https://www.home-assistant.io/lovelace"
|
||||||
|
>the official website</a
|
||||||
|
>
|
||||||
|
for instructions on how to get started with Lovelace.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<template is='dom-repeat' items='[[_lovelaceDemos]]'>
|
<template is="dom-repeat" items="[[_lovelaceDemos]]">
|
||||||
<a href='#[[item]]'>
|
<a href="#[[item]]">
|
||||||
<paper-item>
|
<paper-item>
|
||||||
<paper-item-body>{{ item }}</paper-item-body>
|
<paper-item-body>{{ item }}</paper-item-body>
|
||||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||||
@@ -104,30 +111,10 @@ class HaGallery extends PolymerElement {
|
|||||||
</template>
|
</template>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
<ha-card header="More Info demos">
|
<ha-card header="Other Demos">
|
||||||
<div class='card-content intro'>
|
<div class="card-content intro"></div>
|
||||||
<p>
|
<template is="dom-repeat" items="[[_restDemos]]">
|
||||||
More info screens show up when an entity is clicked.
|
<a href="#[[item]]">
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<template is='dom-repeat' items='[[_moreInfoDemos]]'>
|
|
||||||
<a href='#[[item]]'>
|
|
||||||
<paper-item>
|
|
||||||
<paper-item-body>{{ item }}</paper-item-body>
|
|
||||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
|
||||||
</paper-item>
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
</ha-card>
|
|
||||||
|
|
||||||
<ha-card header="Util demos">
|
|
||||||
<div class='card-content intro'>
|
|
||||||
<p>
|
|
||||||
Test pages for our utility functions.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<template is='dom-repeat' items='[[_utilDemos]]'>
|
|
||||||
<a href='#[[item]]'>
|
|
||||||
<paper-item>
|
<paper-item>
|
||||||
<paper-item-body>{{ item }}</paper-item-body>
|
<paper-item-body>{{ item }}</paper-item-body>
|
||||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||||
@@ -139,7 +126,10 @@ class HaGallery extends PolymerElement {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
<notification-manager hass=[[_fakeHass]] id='notifications'></notification-manager>
|
<notification-manager
|
||||||
|
hass="[[_fakeHass]]"
|
||||||
|
id="notifications"
|
||||||
|
></notification-manager>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,13 +158,9 @@ class HaGallery extends PolymerElement {
|
|||||||
type: Array,
|
type: Array,
|
||||||
computed: "_computeLovelace(_demos)",
|
computed: "_computeLovelace(_demos)",
|
||||||
},
|
},
|
||||||
_moreInfoDemos: {
|
_restDemos: {
|
||||||
type: Array,
|
type: Array,
|
||||||
computed: "_computeMoreInfos(_demos)",
|
computed: "_computeRest(_demos)",
|
||||||
},
|
|
||||||
_utilDemos: {
|
|
||||||
type: Array,
|
|
||||||
computed: "_computeUtil(_demos)",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -227,12 +213,8 @@ class HaGallery extends PolymerElement {
|
|||||||
return demos.filter((demo) => demo.includes("hui"));
|
return demos.filter((demo) => demo.includes("hui"));
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeMoreInfos(demos) {
|
_computeRest(demos) {
|
||||||
return demos.filter((demo) => demo.includes("more-info"));
|
return demos.filter((demo) => !demo.includes("hui"));
|
||||||
}
|
|
||||||
|
|
||||||
_computeUtil(demos) {
|
|
||||||
return demos.filter((demo) => demo.includes("util"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ import {
|
|||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import "../components/hassio-card-content";
|
import "../components/hassio-card-content";
|
||||||
import { filterAndSort } from "../components/hassio-filter-addons";
|
import { filterAndSort } from "../components/hassio-filter-addons";
|
||||||
@@ -23,6 +24,8 @@ import { hassioStyle } from "../resources/hassio-style";
|
|||||||
class HassioAddonRepositoryEl extends LitElement {
|
class HassioAddonRepositoryEl extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
||||||
|
|
||||||
@property({ attribute: false }) public addons!: HassioAddonInfo[];
|
@property({ attribute: false }) public addons!: HassioAddonInfo[];
|
||||||
@@ -54,7 +57,11 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p class="description">
|
<p class="description">
|
||||||
No results found in "${repo.name}."
|
${this.supervisor.localize(
|
||||||
|
"store.no_results_found",
|
||||||
|
"repository",
|
||||||
|
repo.name
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -83,11 +90,13 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
: mdiPuzzle}
|
: mdiPuzzle}
|
||||||
.iconTitle=${addon.installed
|
.iconTitle=${addon.installed
|
||||||
? addon.update_available
|
? addon.update_available
|
||||||
? "New version available"
|
? this.supervisor.localize(
|
||||||
: "Add-on is installed"
|
"common.new_version_available"
|
||||||
|
)
|
||||||
|
: this.supervisor.localize("addon.installed")
|
||||||
: addon.available
|
: addon.available
|
||||||
? "Add-on is not installed"
|
? this.supervisor.localize("addon.not_installed")
|
||||||
: "Add-on is not available on your system"}
|
: this.supervisor.localize("addon.not_available")}
|
||||||
.iconClass=${addon.installed
|
.iconClass=${addon.installed
|
||||||
? addon.update_available
|
? addon.update_available
|
||||||
? "update"
|
? "update"
|
||||||
|
@@ -11,17 +11,20 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import "../../../src/common/search/search-input";
|
import "../../../src/common/search/search-input";
|
||||||
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonsInfo,
|
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
reloadHassioAddons,
|
reloadHassioAddons,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import "../../../src/layouts/hass-loading-screen";
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
@@ -49,58 +52,43 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
|||||||
class HassioAddonStore extends LitElement {
|
class HassioAddonStore extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
@property({ attribute: false }) private _addons?: HassioAddonInfo[];
|
|
||||||
|
|
||||||
@property({ attribute: false }) private _repos?: HassioAddonRepository[];
|
|
||||||
|
|
||||||
@internalProperty() private _filter?: string;
|
@internalProperty() private _filter?: string;
|
||||||
|
|
||||||
public async refreshData() {
|
public async refreshData() {
|
||||||
this._repos = undefined;
|
|
||||||
this._addons = undefined;
|
|
||||||
this._filter = undefined;
|
|
||||||
await reloadHassioAddons(this.hass);
|
await reloadHassioAddons(this.hass);
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const repos: TemplateResult[] = [];
|
let repos: TemplateResult[] = [];
|
||||||
|
|
||||||
if (this._repos) {
|
if (this.supervisor.addon.repositories) {
|
||||||
for (const repo of this._repos) {
|
repos = this.addonRepositories(
|
||||||
const addons = this._addons!.filter(
|
this.supervisor.addon.repositories,
|
||||||
(addon) => addon.repository === repo.slug
|
this.supervisor.addon.addons,
|
||||||
|
this._filter
|
||||||
);
|
);
|
||||||
|
|
||||||
if (addons.length === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
repos.push(html`
|
|
||||||
<hassio-addon-repository
|
|
||||||
.hass=${this.hass}
|
|
||||||
.repo=${repo}
|
|
||||||
.addons=${addons}
|
|
||||||
.filter=${this._filter!}
|
|
||||||
></hassio-addon-repository>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
hassio
|
|
||||||
main-page
|
|
||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">Add-on Store</span>
|
<span slot="header">
|
||||||
|
${this.supervisor.localize("panel.store")}
|
||||||
|
</span>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
corner="BOTTOM_START"
|
corner="BOTTOM_START"
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@@ -110,15 +98,15 @@ class HassioAddonStore extends LitElement {
|
|||||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<mwc-list-item>
|
<mwc-list-item>
|
||||||
Repositories
|
${this.supervisor.localize("store.repositories")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
<mwc-list-item>
|
<mwc-list-item>
|
||||||
Reload
|
${this.supervisor.localize("common.reload")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
${this.hass.userData?.showAdvanced &&
|
${this.hass.userData?.showAdvanced &&
|
||||||
atLeastVersion(this.hass.config.version, 0, 117)
|
atLeastVersion(this.hass.config.version, 0, 117)
|
||||||
? html`<mwc-list-item>
|
? html`<mwc-list-item>
|
||||||
Registries
|
${this.supervisor.localize("store.registries")}
|
||||||
</mwc-list-item>`
|
</mwc-list-item>`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
@@ -139,11 +127,9 @@ class HassioAddonStore extends LitElement {
|
|||||||
${!this.hass.userData?.showAdvanced
|
${!this.hass.userData?.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
<div class="advanced">
|
<div class="advanced">
|
||||||
Missing add-ons? Enable advanced mode on
|
|
||||||
<a href="/profile" target="_top">
|
<a href="/profile" target="_top">
|
||||||
your profile page
|
${this.supervisor.localize("store.missing_addons")}
|
||||||
</a>
|
</a>
|
||||||
.
|
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -153,14 +139,46 @@ class HassioAddonStore extends LitElement {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
const repositoryUrl = extractSearchParam("repository_url");
|
||||||
|
navigate(this, "/hassio/store", true);
|
||||||
|
if (repositoryUrl) {
|
||||||
|
this._manageRepositories(repositoryUrl);
|
||||||
|
}
|
||||||
|
|
||||||
this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev));
|
this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev));
|
||||||
this._loadData();
|
this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addonRepositories = memoizeOne(
|
||||||
|
(
|
||||||
|
repositories: HassioAddonRepository[],
|
||||||
|
addons: HassioAddonInfo[],
|
||||||
|
filter?: string
|
||||||
|
) => {
|
||||||
|
return repositories.sort(sortRepos).map((repo) => {
|
||||||
|
const filteredAddons = addons.filter(
|
||||||
|
(addon) => addon.repository === repo.slug
|
||||||
|
);
|
||||||
|
|
||||||
|
return filteredAddons.length !== 0
|
||||||
|
? html`
|
||||||
|
<hassio-addon-repository
|
||||||
|
.hass=${this.hass}
|
||||||
|
.repo=${repo}
|
||||||
|
.addons=${filteredAddons}
|
||||||
|
.filter=${filter!}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
|
></hassio-addon-repository>
|
||||||
|
`
|
||||||
|
: html``;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
this._manageRepositories();
|
this._manageRepositoriesClicked();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this.refreshData();
|
this.refreshData();
|
||||||
@@ -177,26 +195,26 @@ class HassioAddonStore extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _manageRepositories() {
|
private _manageRepositoriesClicked() {
|
||||||
|
this._manageRepositories();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _manageRepositories(url?: string) {
|
||||||
showRepositoriesDialog(this, {
|
showRepositoriesDialog(this, {
|
||||||
repos: this._repos!,
|
supervisor: this.supervisor,
|
||||||
loadData: () => this._loadData(),
|
url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _manageRegistries() {
|
private async _manageRegistries() {
|
||||||
showRegistriesDialog(this);
|
showRegistriesDialog(this, { supervisor: this.supervisor });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
try {
|
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
||||||
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
this._repos = addonsInfo.repositories;
|
collection: "supervisor",
|
||||||
this._repos.sort(sortRepos);
|
});
|
||||||
this._addons = addonsInfo.addons;
|
|
||||||
} catch (err) {
|
|
||||||
alert(extractApiErrorMessage(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _filterChanged(e) {
|
private async _filterChanged(e) {
|
||||||
|
@@ -7,13 +7,14 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "web-animations-js/web-animations-next-lite.min";
|
import "web-animations-js/web-animations-next-lite.min";
|
||||||
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -24,16 +25,18 @@ import {
|
|||||||
fetchHassioHardwareAudio,
|
fetchHassioHardwareAudio,
|
||||||
HassioHardwareAudioDevice,
|
HassioHardwareAudioDevice,
|
||||||
} from "../../../../src/data/hassio/hardware";
|
} from "../../../../src/data/hassio/hardware";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
|
||||||
|
|
||||||
@customElement("hassio-addon-audio")
|
@customElement("hassio-addon-audio")
|
||||||
class HassioAddonAudio extends LitElement {
|
class HassioAddonAudio extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
@@ -48,12 +51,16 @@ class HassioAddonAudio extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Audio">
|
<ha-card
|
||||||
|
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
||||||
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
|
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
label="Input"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.configuration.audio.input"
|
||||||
|
)}
|
||||||
@iron-select=${this._setInputDevice}
|
@iron-select=${this._setInputDevice}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
@@ -64,15 +71,17 @@ class HassioAddonAudio extends LitElement {
|
|||||||
${this._inputDevices &&
|
${this._inputDevices &&
|
||||||
this._inputDevices.map((item) => {
|
this._inputDevices.map((item) => {
|
||||||
return html`
|
return html`
|
||||||
<paper-item device=${item.device || ""}
|
<paper-item device=${item.device || ""}>
|
||||||
>${item.name}</paper-item
|
${item.name}
|
||||||
>
|
</paper-item>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
label="Output"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.configuration.audio.output"
|
||||||
|
)}
|
||||||
@iron-select=${this._setOutputDevice}
|
@iron-select=${this._setOutputDevice}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
@@ -93,7 +102,7 @@ class HassioAddonAudio extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-progress-button @click=${this._saveSettings}>
|
<ha-progress-button @click=${this._saveSettings}>
|
||||||
Save
|
${this.supervisor.localize("common.save")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@@ -152,7 +161,7 @@ class HassioAddonAudio extends LitElement {
|
|||||||
|
|
||||||
const noDevice: HassioHardwareAudioDevice = {
|
const noDevice: HassioHardwareAudioDevice = {
|
||||||
device: "default",
|
device: "default",
|
||||||
name: "Default",
|
name: this.supervisor.localize("addon.configuration.audio.default"),
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -189,7 +198,7 @@ class HassioAddonAudio extends LitElement {
|
|||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
this._error = "Failed to set addon audio device";
|
this._error = "Failed to set addon audio device";
|
||||||
|
@@ -7,11 +7,12 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
|
||||||
import "./hassio-addon-audio";
|
import "./hassio-addon-audio";
|
||||||
import "./hassio-addon-config";
|
import "./hassio-addon-config";
|
||||||
import "./hassio-addon-network";
|
import "./hassio-addon-network";
|
||||||
@@ -20,23 +21,37 @@ import "./hassio-addon-network";
|
|||||||
class HassioAddonConfigDashboard extends LitElement {
|
class HassioAddonConfigDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||||
}
|
}
|
||||||
|
const hasConfiguration =
|
||||||
|
(this.addon.options && Object.keys(this.addon.options).length) ||
|
||||||
|
(this.addon.schema && Object.keys(this.addon.schema).length);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
${hasConfiguration || this.addon.network || this.addon.audio
|
||||||
|
? html`
|
||||||
|
${hasConfiguration
|
||||||
|
? html`
|
||||||
<hassio-addon-config
|
<hassio-addon-config
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
></hassio-addon-config>
|
></hassio-addon-config>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
${this.addon.network
|
${this.addon.network
|
||||||
? html`
|
? html`
|
||||||
<hassio-addon-network
|
<hassio-addon-network
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
></hassio-addon-network>
|
></hassio-addon-network>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -45,9 +60,12 @@ class HassioAddonConfigDashboard extends LitElement {
|
|||||||
<hassio-addon-audio
|
<hassio-addon-audio
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
></hassio-addon-audio>
|
></hassio-addon-audio>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
`
|
||||||
|
: this.supervisor.localize("addon.configuration.no_configuration")}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
|
import { ActionDetail } from "@material/mwc-list";
|
||||||
|
import "@material/mwc-list/mwc-list-item";
|
||||||
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -12,9 +15,15 @@ import {
|
|||||||
query,
|
query,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
|
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
||||||
|
import "../../../../src/components/ha-formfield";
|
||||||
|
import "../../../../src/components/ha-switch";
|
||||||
import "../../../../src/components/ha-yaml-editor";
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||||
import {
|
import {
|
||||||
@@ -23,73 +32,208 @@ import {
|
|||||||
setHassioAddonOption,
|
setHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
|
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
|
||||||
|
|
||||||
@customElement("hassio-addon-config")
|
@customElement("hassio-addon-config")
|
||||||
class HassioAddonConfig extends LitElement {
|
class HassioAddonConfig extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ type: Boolean }) private _configHasChanged = false;
|
@property({ type: Boolean }) private _configHasChanged = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) private _valid = true;
|
@property({ type: Boolean }) private _valid = true;
|
||||||
|
|
||||||
@query("ha-yaml-editor", true) private _editor!: HaYamlEditor;
|
@internalProperty() private _canShowSchema = false;
|
||||||
|
|
||||||
|
@internalProperty() private _showOptional = false;
|
||||||
|
|
||||||
|
@internalProperty() private _error?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _options?: Record<string, unknown>;
|
||||||
|
|
||||||
|
@internalProperty() private _yamlMode = false;
|
||||||
|
|
||||||
|
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
||||||
|
|
||||||
|
public computeLabel = (entry: HaFormSchema): string => {
|
||||||
|
return (
|
||||||
|
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||||
|
?.name ||
|
||||||
|
this.addon.translations.en?.configuration?.[entry.name].name ||
|
||||||
|
entry.name
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _filteredShchema = memoizeOne(
|
||||||
|
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
|
||||||
|
return schema.filter((entry) => entry.name in options || entry.required);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
const showForm =
|
||||||
|
!this._yamlMode && this._canShowSchema && this.addon.schema;
|
||||||
|
const hasHiddenOptions =
|
||||||
|
showForm &&
|
||||||
|
JSON.stringify(this.addon.schema) !==
|
||||||
|
JSON.stringify(
|
||||||
|
this._filteredShchema(this.addon.options, this.addon.schema!)
|
||||||
|
);
|
||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
<ha-card header="Configuration">
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="header">
|
||||||
<ha-yaml-editor
|
<h2>
|
||||||
@value-changed=${this._configChanged}
|
${this.supervisor.localize("addon.configuration.options.header")}
|
||||||
></ha-yaml-editor>
|
</h2>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
<div class="card-menu">
|
||||||
${this._valid ? "" : html` <div class="errors">Invalid YAML</div> `}
|
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||||
|
<mwc-icon-button slot="trigger">
|
||||||
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-list-item .disabled=${!this._canShowSchema}>
|
||||||
|
${this._yamlMode
|
||||||
|
? this.supervisor.localize(
|
||||||
|
"addon.configuration.options.edit_in_ui"
|
||||||
|
)
|
||||||
|
: this.supervisor.localize(
|
||||||
|
"addon.configuration.options.edit_in_yaml"
|
||||||
|
)}
|
||||||
|
</mwc-list-item>
|
||||||
|
<mwc-list-item class="warning">
|
||||||
|
${this.supervisor.localize("common.reset_defaults")}
|
||||||
|
</mwc-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
</div>
|
||||||
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
|
||||||
Reset to defaults
|
<div class="card-content">
|
||||||
</ha-progress-button>
|
${showForm
|
||||||
|
? html`<ha-form
|
||||||
|
.data=${this._options!}
|
||||||
|
@value-changed=${this._configChanged}
|
||||||
|
.computeLabel=${this.computeLabel}
|
||||||
|
.schema=${this._showOptional
|
||||||
|
? this.addon.schema!
|
||||||
|
: this._filteredShchema(
|
||||||
|
this.addon.options,
|
||||||
|
this.addon.schema!
|
||||||
|
)}
|
||||||
|
></ha-form>`
|
||||||
|
: html` <ha-yaml-editor
|
||||||
|
@value-changed=${this._configChanged}
|
||||||
|
></ha-yaml-editor>`}
|
||||||
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
|
${!this._yamlMode ||
|
||||||
|
(this._canShowSchema && this.addon.schema) ||
|
||||||
|
this._valid
|
||||||
|
? ""
|
||||||
|
: html`
|
||||||
|
<div class="errors">
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.configuration.options.invalid_yaml"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
${hasHiddenOptions
|
||||||
|
? html`<ha-formfield
|
||||||
|
class="show-additional"
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.configuration.options.show_unused_optional"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-switch
|
||||||
|
@change=${this._toggleOptional}
|
||||||
|
.checked=${this._showOptional}
|
||||||
|
>
|
||||||
|
</ha-switch>
|
||||||
|
</ha-formfield>`
|
||||||
|
: ""}
|
||||||
|
<div class="card-actions right">
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
@click=${this._saveTapped}
|
@click=${this._saveTapped}
|
||||||
.disabled=${!this._configHasChanged || !this._valid}
|
.disabled=${!this._configHasChanged || !this._valid}
|
||||||
>
|
>
|
||||||
Save
|
${this.supervisor.localize("common.save")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._canShowSchema = !this.addon.schema!.find(
|
||||||
|
// @ts-ignore
|
||||||
|
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
|
||||||
|
);
|
||||||
|
this._yamlMode = !this._canShowSchema;
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues): void {
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
super.updated(changedProperties);
|
|
||||||
if (changedProperties.has("addon")) {
|
if (changedProperties.has("addon")) {
|
||||||
this._editor.setValue(this.addon.options);
|
this._options = { ...this.addon.options };
|
||||||
|
}
|
||||||
|
super.updated(changedProperties);
|
||||||
|
if (
|
||||||
|
changedProperties.has("_yamlMode") ||
|
||||||
|
changedProperties.has("_options")
|
||||||
|
) {
|
||||||
|
if (this._yamlMode) {
|
||||||
|
const editor = this._editor;
|
||||||
|
if (editor) {
|
||||||
|
editor.setValue(this._options!);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||||
|
switch (ev.detail.index) {
|
||||||
|
case 0:
|
||||||
|
this._yamlMode = !this._yamlMode;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this._resetTapped(ev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleOptional() {
|
||||||
|
this._showOptional = !this._showOptional;
|
||||||
|
}
|
||||||
|
|
||||||
private _configChanged(ev): void {
|
private _configChanged(ev): void {
|
||||||
|
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
|
||||||
|
this._valid = true;
|
||||||
|
this._configHasChanged = true;
|
||||||
|
this._options! = ev.detail.value;
|
||||||
|
} else {
|
||||||
this._configHasChanged = true;
|
this._configHasChanged = true;
|
||||||
this._valid = ev.detail.isValid;
|
this._valid = ev.detail.isValid;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _resetTapped(ev: CustomEvent): Promise<void> {
|
private async _resetTapped(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
button.progress = true;
|
button.progress = true;
|
||||||
|
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
title: this.addon.name,
|
title: this.supervisor.localize("confirm.reset_options.title"),
|
||||||
text: "Are you sure you want to reset all your options?",
|
text: this.supervisor.localize("confirm.reset_options.text"),
|
||||||
confirmText: "reset options",
|
confirmText: this.supervisor.localize("common.reset_options"),
|
||||||
dismissText: "no",
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
@@ -111,9 +255,11 @@ class HassioAddonConfig extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to reset addon configuration, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.common.update_available",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
@@ -122,18 +268,13 @@ class HassioAddonConfig extends LitElement {
|
|||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
button.progress = true;
|
button.progress = true;
|
||||||
|
|
||||||
let data: HassioAddonSetOptionParams;
|
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = {
|
await setHassioAddonOption(this.hass, this.addon.slug, {
|
||||||
options: this._editor.value,
|
options: this._yamlMode ? this._editor?.value : this._options,
|
||||||
};
|
});
|
||||||
} catch (err) {
|
|
||||||
this._error = err;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
|
||||||
this._configHasChanged = false;
|
this._configHasChanged = false;
|
||||||
const eventdata = {
|
const eventdata = {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -142,12 +283,14 @@ class HassioAddonConfig extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to save addon configuration, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.configuration.options.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
@@ -178,6 +321,36 @@ class HassioAddonConfig extends LitElement {
|
|||||||
.syntaxerror {
|
.syntaxerror {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
.card-menu {
|
||||||
|
float: right;
|
||||||
|
z-index: 3;
|
||||||
|
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
mwc-list-item[disabled] {
|
||||||
|
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.header h2 {
|
||||||
|
color: var(--ha-card-header-color, --primary-text-color);
|
||||||
|
font-family: var(--ha-card-header-font-family, inherit);
|
||||||
|
font-size: var(--ha-card-header-font-size, 24px);
|
||||||
|
letter-spacing: -0.012em;
|
||||||
|
line-height: 48px;
|
||||||
|
padding: 12px 16px 16px;
|
||||||
|
display: block;
|
||||||
|
margin-block: 0px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.card-actions.right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-additional {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ import {
|
|||||||
setHassioAddonOption,
|
setHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
@@ -38,6 +39,8 @@ interface NetworkItemInput extends PaperInputElement {
|
|||||||
class HassioAddonNetwork extends LitElement {
|
class HassioAddonNetwork extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
@@ -55,16 +58,30 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Network">
|
<ha-card
|
||||||
|
.header=${this.supervisor.localize(
|
||||||
|
"addon.configuration.network.header"
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Container</th>
|
<th>
|
||||||
<th>Host</th>
|
${this.supervisor.localize(
|
||||||
<th>Description</th>
|
"addon.configuration.network.container"
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.configuration.network.host"
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
${this.supervisor.localize("common.description")}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
${this._config!.map((item) => {
|
${this._config!.map((item) => {
|
||||||
return html`
|
return html`
|
||||||
@@ -73,13 +90,15 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
<td>
|
<td>
|
||||||
<paper-input
|
<paper-input
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
placeholder="disabled"
|
placeholder="${this.supervisor.localize(
|
||||||
|
"addon.configuration.network.disabled"
|
||||||
|
)}"
|
||||||
.value=${item.host ? String(item.host) : ""}
|
.value=${item.host ? String(item.host) : ""}
|
||||||
.container=${item.container}
|
.container=${item.container}
|
||||||
no-label-float
|
no-label-float
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</td>
|
</td>
|
||||||
<td>${item.description}</td>
|
<td>${this._computeDescription(item)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
@@ -88,10 +107,10 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
||||||
Reset to defaults
|
${this.supervisor.localize("common.reset_defaults")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
<ha-progress-button @click=${this._saveTapped}>
|
<ha-progress-button @click=${this._saveTapped}>
|
||||||
Save
|
${this.supervisor.localize("common.save")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@@ -105,6 +124,15 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _computeDescription = (item: NetworkItem): string => {
|
||||||
|
return (
|
||||||
|
this.addon.translations[this.hass.language]?.network?.[item.container]
|
||||||
|
?.description ||
|
||||||
|
this.addon.translations.en?.network?.[item.container]?.description ||
|
||||||
|
item.description
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
private _setNetworkConfig(): void {
|
private _setNetworkConfig(): void {
|
||||||
const network = this.addon.network || {};
|
const network = this.addon.network || {};
|
||||||
const description = this.addon.network_description || {};
|
const description = this.addon.network_description || {};
|
||||||
@@ -147,12 +175,14 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_reset",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
@@ -181,12 +211,14 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@@ -19,11 +20,14 @@ import "../../../../src/layouts/hass-loading-screen";
|
|||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
@customElement("hassio-addon-documentation-tab")
|
@customElement("hassio-addon-documentation-tab")
|
||||||
class HassioAddonDocumentationDashboard extends LitElement {
|
class HassioAddonDocumentationDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
@@ -81,9 +85,11 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
this.addon!.slug
|
this.addon!.slug
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to get addon documentation, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.documentation.get_logs",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,17 +9,26 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
|
import { navigate } from "../../../src/common/navigate";
|
||||||
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
|
import "../../../src/components/ha-circular-progress";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonInfo,
|
fetchHassioAddonInfo,
|
||||||
|
fetchHassioAddonsInfo,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
|
import "../../../src/layouts/hass-error-screen";
|
||||||
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import "../../../src/components/ha-circular-progress";
|
|
||||||
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
|
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
@@ -35,12 +44,16 @@ import "./log/hassio-addon-logs";
|
|||||||
class HassioAddonDashboard extends LitElement {
|
class HassioAddonDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
|
@internalProperty() _error?: string;
|
||||||
|
|
||||||
private _computeTail = memoizeOne((route: Route) => {
|
private _computeTail = memoizeOne((route: Route) => {
|
||||||
const dividerPos = route.path.indexOf("/", 1);
|
const dividerPos = route.path.indexOf("/", 1);
|
||||||
return dividerPos === -1
|
return dividerPos === -1
|
||||||
@@ -55,13 +68,19 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
});
|
});
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
if (this._error) {
|
||||||
|
return html`<hass-error-screen
|
||||||
|
.error=${this._error}
|
||||||
|
></hass-error-screen>`;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.addon) {
|
if (!this.addon) {
|
||||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addonTabs: PageNavigation[] = [
|
const addonTabs: PageNavigation[] = [
|
||||||
{
|
{
|
||||||
name: "Info",
|
translationKey: "addon.panel.info",
|
||||||
path: `/hassio/addon/${this.addon.slug}/info`,
|
path: `/hassio/addon/${this.addon.slug}/info`,
|
||||||
iconPath: mdiInformationVariant,
|
iconPath: mdiInformationVariant,
|
||||||
},
|
},
|
||||||
@@ -69,7 +88,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
|
|
||||||
if (this.addon.documentation) {
|
if (this.addon.documentation) {
|
||||||
addonTabs.push({
|
addonTabs.push({
|
||||||
name: "Documentation",
|
translationKey: "addon.panel.documentation",
|
||||||
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
path: `/hassio/addon/${this.addon.slug}/documentation`,
|
||||||
iconPath: mdiFileDocument,
|
iconPath: mdiFileDocument,
|
||||||
});
|
});
|
||||||
@@ -78,12 +97,12 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
if (this.addon.version) {
|
if (this.addon.version) {
|
||||||
addonTabs.push(
|
addonTabs.push(
|
||||||
{
|
{
|
||||||
name: "Configuration",
|
translationKey: "addon.panel.configuration",
|
||||||
path: `/hassio/addon/${this.addon.slug}/config`,
|
path: `/hassio/addon/${this.addon.slug}/config`,
|
||||||
iconPath: mdiCogs,
|
iconPath: mdiCogs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Log",
|
translationKey: "addon.panel.log",
|
||||||
path: `/hassio/addon/${this.addon.slug}/logs`,
|
path: `/hassio/addon/${this.addon.slug}/logs`,
|
||||||
iconPath: mdiMathLog,
|
iconPath: mdiMathLog,
|
||||||
}
|
}
|
||||||
@@ -95,17 +114,19 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
|
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
|
||||||
.route=${route}
|
.route=${route}
|
||||||
hassio
|
|
||||||
.tabs=${addonTabs}
|
.tabs=${addonTabs}
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">${this.addon.name}</span>
|
<span slot="header">${this.addon.name}</span>
|
||||||
<hassio-addon-router
|
<hassio-addon-router
|
||||||
.route=${route}
|
.route=${route}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
></hassio-addon-router>
|
></hassio-addon-router>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
@@ -152,30 +173,60 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async firstUpdated(): Promise<void> {
|
protected async firstUpdated(): Promise<void> {
|
||||||
await this._routeDataChanged(this.route);
|
if (this.route.path === "") {
|
||||||
|
const requestedAddon = extractSearchParam("addon");
|
||||||
|
if (requestedAddon) {
|
||||||
|
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
||||||
|
const validAddon = addonsInfo.addons
|
||||||
|
.some((addon) => addon.slug === requestedAddon);
|
||||||
|
if (!validAddon) {
|
||||||
|
this._error = this.supervisor.localize("my.error_addon_not_found");
|
||||||
|
} else {
|
||||||
|
navigate(this, `/hassio/addon/${requestedAddon}`, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _apiCalled(ev): Promise<void> {
|
private async _apiCalled(ev): Promise<void> {
|
||||||
const path: string = ev.detail.path;
|
const pathSplit: string[] = ev.detail.path?.split("/");
|
||||||
|
|
||||||
if (!path) {
|
if (!pathSplit || pathSplit.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const path: string = pathSplit[pathSplit.length - 1];
|
||||||
|
|
||||||
|
if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "supervisor",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (path === "uninstall") {
|
if (path === "uninstall") {
|
||||||
history.back();
|
window.history.back();
|
||||||
} else {
|
} else {
|
||||||
await this._routeDataChanged(this.route);
|
await this._routeDataChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _routeDataChanged(routeData: Route): Promise<void> {
|
protected updated(changedProperties) {
|
||||||
const addon = routeData.path.split("/")[1];
|
if (changedProperties.has("route") && !this.addon) {
|
||||||
|
this._routeDataChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _routeDataChanged(): Promise<void> {
|
||||||
|
const addon = this.route.path.split("/")[1];
|
||||||
|
if (!addon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
||||||
this.addon = addoninfo;
|
this.addon = addoninfo;
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
||||||
this.addon = undefined;
|
this.addon = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { customElement, property } from "lit-element";
|
import { customElement, property } from "lit-element";
|
||||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
||||||
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
HassRouterPage,
|
HassRouterPage,
|
||||||
RouterOptions,
|
RouterOptions,
|
||||||
@@ -17,6 +18,8 @@ class HassioAddonRouter extends HassRouterPage {
|
|||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
protected routerOptions: RouterOptions = {
|
protected routerOptions: RouterOptions = {
|
||||||
@@ -41,6 +44,7 @@ class HassioAddonRouter extends HassRouterPage {
|
|||||||
protected updatePageEl(el) {
|
protected updatePageEl(el) {
|
||||||
el.route = this.routeTail;
|
el.route = this.routeTail;
|
||||||
el.hass = this.hass;
|
el.hass = this.hass;
|
||||||
|
el.supervisor = this.supervisor;
|
||||||
el.addon = this.addon;
|
el.addon = this.addon;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
}
|
}
|
||||||
|
@@ -7,8 +7,9 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
@@ -20,6 +21,8 @@ class HassioAddonInfoDashboard extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -32,6 +35,7 @@ class HassioAddonInfoDashboard extends LitElement {
|
|||||||
<hassio-addon-info
|
<hassio-addon-info
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
></hassio-addon-info>
|
></hassio-addon-info>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -25,6 +25,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { navigate } from "../../../../src/common/navigate";
|
import { navigate } from "../../../../src/common/navigate";
|
||||||
@@ -43,22 +44,35 @@ import {
|
|||||||
HassioAddonSetOptionParams,
|
HassioAddonSetOptionParams,
|
||||||
HassioAddonSetSecurityParams,
|
HassioAddonSetSecurityParams,
|
||||||
installHassioAddon,
|
installHassioAddon,
|
||||||
|
restartHassioAddon,
|
||||||
setHassioAddonOption,
|
setHassioAddonOption,
|
||||||
setHassioAddonSecurity,
|
setHassioAddonSecurity,
|
||||||
startHassioAddon,
|
startHassioAddon,
|
||||||
|
stopHassioAddon,
|
||||||
uninstallHassioAddon,
|
uninstallHassioAddon,
|
||||||
|
updateHassioAddon,
|
||||||
validateHassioAddonOption,
|
validateHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import {
|
||||||
|
extractApiErrorMessage,
|
||||||
|
fetchHassioStats,
|
||||||
|
HassioStats,
|
||||||
|
} from "../../../../src/data/hassio/common";
|
||||||
|
import { StoreAddon } from "../../../../src/data/supervisor/store";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
||||||
import "../../components/hassio-card-content";
|
import "../../components/hassio-card-content";
|
||||||
|
import "../../components/supervisor-metric";
|
||||||
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
||||||
|
import { showDialogSupervisorUpdate } from "../../dialogs/update/show-dialog-update";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
import { addonArchIsSupported } from "../../util/addon";
|
||||||
|
|
||||||
const STAGE_ICON = {
|
const STAGE_ICON = {
|
||||||
stable: mdiCheckCircle,
|
stable: mdiCheckCircle,
|
||||||
@@ -66,63 +80,6 @@ const STAGE_ICON = {
|
|||||||
deprecated: mdiExclamationThick,
|
deprecated: mdiExclamationThick,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PERMIS_DESC = {
|
|
||||||
stage: {
|
|
||||||
title: "Add-on Stage",
|
|
||||||
description: `Add-ons can have one of three stages:\n\n<ha-svg-icon .path='${STAGE_ICON.stable}'></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon .path='${STAGE_ICON.experimental}'></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon .path='${STAGE_ICON.deprecated}'></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`,
|
|
||||||
},
|
|
||||||
rating: {
|
|
||||||
title: "Add-on Security Rating",
|
|
||||||
description:
|
|
||||||
"Home Assistant provides a security rating to each of the add-ons, which indicates the risks involved when using this add-on. The more access an add-on requires on your system, the lower the score, thus raising the possible security risks.\n\nA score is on a scale from 1 to 6. Where 1 is the lowest score (considered the most insecure and highest risk) and a score of 6 is the highest score (considered the most secure and lowest risk).",
|
|
||||||
},
|
|
||||||
host_network: {
|
|
||||||
title: "Host Network",
|
|
||||||
description:
|
|
||||||
"Add-ons usually run in their own isolated network layer, which prevents them from accessing the network of the host operating system. In some cases, this network isolation can limit add-ons in providing their services and therefore, the isolation can be lifted by the add-on author, giving the add-on full access to the network capabilities of the host machine. This gives the add-on more networking capabilities but lowers the security, hence, the security rating of the add-on will be lowered when this option is used by the add-on.",
|
|
||||||
},
|
|
||||||
homeassistant_api: {
|
|
||||||
title: "Home Assistant API Access",
|
|
||||||
description:
|
|
||||||
"This add-on is allowed to access your running Home Assistant instance directly via the Home Assistant API. This mode handles authentication for the add-on as well, which enables an add-on to interact with Home Assistant without the need for additional authentication tokens.",
|
|
||||||
},
|
|
||||||
full_access: {
|
|
||||||
title: "Full Hardware Access",
|
|
||||||
description:
|
|
||||||
"This add-on is given full access to the hardware of your system, by request of the add-on author. Access is comparable to the privileged mode in Docker. Since this opens up possible security risks, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on.",
|
|
||||||
},
|
|
||||||
hassio_api: {
|
|
||||||
title: "Supervisor API Access",
|
|
||||||
description:
|
|
||||||
"The add-on was given access to the Supervisor API, by request of the add-on author. By default, the add-on can access general version information of your system. When the add-on requests 'manager' or 'admin' level access to the API, it will gain access to control multiple parts of your Home Assistant system. This permission is indicated by this badge and will impact the security score of the addon negatively.",
|
|
||||||
},
|
|
||||||
docker_api: {
|
|
||||||
title: "Full Docker Access",
|
|
||||||
description:
|
|
||||||
"The add-on author has requested the add-on to have management access to the Docker instance running on your system. This mode gives the add-on full access and control to your entire Home Assistant system, which adds security risks, and could damage your system when misused. Therefore, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on.",
|
|
||||||
},
|
|
||||||
host_pid: {
|
|
||||||
title: "Host Processes Namespace",
|
|
||||||
description:
|
|
||||||
"Usually, the processes the add-on runs, are isolated from all other system processes. The add-on author has requested the add-on to have access to the system processes running on the host system instance, and allow the add-on to spawn processes on the host system as well. This mode gives the add-on full access and control to your entire Home Assistant system, which adds security risks, and could damage your system when misused. Therefore, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on.",
|
|
||||||
},
|
|
||||||
apparmor: {
|
|
||||||
title: "AppArmor",
|
|
||||||
description:
|
|
||||||
"AppArmor ('Application Armor') is a Linux kernel security module that restricts add-ons capabilities like network access, raw socket access, and permission to read, write, or execute specific files.\n\nAdd-on authors can provide their security profiles, optimized for the add-on, or request it to be disabled. If AppArmor is disabled, it will raise security risks and therefore, has a negative impact on the security score of the add-on.",
|
|
||||||
},
|
|
||||||
auth_api: {
|
|
||||||
title: "Home Assistant Authentication",
|
|
||||||
description:
|
|
||||||
"An add-on can authenticate users against Home Assistant, allowing add-ons to give users the possibility to log into applications running inside add-ons, using their Home Assistant username/password. This badge indicates if the add-on author requests this capability.",
|
|
||||||
},
|
|
||||||
ingress: {
|
|
||||||
title: "Ingress",
|
|
||||||
description:
|
|
||||||
"This add-on is using Ingress to embed its interface securely into Home Assistant.",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
@customElement("hassio-addon-info")
|
@customElement("hassio-addon-info")
|
||||||
class HassioAddonInfo extends LitElement {
|
class HassioAddonInfo extends LitElement {
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
@@ -131,43 +88,96 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
|
@internalProperty() private _metrics?: HassioStats;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
|
|
||||||
|
private _addonStoreInfo = memoizeOne(
|
||||||
|
(slug: string, storeAddons: StoreAddon[]) =>
|
||||||
|
storeAddons.find((addon) => addon.slug === slug)
|
||||||
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
const addonStoreInfo =
|
||||||
|
!this.addon.detached && !this.addon.available
|
||||||
|
? this._addonStoreInfo(this.addon.slug, this.supervisor.store.addons)
|
||||||
|
: undefined;
|
||||||
|
const metrics = [
|
||||||
|
{
|
||||||
|
description: this.supervisor.localize("addon.dashboard.cpu_usage"),
|
||||||
|
value: this._metrics?.cpu_percent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: this.supervisor.localize("addon.dashboard.ram_usage"),
|
||||||
|
value: this._metrics?.memory_percent,
|
||||||
|
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
|
||||||
|
this._metrics?.memory_limit
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
return html`
|
return html`
|
||||||
${this.addon.update_available
|
${this.addon.update_available
|
||||||
? html`
|
? html`
|
||||||
<ha-card header="Update available! 🎉">
|
<ha-card
|
||||||
|
.header="${this.supervisor.localize(
|
||||||
|
"common.update_available",
|
||||||
|
"count",
|
||||||
|
1
|
||||||
|
)}🎉"
|
||||||
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<hassio-card-content
|
<hassio-card-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.title="${this.addon.name} ${this.addon
|
.title="${this.supervisor.localize(
|
||||||
.version_latest} is available"
|
"addon.dashboard.new_update_available",
|
||||||
.description="You are currently running version ${this.addon
|
"name",
|
||||||
.version}"
|
this.addon.name,
|
||||||
|
"version",
|
||||||
|
this.addon.version_latest
|
||||||
|
)}"
|
||||||
|
.description="${this.supervisor.localize(
|
||||||
|
"common.running_version",
|
||||||
|
"version",
|
||||||
|
this.addon.version
|
||||||
|
)}"
|
||||||
icon=${mdiArrowUpBoldCircle}
|
icon=${mdiArrowUpBoldCircle}
|
||||||
iconClass="update"
|
iconClass="update"
|
||||||
></hassio-card-content>
|
></hassio-card-content>
|
||||||
${!this.addon.available
|
${!this.addon.available && addonStoreInfo
|
||||||
|
? !addonArchIsSupported(
|
||||||
|
this.supervisor.info.supported_arch,
|
||||||
|
this.addon.arch
|
||||||
|
)
|
||||||
? html`
|
? html`
|
||||||
<p>
|
<p class="warning">
|
||||||
This update is no longer compatible with your system.
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.not_available_arch"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<p class="warning">
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.not_available_arch",
|
||||||
|
"core_version_installed",
|
||||||
|
this.supervisor.core.version,
|
||||||
|
"core_version_needed",
|
||||||
|
addonStoreInfo.homeassistant
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-call-api-button
|
<mwc-button @click=${this._updateClicked}>
|
||||||
.hass=${this.hass}
|
${this.supervisor.localize("common.update")}
|
||||||
.disabled=${!this.addon.available}
|
</mwc-button>
|
||||||
path="hassio/addons/${this.addon.slug}/update"
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ha-call-api-button>
|
|
||||||
${this.addon.changelog
|
${this.addon.changelog
|
||||||
? html`
|
? html`
|
||||||
<mwc-button @click=${this._openChangelog}>
|
<mwc-button @click=${this._openChangelog}>
|
||||||
Changelog
|
${this.supervisor.localize("addon.dashboard.changelog")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -178,12 +188,19 @@ class HassioAddonInfo extends LitElement {
|
|||||||
${!this.addon.protected
|
${!this.addon.protected
|
||||||
? html`
|
? html`
|
||||||
<ha-card class="warning">
|
<ha-card class="warning">
|
||||||
<h1 class="card-header">Warning: Protection mode is disabled!</h1>
|
<h1 class="card-header">${this.supervisor.localize(
|
||||||
|
"addon.dashboard.protection_mode.title"
|
||||||
|
)}
|
||||||
|
</h1>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
Protection mode on this add-on is disabled! This gives the add-on full access to the entire system, which adds security risks, and could damage your system when used incorrectly. Only disable the protection mode if you know, need AND trust the source of this add-on.
|
${this.supervisor.localize("addon.dashboard.protection_mode.content")}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions protection-enable">
|
<div class="card-actions protection-enable">
|
||||||
<mwc-button @click=${this._protectionToggled}>Enable Protection mode</mwc-button>
|
<mwc-button @click=${this._protectionToggled}>
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.protection_mode.enable"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@@ -200,14 +217,18 @@ class HassioAddonInfo extends LitElement {
|
|||||||
${this._computeIsRunning
|
${this._computeIsRunning
|
||||||
? html`
|
? html`
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
title="Add-on is running"
|
.title=${this.supervisor.localize(
|
||||||
|
"dashboard.addon_running"
|
||||||
|
)}
|
||||||
class="running"
|
class="running"
|
||||||
.path=${mdiCircle}
|
.path=${mdiCircle}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
title="Add-on is stopped"
|
.title=${this.supervisor.localize(
|
||||||
|
"dashboard.addon_stopped"
|
||||||
|
)}
|
||||||
class="stopped"
|
class="stopped"
|
||||||
.path=${mdiCircle}
|
.path=${mdiCircle}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
@@ -221,22 +242,32 @@ class HassioAddonInfo extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
Current version: ${this.addon.version}
|
Current version: ${this.addon.version}
|
||||||
<div class="changelog" @click=${this._openChangelog}>
|
<div class="changelog" @click=${this._openChangelog}>
|
||||||
(<span class="changelog-link">changelog</span>)
|
(<span class="changelog-link">${
|
||||||
|
this.supervisor.localize("addon.dashboard.changelog")}</span
|
||||||
|
>)
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`<span class="changelog-link" @click=${this._openChangelog}
|
: html`<span class="changelog-link" @click=${this._openChangelog}>${
|
||||||
>Changelog</span
|
this.supervisor.localize("addon.dashboard.changelog")
|
||||||
>`}
|
}</span>`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="description light-color">
|
<div class="description light-color">
|
||||||
${this.addon.description}.<br />
|
${this.addon.description}.<br />
|
||||||
Visit
|
${this.supervisor.localize(
|
||||||
<a href="${this.addon.url!}" target="_blank" rel="noreferrer">
|
"addon.dashboard.visit_addon_page",
|
||||||
${this.addon.name} page</a
|
"name",
|
||||||
|
html`<a
|
||||||
|
href="${this.addon.url!}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
for details.
|
${this.addon.name}
|
||||||
|
</a>`
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="addon-container">
|
||||||
|
<div>
|
||||||
${this.addon.logo
|
${this.addon.logo
|
||||||
? html`
|
? html`
|
||||||
<img
|
<img
|
||||||
@@ -254,7 +285,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
})}
|
})}
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="stage"
|
id="stage"
|
||||||
label="stage"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.stage"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
@@ -280,7 +313,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="host_network"
|
id="host_network"
|
||||||
label="host"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.host"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
|
||||||
@@ -292,7 +327,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="full_access"
|
id="full_access"
|
||||||
label="hardware"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.hardware"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
|
||||||
@@ -304,7 +341,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="homeassistant_api"
|
id="homeassistant_api"
|
||||||
label="hass"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.hass"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
@@ -316,8 +355,12 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="hassio_api"
|
id="hassio_api"
|
||||||
label="hassio"
|
.label=${this.supervisor.localize(
|
||||||
.description=${this.addon.hassio_role}
|
"addon.dashboard.capability.label.hassio"
|
||||||
|
)}
|
||||||
|
.description=${this.supervisor.localize(
|
||||||
|
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||||
|
) || this.addon.hassio_role}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
</ha-label-badge>
|
</ha-label-badge>
|
||||||
@@ -328,7 +371,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="docker_api"
|
id="docker_api"
|
||||||
label="docker"
|
.label=".${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.docker"
|
||||||
|
)}"
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
||||||
@@ -340,7 +385,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="host_pid"
|
id="host_pid"
|
||||||
label="host pid"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.host_pid"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
||||||
@@ -353,7 +400,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
class=${this._computeApparmorClassName}
|
class=${this._computeApparmorClassName}
|
||||||
id="apparmor"
|
id="apparmor"
|
||||||
label="apparmor"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.apparmor"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
|
||||||
@@ -365,7 +414,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="auth_api"
|
id="auth_api"
|
||||||
label="auth"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.auth"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
|
||||||
@@ -377,7 +428,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="ingress"
|
id="ingress"
|
||||||
label="ingress"
|
.label=${this.supervisor.localize(
|
||||||
|
"addon.dashboard.capability.label.ingress"
|
||||||
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
@@ -390,13 +443,22 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
${this.addon.version
|
${this.addon.version
|
||||||
? html`
|
? html`
|
||||||
<div class="addon-options">
|
<div
|
||||||
|
class="${classMap({
|
||||||
|
"addon-options": true,
|
||||||
|
started: this.addon.state === "started",
|
||||||
|
})}"
|
||||||
|
>
|
||||||
<ha-settings-row ?three-line=${this.narrow}>
|
<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
Start on boot
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.boot.title"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
Make the add-on start during a system boot
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.boot.description"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
@change=${this._startOnBootToggled}
|
@change=${this._startOnBootToggled}
|
||||||
@@ -409,10 +471,14 @@ class HassioAddonInfo extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<ha-settings-row ?three-line=${this.narrow}>
|
<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
Watchdog
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.watchdog.title"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
This will start the add-on if it crashes
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.watchdog.description"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
@change=${this._watchdogToggled}
|
@change=${this._watchdogToggled}
|
||||||
@@ -422,15 +488,19 @@ class HassioAddonInfo extends LitElement {
|
|||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.auto_update || this.hass.userData?.showAdvanced
|
${this.addon.auto_update ||
|
||||||
|
this.hass.userData?.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
<ha-settings-row ?three-line=${this.narrow}>
|
<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
Auto update
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.auto_update.title"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
Auto update the add-on when there is a new version
|
${this.supervisor.localize(
|
||||||
available
|
"addon.dashboard.option.auto_update.description"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
@change=${this._autoUpdateToggled}
|
@change=${this._autoUpdateToggled}
|
||||||
@@ -440,21 +510,22 @@ class HassioAddonInfo extends LitElement {
|
|||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.addon.ingress
|
${!this._computeCannotIngressSidebar && this.addon.ingress
|
||||||
? html`
|
? html`
|
||||||
<ha-settings-row ?three-line=${this.narrow}>
|
<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
Show in sidebar
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.ingress_panel.title"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this._computeCannotIngressSidebar
|
${this.supervisor.localize(
|
||||||
? "This option requires Home Assistant 0.92 or later."
|
"addon.dashboard.option.ingress_panel.description"
|
||||||
: "Add this add-on to your sidebar"}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
@change=${this._panelToggled}
|
@change=${this._panelToggled}
|
||||||
.checked=${this.addon.ingress_panel}
|
.checked=${this.addon.ingress_panel}
|
||||||
.disabled=${this._computeCannotIngressSidebar}
|
|
||||||
haptic
|
haptic
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -464,10 +535,14 @@ class HassioAddonInfo extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<ha-settings-row ?three-line=${this.narrow}>
|
<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
Protection mode
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.protected.title"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
Blocks elevated system access from the add-on
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.option.protected.description"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
@change=${this._protectionToggled}
|
@change=${this._protectionToggled}
|
||||||
@@ -480,88 +555,134 @@ class HassioAddonInfo extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
${this.addon.state === "started"
|
||||||
|
? html`<ha-settings-row ?three-line=${this.narrow}>
|
||||||
|
<span slot="heading">
|
||||||
|
${this.supervisor.localize("addon.dashboard.hostname")}
|
||||||
|
</span>
|
||||||
|
<code slot="description">
|
||||||
|
${this.addon.hostname}
|
||||||
|
</code>
|
||||||
|
</ha-settings-row>
|
||||||
|
${metrics.map(
|
||||||
|
(metric) =>
|
||||||
|
html`
|
||||||
|
<supervisor-metric
|
||||||
|
.description=${metric.description}
|
||||||
|
.value=${metric.value ?? 0}
|
||||||
|
.tooltip=${metric.tooltip}
|
||||||
|
></supervisor-metric>
|
||||||
|
`
|
||||||
|
)}`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||||
|
${!this.addon.version && addonStoreInfo && !this.addon.available
|
||||||
|
? !addonArchIsSupported(
|
||||||
|
this.supervisor.info.supported_arch,
|
||||||
|
this.addon.arch
|
||||||
|
)
|
||||||
|
? html`
|
||||||
|
<p class="warning">
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.not_available_arch"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<p class="warning">
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.not_available_version",
|
||||||
|
"core_version_installed",
|
||||||
|
this.supervisor.core.version,
|
||||||
|
"core_version_needed",
|
||||||
|
addonStoreInfo!.homeassistant
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
|
<div>
|
||||||
${this.addon.version
|
${this.addon.version
|
||||||
|
? this._computeIsRunning
|
||||||
? html`
|
? html`
|
||||||
${this._computeIsRunning
|
<ha-progress-button
|
||||||
? html`
|
|
||||||
<ha-call-api-button
|
|
||||||
class="warning"
|
class="warning"
|
||||||
.hass=${this.hass}
|
@click=${this._stopClicked}
|
||||||
.path="hassio/addons/${this.addon.slug}/stop"
|
|
||||||
>
|
>
|
||||||
Stop
|
${this.supervisor.localize("addon.dashboard.stop")}
|
||||||
</ha-call-api-button>
|
</ha-progress-button>
|
||||||
<ha-call-api-button
|
<ha-progress-button
|
||||||
class="warning"
|
class="warning"
|
||||||
.hass=${this.hass}
|
@click=${this._restartClicked}
|
||||||
.path="hassio/addons/${this.addon.slug}/restart"
|
|
||||||
>
|
>
|
||||||
Restart
|
${this.supervisor.localize("addon.dashboard.restart")}
|
||||||
</ha-call-api-button>
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-progress-button @click=${this._startClicked}>
|
<ha-progress-button @click=${this._startClicked}>
|
||||||
Start
|
${this.supervisor.localize("addon.dashboard.start")}
|
||||||
|
</ha-progress-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-progress-button
|
||||||
|
.disabled=${!this.addon.available}
|
||||||
|
@click=${this._installClicked}
|
||||||
|
>
|
||||||
|
${this.supervisor.localize("addon.dashboard.install")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`}
|
`}
|
||||||
${this._computeShowWebUI
|
</div>
|
||||||
|
<div>
|
||||||
|
${this.addon.version
|
||||||
|
? html` ${this._computeShowWebUI
|
||||||
? html`
|
? html`
|
||||||
<a
|
<a
|
||||||
href=${this._pathWebui!}
|
href=${this._pathWebui!}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="right"
|
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
>
|
>
|
||||||
<mwc-button>
|
<mwc-button>
|
||||||
Open web UI
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.open_web_ui"
|
||||||
|
)}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</a>
|
</a>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this._computeShowIngressUI
|
${this._computeShowIngressUI
|
||||||
? html`
|
? html`
|
||||||
<mwc-button class="right" @click=${this._openIngress}>
|
<mwc-button @click=${this._openIngress}>
|
||||||
Open web UI
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.open_web_ui"
|
||||||
|
)}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
class=" right warning"
|
class="warning"
|
||||||
@click=${this._uninstallClicked}
|
@click=${this._uninstallClicked}
|
||||||
>
|
>
|
||||||
Uninstall
|
${this.supervisor.localize("addon.dashboard.uninstall")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
${this.addon.build
|
${this.addon.build
|
||||||
? html`
|
? html`
|
||||||
<ha-call-api-button
|
<ha-call-api-button
|
||||||
class="warning right"
|
class="warning"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.path="hassio/addons/${this.addon.slug}/rebuild"
|
.path="hassio/addons/${this.addon.slug}/rebuild"
|
||||||
>
|
>
|
||||||
Rebuild
|
${this.supervisor.localize("addon.dashboard.rebuild")}
|
||||||
</ha-call-api-button>
|
</ha-call-api-button>
|
||||||
`
|
`
|
||||||
|
: ""}`
|
||||||
: ""}
|
: ""}
|
||||||
`
|
</div>
|
||||||
: html`
|
|
||||||
${!this.addon.available
|
|
||||||
? html`
|
|
||||||
<p class="warning">
|
|
||||||
This add-on is not available on your system.
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<ha-progress-button
|
|
||||||
.disabled=${!this.addon.available}
|
|
||||||
@click=${this._installClicked}
|
|
||||||
>
|
|
||||||
Install
|
|
||||||
</ha-progress-button>
|
|
||||||
`}
|
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
@@ -579,6 +700,22 @@ class HassioAddonInfo extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (changedProps.has("addon")) {
|
||||||
|
this._loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadData(): Promise<void> {
|
||||||
|
if (this.addon.state === "started") {
|
||||||
|
this._metrics = await fetchHassioStats(
|
||||||
|
this.hass,
|
||||||
|
`addons/${this.addon.slug}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private get _computeHassioApi(): boolean {
|
private get _computeHassioApi(): boolean {
|
||||||
return (
|
return (
|
||||||
this.addon.hassio_api &&
|
this.addon.hassio_api &&
|
||||||
@@ -600,8 +737,21 @@ class HassioAddonInfo extends LitElement {
|
|||||||
private _showMoreInfo(ev): void {
|
private _showMoreInfo(ev): void {
|
||||||
const id = ev.currentTarget.id;
|
const id = ev.currentTarget.id;
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: PERMIS_DESC[id].title,
|
title: this.supervisor.localize(`addon.dashboard.capability.${id}.title`),
|
||||||
content: PERMIS_DESC[id].description,
|
content:
|
||||||
|
id === "stage"
|
||||||
|
? this.supervisor.localize(
|
||||||
|
`addon.dashboard.capability.${id}.description`,
|
||||||
|
"icon_stable",
|
||||||
|
`<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon>`,
|
||||||
|
"icon_experimental",
|
||||||
|
`<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon>`,
|
||||||
|
"icon_deprecated",
|
||||||
|
`<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon>`
|
||||||
|
)
|
||||||
|
: this.supervisor.localize(
|
||||||
|
`addon.dashboard.capability.${id}.description`
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,9 +804,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon option, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,9 +826,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon option, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,9 +848,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon option, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,9 +870,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon security option, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,9 +892,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to set addon option, ${extractApiErrorMessage(
|
this._error = this.supervisor.localize(
|
||||||
err
|
"addon.failed_to_save",
|
||||||
)}`;
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,12 +907,14 @@ class HassioAddonInfo extends LitElement {
|
|||||||
this.addon.slug
|
this.addon.slug
|
||||||
);
|
);
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: "Changelog",
|
title: this.supervisor.localize("addon.dashboard.changelog"),
|
||||||
content,
|
content,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to get addon changelog",
|
title: this.supervisor.localize(
|
||||||
|
"addon.dashboard.action_error.get_changelog"
|
||||||
|
),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -772,13 +934,82 @@ class HassioAddonInfo extends LitElement {
|
|||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to install addon",
|
title: this.supervisor.localize("addon.dashboard.action_error.install"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _stopClicked(ev: CustomEvent): Promise<void> {
|
||||||
|
const button = ev.currentTarget as any;
|
||||||
|
button.progress = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await stopHassioAddon(this.hass, this.addon.slug);
|
||||||
|
const eventdata = {
|
||||||
|
success: true,
|
||||||
|
response: undefined,
|
||||||
|
path: "stop",
|
||||||
|
};
|
||||||
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.supervisor.localize("addon.dashboard.action_error.stop"),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
button.progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _restartClicked(ev: CustomEvent): Promise<void> {
|
||||||
|
const button = ev.currentTarget as any;
|
||||||
|
button.progress = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await restartHassioAddon(this.hass, this.addon.slug);
|
||||||
|
const eventdata = {
|
||||||
|
success: true,
|
||||||
|
response: undefined,
|
||||||
|
path: "stop",
|
||||||
|
};
|
||||||
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
|
} catch (err) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.supervisor.localize("addon.dashboard.action_error.restart"),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
button.progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateClicked(): Promise<void> {
|
||||||
|
showDialogSupervisorUpdate(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
name: this.addon.name,
|
||||||
|
version: this.addon.version_latest,
|
||||||
|
snapshotParams: {
|
||||||
|
name: `addon_${this.addon.slug}_${this.addon.version}`,
|
||||||
|
addons: [this.addon.slug],
|
||||||
|
homeassistant: false,
|
||||||
|
},
|
||||||
|
updateHandler: async () => await this._updateAddon(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateAddon(): Promise<void> {
|
||||||
|
await updateHassioAddon(this.hass, this.addon.slug);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "addon",
|
||||||
|
});
|
||||||
|
const eventdata = {
|
||||||
|
success: true,
|
||||||
|
response: undefined,
|
||||||
|
path: "update",
|
||||||
|
};
|
||||||
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
|
}
|
||||||
|
|
||||||
private async _startClicked(ev: CustomEvent): Promise<void> {
|
private async _startClicked(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
button.progress = true;
|
button.progress = true;
|
||||||
@@ -787,13 +1018,17 @@ class HassioAddonInfo extends LitElement {
|
|||||||
this.hass,
|
this.hass,
|
||||||
this.addon.slug
|
this.addon.slug
|
||||||
);
|
);
|
||||||
if (!validate.data.valid) {
|
if (!validate.valid) {
|
||||||
await showConfirmationDialog(this, {
|
await showConfirmationDialog(this, {
|
||||||
title: "Failed to start addon - configuration validation failed!",
|
title: this.supervisor.localize(
|
||||||
text: validate.data.message.split(" Got ")[0],
|
"addon.dashboard.action_error.start_invalid_config"
|
||||||
|
),
|
||||||
|
text: validate.message.split(" Got ")[0],
|
||||||
confirm: () => this._openConfiguration(),
|
confirm: () => this._openConfiguration(),
|
||||||
confirmText: "Go to configuration",
|
confirmText: this.supervisor.localize(
|
||||||
dismissText: "Cancel",
|
"addon.dashboard.action_error.go_to_config"
|
||||||
|
),
|
||||||
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
});
|
});
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
return;
|
return;
|
||||||
@@ -810,9 +1045,15 @@ class HassioAddonInfo extends LitElement {
|
|||||||
try {
|
try {
|
||||||
await startHassioAddon(this.hass, this.addon.slug);
|
await startHassioAddon(this.hass, this.addon.slug);
|
||||||
this.addon = await fetchHassioAddonInfo(this.hass, this.addon.slug);
|
this.addon = await fetchHassioAddonInfo(this.hass, this.addon.slug);
|
||||||
|
const eventdata = {
|
||||||
|
success: true,
|
||||||
|
response: undefined,
|
||||||
|
path: "start",
|
||||||
|
};
|
||||||
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to start addon",
|
title: this.supervisor.localize("addon.dashboard.action_error.start"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -850,7 +1091,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to uninstall addon",
|
title: this.supervisor.localize(
|
||||||
|
"addon.dashboard.action_error.uninstall"
|
||||||
|
),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -925,9 +1168,6 @@ class HassioAddonInfo extends LitElement {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
.right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
protection-enable mwc-button {
|
protection-enable mwc-button {
|
||||||
--mdc-theme-primary: white;
|
--mdc-theme-primary: white;
|
||||||
}
|
}
|
||||||
@@ -950,7 +1190,8 @@ class HassioAddonInfo extends LitElement {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.card-actions {
|
.card-actions {
|
||||||
display: flow-root;
|
justify-content: space-between;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.security h3 {
|
.security h3 {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
@@ -986,12 +1227,26 @@ class HassioAddonInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.addon-options {
|
.addon-options {
|
||||||
max-width: 50%;
|
max-width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addon-container {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-template-columns: 60% 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addon-container > div:last-of-type {
|
||||||
|
align-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.addon-options {
|
.addon-options {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
.addon-container {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -7,8 +7,9 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
|
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
@@ -18,6 +19,8 @@ import "./hassio-addon-logs";
|
|||||||
class HassioAddonLogDashboard extends LitElement {
|
class HassioAddonLogDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -28,6 +31,7 @@ class HassioAddonLogDashboard extends LitElement {
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<hassio-addon-logs
|
<hassio-addon-logs
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.supervisor=${this.supervisor}
|
||||||
.addon=${this.addon}
|
.addon=${this.addon}
|
||||||
></hassio-addon-logs>
|
></hassio-addon-logs>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -15,6 +15,7 @@ import {
|
|||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import "../../components/hassio-ansi-to-html";
|
import "../../components/hassio-ansi-to-html";
|
||||||
@@ -24,6 +25,8 @@ import { hassioStyle } from "../../resources/hassio-style";
|
|||||||
class HassioAddonLogs extends LitElement {
|
class HassioAddonLogs extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
@@ -48,7 +51,9 @@ class HassioAddonLogs extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
<mwc-button @click=${this._refresh}>
|
||||||
|
${this.supervisor.localize("common.refresh")}
|
||||||
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
@@ -76,7 +81,11 @@ class HassioAddonLogs extends LitElement {
|
|||||||
try {
|
try {
|
||||||
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
|
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to get addon logs, ${extractApiErrorMessage(err)}`;
|
this._error = this.supervisor.localize(
|
||||||
|
"addon.logs.get_logs",
|
||||||
|
"error",
|
||||||
|
extractApiErrorMessage(err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,7 +44,7 @@ class HassioCardContent extends LitElement {
|
|||||||
${this.iconImage
|
${this.iconImage
|
||||||
? html`
|
? html`
|
||||||
<div class="icon_image ${this.iconClass}">
|
<div class="icon_image ${this.iconClass}">
|
||||||
<img src="${this.iconImage}" title="${this.iconTitle}" />
|
<img src="${this.iconImage}" .title=${this.iconTitle} />
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
@@ -27,6 +27,8 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
||||||
|
|
||||||
@customElement("hassio-upload-snapshot")
|
@customElement("hassio-upload-snapshot")
|
||||||
export class HassioUploadSnapshot extends LitElement {
|
export class HassioUploadSnapshot extends LitElement {
|
||||||
public hass!: HomeAssistant;
|
public hass!: HomeAssistant;
|
||||||
@@ -51,6 +53,20 @@ export class HassioUploadSnapshot extends LitElement {
|
|||||||
private async _uploadFile(ev) {
|
private async _uploadFile(ev) {
|
||||||
const file = ev.detail.files[0];
|
const file = ev.detail.files[0];
|
||||||
|
|
||||||
|
if (file.size > MAX_FILE_SIZE) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: "Snapshot file is too big",
|
||||||
|
text: html`The maximum allowed filesize is 1GB.<br />
|
||||||
|
<a
|
||||||
|
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-snapshot-on-a-new-install"
|
||||||
|
target="_blank"
|
||||||
|
>Have a look here on how to restore it.</a
|
||||||
|
>`,
|
||||||
|
confirmText: "ok",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!["application/x-tar"].includes(file.type)) {
|
if (!["application/x-tar"].includes(file.type)) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Unsupported file format",
|
title: "Unsupported file format",
|
||||||
|
87
hassio/src/components/supervisor-metric.ts
Normal file
87
hassio/src/components/supervisor-metric.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import "../../../src/components/ha-bar";
|
||||||
|
import "../../../src/components/ha-settings-row";
|
||||||
|
import { roundWithOneDecimal } from "../../../src/util/calculate";
|
||||||
|
|
||||||
|
@customElement("supervisor-metric")
|
||||||
|
class SupervisorMetric extends LitElement {
|
||||||
|
@property({ type: Number }) public value!: number;
|
||||||
|
|
||||||
|
@property({ type: String }) public description!: string;
|
||||||
|
|
||||||
|
@property({ type: String }) public tooltip?: string;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const roundedValue = roundWithOneDecimal(this.value);
|
||||||
|
return html`<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
${this.description}
|
||||||
|
</span>
|
||||||
|
<div slot="description" .title=${this.tooltip ?? ""}>
|
||||||
|
<span class="value">
|
||||||
|
${roundedValue} %
|
||||||
|
</span>
|
||||||
|
<ha-bar
|
||||||
|
class="${classMap({
|
||||||
|
"target-warning": roundedValue > 50,
|
||||||
|
"target-critical": roundedValue > 85,
|
||||||
|
})}"
|
||||||
|
.value=${this.value}
|
||||||
|
></ha-bar>
|
||||||
|
</div>
|
||||||
|
</ha-settings-row>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-settings-row {
|
||||||
|
padding: 0;
|
||||||
|
height: 54px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
ha-settings-row > div[slot="description"] {
|
||||||
|
white-space: normal;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
ha-bar {
|
||||||
|
--ha-bar-primary-color: var(
|
||||||
|
--hassio-bar-ok-color,
|
||||||
|
var(--success-color)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.target-warning {
|
||||||
|
--ha-bar-primary-color: var(
|
||||||
|
--hassio-bar-warning-color,
|
||||||
|
var(--warning-color)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.target-critical {
|
||||||
|
--ha-bar-primary-color: var(
|
||||||
|
--hassio-bar-critical-color,
|
||||||
|
var(--error-color)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
width: 42px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"supervisor-metric": SupervisorMetric;
|
||||||
|
}
|
||||||
|
}
|
@@ -12,7 +12,7 @@ import { atLeastVersion } from "../../../src/common/config/version";
|
|||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import { compare } from "../../../src/common/string/compare";
|
import { compare } from "../../../src/common/string/compare";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { HassioAddonInfo } from "../../../src/data/hassio/addon";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import "../components/hassio-card-content";
|
import "../components/hassio-card-content";
|
||||||
@@ -22,26 +22,24 @@ import { hassioStyle } from "../resources/hassio-style";
|
|||||||
class HassioAddons extends LitElement {
|
class HassioAddons extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public addons?: HassioAddonInfo[];
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>Add-ons</h1>
|
<h1>${this.supervisor.localize("dashboard.addons")}</h1>
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${!this.addons?.length
|
${!this.supervisor.supervisor.addons?.length
|
||||||
? html`
|
? html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
You don't have any add-ons installed yet. Head over to
|
|
||||||
<button class="link" @click=${this._openStore}>
|
<button class="link" @click=${this._openStore}>
|
||||||
the add-on store
|
${this.supervisor.localize("dashboard.no_addons")}
|
||||||
</button>
|
</button>
|
||||||
to get started!
|
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: this.addons
|
: this.supervisor.supervisor.addons
|
||||||
.sort((a, b) => compare(a.name, b.name))
|
.sort((a, b) => compare(a.name, b.name))
|
||||||
.map(
|
.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
@@ -58,10 +56,16 @@ class HassioAddons extends LitElement {
|
|||||||
? mdiArrowUpBoldCircle
|
? mdiArrowUpBoldCircle
|
||||||
: mdiPuzzle}
|
: mdiPuzzle}
|
||||||
.iconTitle=${addon.state !== "started"
|
.iconTitle=${addon.state !== "started"
|
||||||
? "Add-on is stopped"
|
? this.supervisor.localize(
|
||||||
|
"dashboard.addon_stopped"
|
||||||
|
)
|
||||||
: addon.update_available!
|
: addon.update_available!
|
||||||
? "New version available"
|
? this.supervisor.localize(
|
||||||
: "Add-on is running"}
|
"dashboard.addon_new_version"
|
||||||
|
)
|
||||||
|
: this.supervisor.localize(
|
||||||
|
"dashboard.addon_running"
|
||||||
|
)}
|
||||||
.iconClass=${addon.update_available
|
.iconClass=${addon.update_available
|
||||||
? addon.state === "started"
|
? addon.state === "started"
|
||||||
? "update"
|
? "update"
|
||||||
|
@@ -7,11 +7,7 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
|
||||||
HassioHomeAssistantInfo,
|
|
||||||
HassioSupervisorInfo,
|
|
||||||
} from "../../../src/data/hassio/supervisor";
|
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
@@ -23,37 +19,34 @@ import "./hassio-update";
|
|||||||
class HassioDashboard extends LitElement {
|
class HassioDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public hassInfo!: HassioHomeAssistantInfo;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
hassio
|
|
||||||
main-page
|
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${supervisorTabs}
|
.tabs=${supervisorTabs}
|
||||||
|
main-page
|
||||||
|
supervisor
|
||||||
>
|
>
|
||||||
<span slot="header">Dashboard</span>
|
<span slot="header">
|
||||||
|
${this.supervisor.localize("panel.dashboard")}
|
||||||
|
</span>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<hassio-update
|
<hassio-update
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.hassInfo=${this.hassInfo}
|
.supervisor=${this.supervisor}
|
||||||
.supervisorInfo=${this.supervisorInfo}
|
|
||||||
.hassOsInfo=${this.hassOsInfo}
|
|
||||||
></hassio-update>
|
></hassio-update>
|
||||||
<hassio-addons
|
<hassio-addons
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.addons=${this.supervisorInfo.addons}
|
.supervisor=${this.supervisor}
|
||||||
></hassio-addons>
|
></hassio-addons>
|
||||||
</div>
|
</div>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
|
@@ -10,56 +10,58 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/ha-settings-row";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
HassioResponse,
|
HassioResponse,
|
||||||
ignoredStatusCodes,
|
ignoreSupervisorError,
|
||||||
} from "../../../src/data/hassio/common";
|
} from "../../../src/data/hassio/common";
|
||||||
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
||||||
import {
|
import {
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
|
import { updateCore } from "../../../src/data/supervisor/core";
|
||||||
|
import {
|
||||||
|
Supervisor,
|
||||||
|
supervisorApiWsRequest,
|
||||||
|
} from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
|
const computeVersion = (key: string, version: string): string => {
|
||||||
|
return key === "os" ? version : `${key}-${version}`;
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("hassio-update")
|
@customElement("hassio-update")
|
||||||
export class HassioUpdate extends LitElement {
|
export class HassioUpdate extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassInfo?: HassioHomeAssistantInfo;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public hassOsInfo?: HassioHassOSInfo;
|
private _pendingUpdates = memoizeOne((supervisor: Supervisor): number => {
|
||||||
|
return Object.keys(supervisor).filter(
|
||||||
@property({ attribute: false }) public supervisorInfo?: HassioSupervisorInfo;
|
(value) => supervisor[value].update_available
|
||||||
|
|
||||||
private _pendingUpdates = memoizeOne(
|
|
||||||
(
|
|
||||||
core?: HassioHomeAssistantInfo,
|
|
||||||
supervisor?: HassioSupervisorInfo,
|
|
||||||
os?: HassioHassOSInfo
|
|
||||||
): number => {
|
|
||||||
return [core, supervisor, os].filter(
|
|
||||||
(value) => !!value && value?.update_available
|
|
||||||
).length;
|
).length;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const updatesAvailable = this._pendingUpdates(
|
if (!this.supervisor) {
|
||||||
this.hassInfo,
|
return html``;
|
||||||
this.supervisorInfo,
|
}
|
||||||
this.hassOsInfo
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const updatesAvailable = this._pendingUpdates(this.supervisor);
|
||||||
if (!updatesAvailable) {
|
if (!updatesAvailable) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
@@ -67,33 +69,37 @@ export class HassioUpdate extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>
|
<h1>
|
||||||
${updatesAvailable > 1
|
${this.supervisor.localize(
|
||||||
? "Updates Available 🎉"
|
"common.update_available",
|
||||||
: "Update Available 🎉"}
|
"count",
|
||||||
|
updatesAvailable
|
||||||
|
)}
|
||||||
|
🎉
|
||||||
</h1>
|
</h1>
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${this._renderUpdateCard(
|
${this._renderUpdateCard(
|
||||||
"Home Assistant Core",
|
"Home Assistant Core",
|
||||||
this.hassInfo!,
|
"core",
|
||||||
|
this.supervisor.core,
|
||||||
"hassio/homeassistant/update",
|
"hassio/homeassistant/update",
|
||||||
`https://${
|
`https://${
|
||||||
this.hassInfo?.version_latest.includes("b") ? "rc" : "www"
|
this.supervisor.core.version_latest.includes("b") ? "rc" : "www"
|
||||||
}.home-assistant.io/latest-release-notes/`
|
}.home-assistant.io/latest-release-notes/`
|
||||||
)}
|
)}
|
||||||
${this._renderUpdateCard(
|
${this._renderUpdateCard(
|
||||||
"Supervisor",
|
"Supervisor",
|
||||||
this.supervisorInfo!,
|
"supervisor",
|
||||||
|
this.supervisor.supervisor,
|
||||||
"hassio/supervisor/update",
|
"hassio/supervisor/update",
|
||||||
`https://github.com//home-assistant/hassio/releases/tag/${
|
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
||||||
this.supervisorInfo!.version_latest
|
|
||||||
}`
|
|
||||||
)}
|
)}
|
||||||
${this.hassOsInfo
|
${this.supervisor.host.features.includes("hassos")
|
||||||
? this._renderUpdateCard(
|
? this._renderUpdateCard(
|
||||||
"Operating System",
|
"Operating System",
|
||||||
this.hassOsInfo,
|
"os",
|
||||||
|
this.supervisor.os,
|
||||||
"hassio/os/update",
|
"hassio/os/update",
|
||||||
`https://github.com//home-assistant/hassos/releases/tag/${this.hassOsInfo.version_latest}`
|
`https://github.com//home-assistant/hassos/releases/tag/${this.supervisor.os.version_latest}`
|
||||||
)
|
)
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -103,6 +109,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
|
|
||||||
private _renderUpdateCard(
|
private _renderUpdateCard(
|
||||||
name: string,
|
name: string,
|
||||||
|
key: string,
|
||||||
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo,
|
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo,
|
||||||
apiPath: string,
|
apiPath: string,
|
||||||
releaseNotesUrl: string
|
releaseNotesUrl: string
|
||||||
@@ -116,22 +123,39 @@ export class HassioUpdate extends LitElement {
|
|||||||
<div class="icon">
|
<div class="icon">
|
||||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="update-heading">${name} ${object.version_latest}</div>
|
<div class="update-heading">${name}</div>
|
||||||
<div class="warning">
|
<ha-settings-row two-line>
|
||||||
You are currently running version ${object.version}
|
<span slot="heading">
|
||||||
</div>
|
${this.supervisor.localize("common.version")}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${computeVersion(key, object.version!)}
|
||||||
|
</span>
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
<ha-settings-row two-line>
|
||||||
|
<span slot="heading">
|
||||||
|
${this.supervisor.localize("common.newest_version")}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${computeVersion(key, object.version_latest!)}
|
||||||
|
</span>
|
||||||
|
</ha-settings-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
||||||
<mwc-button>Release notes</mwc-button>
|
<mwc-button>
|
||||||
|
${this.supervisor.localize("common.release_notes")}
|
||||||
|
</mwc-button>
|
||||||
</a>
|
</a>
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
.apiPath=${apiPath}
|
.apiPath=${apiPath}
|
||||||
.name=${name}
|
.name=${name}
|
||||||
|
.key=${key}
|
||||||
.version=${object.version_latest}
|
.version=${object.version_latest}
|
||||||
@click=${this._confirmUpdate}
|
@click=${this._confirmUpdate}
|
||||||
>
|
>
|
||||||
Update
|
${this.supervisor.localize("common.update")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@@ -140,12 +164,36 @@ export class HassioUpdate extends LitElement {
|
|||||||
|
|
||||||
private async _confirmUpdate(ev): Promise<void> {
|
private async _confirmUpdate(ev): Promise<void> {
|
||||||
const item = ev.currentTarget;
|
const item = ev.currentTarget;
|
||||||
|
if (item.key === "core") {
|
||||||
|
showDialogSupervisorUpdate(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
name: "Home Assistant Core",
|
||||||
|
version: this.supervisor.core.version_latest,
|
||||||
|
snapshotParams: {
|
||||||
|
name: `core_${this.supervisor.core.version}`,
|
||||||
|
folders: ["homeassistant"],
|
||||||
|
homeassistant: true,
|
||||||
|
},
|
||||||
|
updateHandler: async () => this._updateCore(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
item.progress = true;
|
item.progress = true;
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
title: `Update ${item.name}`,
|
title: this.supervisor.localize(
|
||||||
text: `Are you sure you want to update ${item.name} to version ${item.version}?`,
|
"confirm.update.title",
|
||||||
confirmText: "update",
|
"name",
|
||||||
dismissText: "cancel",
|
item.name
|
||||||
|
),
|
||||||
|
text: this.supervisor.localize(
|
||||||
|
"confirm.update.text",
|
||||||
|
"name",
|
||||||
|
item.name,
|
||||||
|
"version",
|
||||||
|
computeVersion(item.key, item.version)
|
||||||
|
),
|
||||||
|
confirmText: this.supervisor.localize("common.update"),
|
||||||
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
@@ -153,13 +201,24 @@ export class HassioUpdate extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
|
||||||
|
await supervisorApiWsRequest(this.hass.connection, {
|
||||||
|
method: "post",
|
||||||
|
endpoint: item.apiPath.replace("hassio", ""),
|
||||||
|
timeout: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
||||||
|
}
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: item.key,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Only show an error if the status code was not expected (user behind proxy)
|
// Only show an error if the status code was not expected (user behind proxy)
|
||||||
// or no status at all(connection terminated)
|
// or no status at all(connection terminated)
|
||||||
if (err.status_code && !ignoredStatusCodes.has(err.status_code)) {
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Update failed",
|
title: this.supervisor.localize("common.error.update_failed"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -167,6 +226,13 @@ export class HassioUpdate extends LitElement {
|
|||||||
item.progress = false;
|
item.progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _updateCore(): Promise<void> {
|
||||||
|
await updateCore(this.hass);
|
||||||
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
|
collection: "core",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@@ -184,9 +250,6 @@ export class HassioUpdate extends LitElement {
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
.warning {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
.card-content {
|
.card-content {
|
||||||
height: calc(100% - 47px);
|
height: calc(100% - 47px);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -194,13 +257,13 @@ export class HassioUpdate extends LitElement {
|
|||||||
.card-actions {
|
.card-actions {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
color: var(--error-color);
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
ha-settings-row {
|
||||||
|
padding: 0;
|
||||||
|
--paper-item-body-two-line-min-height: 32px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user