mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-13 11:19:25 +00:00
Compare commits
585 Commits
lit-grid-l
...
20210226.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
16fa6904d9 | ||
![]() |
b58c17e75e | ||
![]() |
ae590d42dc | ||
![]() |
d7917160c0 | ||
![]() |
01e4414d17 | ||
![]() |
0bc2eb530d | ||
![]() |
12b124e5a3 | ||
![]() |
478a4b2593 | ||
![]() |
9752e30eb4 | ||
![]() |
af6e87ba31 | ||
![]() |
64d390ad0f | ||
![]() |
c94bcb6896 | ||
![]() |
97f9df2f2d | ||
![]() |
4e7f68a86c | ||
![]() |
2f7f677549 | ||
![]() |
12a8a1531d | ||
![]() |
f44d867d3a | ||
![]() |
6f636187f7 | ||
![]() |
9414f89e50 | ||
![]() |
60bf1a5451 | ||
![]() |
32ba8f4731 | ||
![]() |
81f96de2bd | ||
![]() |
0c417755ed | ||
![]() |
93e5bde797 | ||
![]() |
c85f69c9ee | ||
![]() |
b6eaf0a7c5 | ||
![]() |
5f1851bade | ||
![]() |
5c66a02711 | ||
![]() |
bde925a0e3 | ||
![]() |
0f574a765b | ||
![]() |
782b941531 | ||
![]() |
f42c0a0717 | ||
![]() |
13ac14d449 | ||
![]() |
db9cea81db | ||
![]() |
7c1fd542da | ||
![]() |
54a2b2534a | ||
![]() |
f5fb6c1e03 | ||
![]() |
781c0701fc | ||
![]() |
742f1f85dc | ||
![]() |
a648e9be49 | ||
![]() |
fd9441dde2 | ||
![]() |
b5ec59c396 | ||
![]() |
60e4594abd | ||
![]() |
79692ef58a | ||
![]() |
ace7ee5622 | ||
![]() |
741ac679a0 | ||
![]() |
216526e391 | ||
![]() |
d76af2cb61 | ||
![]() |
b7d4c40736 | ||
![]() |
6092af8de6 | ||
![]() |
627424b8b9 | ||
![]() |
e33aff7cf3 | ||
![]() |
ef0bfb237a | ||
![]() |
c042c5568b | ||
![]() |
d84a7ee358 | ||
![]() |
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 | ||
![]() |
311e1cfb00 | ||
![]() |
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 | ||
![]() |
e37b7bd73f | ||
![]() |
d6f3c34b33 | ||
![]() |
bc5cb46e7d | ||
![]() |
c7b747c4fa | ||
![]() |
d3c51d7acd | ||
![]() |
b6881d797c | ||
![]() |
b9f802939c | ||
![]() |
6558c2c065 | ||
![]() |
37a089c868 | ||
![]() |
f68eff6bb3 | ||
![]() |
88a525f1a7 | ||
![]() |
7e6153ba7d | ||
![]() |
34fddd5940 | ||
![]() |
0e5d6fe8d8 | ||
![]() |
e1342a0d9d | ||
![]() |
0cc2d3aaa7 | ||
![]() |
67814505b3 | ||
![]() |
bae29c6d62 | ||
![]() |
a0e67d4c03 | ||
![]() |
131bc5fbf7 | ||
![]() |
051218e29b | ||
![]() |
6ace8307d8 | ||
![]() |
e84bef44b7 | ||
![]() |
3186d762f2 | ||
![]() |
c97a3b0a56 | ||
![]() |
78f1bb3b91 | ||
![]() |
67707fbc90 | ||
![]() |
2a57ffa615 | ||
![]() |
216fce74f8 | ||
![]() |
6cd3e6652a | ||
![]() |
fe7d79cee6 | ||
![]() |
2f4e7b388b | ||
![]() |
2e289cd152 | ||
![]() |
21a3dcf06c | ||
![]() |
7f56add914 | ||
![]() |
88701c6167 | ||
![]() |
e4ce6117a1 | ||
![]() |
cec2a61bdf | ||
![]() |
8275ac5853 | ||
![]() |
b7bcf97365 | ||
![]() |
fa28b480f1 | ||
![]() |
4bb95b7396 | ||
![]() |
5a9bd73e8b | ||
![]() |
4fe0276914 | ||
![]() |
5e8bda55b4 | ||
![]() |
d09c4898c1 | ||
![]() |
6ae67ed299 | ||
![]() |
32ff166a74 | ||
![]() |
8feae04281 | ||
![]() |
129f9c147b | ||
![]() |
6e336dd207 | ||
![]() |
161561c48a | ||
![]() |
c162e84383 | ||
![]() |
dc8d80a6e5 | ||
![]() |
293f67968c | ||
![]() |
4dcf26236e | ||
![]() |
a0e8d69243 | ||
![]() |
33cd9bf516 | ||
![]() |
0132797f2f | ||
![]() |
7e2db0aa4e | ||
![]() |
cc1d50491b | ||
![]() |
461b86a04b | ||
![]() |
9a3a7c28f4 | ||
![]() |
1c9d0200ca | ||
![]() |
0037cd2e69 | ||
![]() |
028ae061da | ||
![]() |
744efa30f2 | ||
![]() |
bf4a94dc48 | ||
![]() |
ce4ba2f6f1 | ||
![]() |
5b232b5d35 | ||
![]() |
35151bbac7 | ||
![]() |
e37eebe4ad | ||
![]() |
0baaaefdf8 | ||
![]() |
bec0d9b00e | ||
![]() |
e6a4ab789b | ||
![]() |
36c1d3230c | ||
![]() |
30466ec3fe | ||
![]() |
ce414a5ca9 | ||
![]() |
e4e6edd573 | ||
![]() |
79927f4dc9 | ||
![]() |
603b833757 | ||
![]() |
48543a2dad | ||
![]() |
b22f5ae5c2 | ||
![]() |
2acb6a28fe | ||
![]() |
1064cdb79d | ||
![]() |
bd7cb1c877 | ||
![]() |
6c314982dc | ||
![]() |
d54710f113 | ||
![]() |
1346156ecd | ||
![]() |
a2d9f9b417 | ||
![]() |
3de78cca2d | ||
![]() |
a27680b8c0 | ||
![]() |
73be0fef75 |
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
|
||||
if it seems unimportant to you. Please be sure to remove personal information
|
||||
like passwords, private URLs and other credentials.
|
||||
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.
|
||||
-->
|
||||
|
||||
```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
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
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.
|
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -18,8 +18,8 @@
|
||||
<!--
|
||||
Describe the big picture of your changes here to communicate to the
|
||||
maintainers why we should accept this pull request. If it fixes a bug
|
||||
or resolves a feature request, be sure to link to that issue in the
|
||||
additional information section.
|
||||
or resolves a feature request, be sure to link to that issue or discussion
|
||||
in the additional information section.
|
||||
-->
|
||||
|
||||
## Type of change
|
||||
@@ -56,7 +56,7 @@
|
||||
-->
|
||||
|
||||
- This PR fixes or closes issue: fixes #
|
||||
- This PR is related to issue:
|
||||
- This PR is related to issue or discussion:
|
||||
- Link to documentation pull request:
|
||||
|
||||
## Checklist
|
||||
|
27
.github/lock.yml
vendored
27
.github/lock.yml
vendored
@@ -1,27 +0,0 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 1
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: 2020-01-01
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: false
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: false
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: pulls
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
issues:
|
||||
daysUntilLock: 30
|
56
.github/stale.yml
vendored
56
.github/stale.yml
vendored
@@ -1,56 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 90
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- feature request
|
||||
- Help wanted
|
||||
- to do
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: true
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: true
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
There hasn't been any activity on this issue recently. Due to the high number
|
||||
of incoming GitHub notifications, we have to clean some of the old issues,
|
||||
as many of them have already been resolved with the latest updates.
|
||||
|
||||
Please make sure to update to the latest Home Assistant version and check
|
||||
if that solves the issue. Let us know if that works for you by adding a
|
||||
comment 👍
|
||||
|
||||
This issue now has been marked as stale and will be closed if no further
|
||||
activity occurs. Thank you for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
20
.github/workflows/lock.yml
vendored
Normal file
20
.github/workflows/lock.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Lock
|
||||
|
||||
# yamllint disable-line rule:truthy
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: "30"
|
||||
issue-exclude-created-before: "2020-10-01T00:00:00Z"
|
||||
issue-lock-reason: ""
|
||||
pr-lock-inactive-days: "1"
|
||||
pr-exclude-created-before: "2020-11-01T00:00:00Z"
|
||||
pr-lock-reason: ""
|
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"
|
42
.github/workflows/stale.yml
vendored
Normal file
42
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Stale
|
||||
|
||||
# yamllint disable-line rule:truthy
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 90 days stale policy
|
||||
uses: actions/stale@v3.0.13
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
operations-per-run: 25
|
||||
remove-stale-when-updated: true
|
||||
stale-issue-label: "stale"
|
||||
exempt-issue-labels: "no-stale,Help%20wanted,help-wanted,feature-request,feature%20request"
|
||||
stale-issue-message: >
|
||||
There hasn't been any activity on this issue recently. Due to the
|
||||
high number of incoming GitHub notifications, we have to clean some
|
||||
of the old issues, as many of them have already been resolved with
|
||||
the latest updates.
|
||||
|
||||
Please make sure to update to the latest Home Assistant version and
|
||||
check if that solves the issue. Let us know if that works for you by
|
||||
adding a comment 👍
|
||||
|
||||
This issue has now been marked as stale and will be closed if no
|
||||
further activity occurs. Thank you for your contributions.
|
||||
|
||||
stale-pr-label: "stale"
|
||||
exempt-pr-labels: "no-stale"
|
||||
stale-pr-message: >
|
||||
There hasn't been any activity on this pull request recently. This
|
||||
pull request has been automatically marked as stale because of that
|
||||
and will be closed if no further activity occurs within 7 days.
|
||||
|
||||
Thank you for your contributions.
|
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:
|
||||
- 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
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -23,6 +23,8 @@ dist
|
||||
# vscode
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/tasks.json
|
||||
|
||||
# Cast dev settings
|
||||
src/cast/dev_const.ts
|
||||
@@ -33,3 +35,6 @@ yarn-error.log
|
||||
|
||||
#asdf
|
||||
.tool-versions
|
||||
|
||||
# Home Assistant config
|
||||
/config
|
||||
|
@@ -1,6 +0,0 @@
|
||||
jshint:
|
||||
enabled: false
|
||||
|
||||
eslint:
|
||||
enabled: true
|
||||
config_file: .eslintrc-hound.json
|
44
.vscode/launch.json
vendored
Normal file
44
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
// https://github.com/microsoft/vscode-js-debug/blob/master/OPTIONS.md
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Frontend",
|
||||
"request": "launch",
|
||||
"type": "pwa-chrome",
|
||||
"url": "http://localhost:8123/",
|
||||
"webRoot": "${workspaceFolder}/hass_frontend",
|
||||
"disableNetworkCache": true,
|
||||
"preLaunchTask": "Develop Frontend",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/hass_frontend/frontend_latest/*.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug Gallery",
|
||||
"request": "launch",
|
||||
"type": "pwa-chrome",
|
||||
"url": "http://localhost:8100/",
|
||||
"webRoot": "${workspaceFolder}/gallery/dist",
|
||||
"disableNetworkCache": true,
|
||||
"preLaunchTask": "Develop Gallery"
|
||||
},
|
||||
{
|
||||
"name": "Debug Demo",
|
||||
"request": "launch",
|
||||
"type": "pwa-chrome",
|
||||
"url": "http://localhost:8090/",
|
||||
"webRoot": "${workspaceFolder}/demo/dist",
|
||||
"disableNetworkCache": true,
|
||||
"preLaunchTask": "Develop Demo"
|
||||
},
|
||||
{
|
||||
"name": "Debug Cast",
|
||||
"request": "launch",
|
||||
"type": "pwa-chrome",
|
||||
"url": "http://localhost:8080/",
|
||||
"webRoot": "${workspaceFolder}/cast/dist",
|
||||
"disableNetworkCache": true,
|
||||
"preLaunchTask": "Develop Cast"
|
||||
},
|
||||
]
|
||||
}
|
208
.vscode/tasks.json
vendored
Normal file
208
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Develop Frontend",
|
||||
"type": "gulp",
|
||||
"task": "develop-app",
|
||||
// Sync changes here to other tasks until issue resolved
|
||||
// https://github.com/Microsoft/vscode/issues/61497
|
||||
"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": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"runOptions": {
|
||||
"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",
|
||||
"type": "gulp",
|
||||
"task": "develop-gallery",
|
||||
"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 Demo",
|
||||
"type": "gulp",
|
||||
"task": "develop-demo",
|
||||
"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 Cast",
|
||||
"type": "gulp",
|
||||
"task": "develop-cast",
|
||||
"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": "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/)
|
||||
- Production build: `script/build_frontend`
|
||||
- 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
|
||||
|
||||
|
@@ -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) => ({
|
||||
safari10: true,
|
||||
safari10: !latestBuild,
|
||||
ecma: latestBuild ? undefined : 5,
|
||||
output: { comments: false },
|
||||
});
|
||||
@@ -117,7 +117,7 @@ BundleConfig {
|
||||
*/
|
||||
|
||||
module.exports.config = {
|
||||
app({ isProdBuild, latestBuild, isStatsBuild }) {
|
||||
app({ isProdBuild, latestBuild, isStatsBuild, isWDS }) {
|
||||
return {
|
||||
entry: {
|
||||
service_worker: "./src/entrypoints/service_worker.ts",
|
||||
@@ -132,6 +132,7 @@ module.exports.config = {
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isWDS,
|
||||
};
|
||||
},
|
||||
|
||||
|
@@ -6,6 +6,9 @@ module.exports = {
|
||||
useRollup() {
|
||||
return process.env.ROLLUP === "1";
|
||||
},
|
||||
useWDS() {
|
||||
return process.env.WDS === "1";
|
||||
},
|
||||
isProdBuild() {
|
||||
return (
|
||||
process.env.NODE_ENV === "production" || module.exports.isStatsBuild()
|
||||
|
@@ -12,6 +12,7 @@ require("./webpack.js");
|
||||
require("./service-worker.js");
|
||||
require("./entry-html.js");
|
||||
require("./rollup.js");
|
||||
require("./wds.js");
|
||||
|
||||
gulp.task(
|
||||
"develop-app",
|
||||
@@ -28,7 +29,11 @@ gulp.task(
|
||||
"build-translations"
|
||||
),
|
||||
"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({
|
||||
...data,
|
||||
useRollup: env.useRollup(),
|
||||
useWDS: env.useWDS(),
|
||||
renderTemplate,
|
||||
});
|
||||
};
|
||||
@@ -90,10 +91,23 @@ gulp.task("gen-pages-prod", (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", {
|
||||
latestAppJS: "/frontend_latest/app.js",
|
||||
latestCoreJS: "/frontend_latest/core.js",
|
||||
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
|
||||
latestAppJS,
|
||||
latestCoreJS,
|
||||
latestCustomPanelJS,
|
||||
|
||||
es5AppJS: "/frontend_es5/app.js",
|
||||
es5CoreJS: "/frontend_es5/core.js",
|
||||
|
@@ -33,21 +33,10 @@ String.prototype.rsplit = function (sep, maxsplit) {
|
||||
: split;
|
||||
};
|
||||
|
||||
// Panel translations which should be split from the core translations. These
|
||||
// should mirror the fragment definitions in polymer.json, so that we load
|
||||
// additional resources at equivalent points.
|
||||
const TRANSLATION_FRAGMENTS = [
|
||||
"config",
|
||||
"history",
|
||||
"logbook",
|
||||
"mailbox",
|
||||
"profile",
|
||||
"shopping-list",
|
||||
"page-authorize",
|
||||
"page-demo",
|
||||
"page-onboarding",
|
||||
"developer-tools",
|
||||
];
|
||||
// Panel translations which should be split from the core translations.
|
||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
require("../../src/translations/en.json").ui.panel
|
||||
);
|
||||
|
||||
function recursiveFlatten(prefix, data) {
|
||||
let output = {};
|
||||
|
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,
|
||||
},
|
||||
});
|
||||
});
|
@@ -18,6 +18,14 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
createConfigFunc({ ...params, latestBuild: false }),
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* compiler: import("webpack").Compiler,
|
||||
* contentBase: string,
|
||||
* port: number,
|
||||
* listenHost?: string
|
||||
* }}
|
||||
*/
|
||||
const runDevServer = ({
|
||||
compiler,
|
||||
contentBase,
|
||||
@@ -33,10 +41,13 @@ const runDevServer = ({
|
||||
throw err;
|
||||
}
|
||||
// Server listening
|
||||
log("[webpack-dev-server]", `http://localhost:${port}`);
|
||||
log(
|
||||
"[webpack-dev-server]",
|
||||
`Project is running at http://localhost:${port}`
|
||||
);
|
||||
});
|
||||
|
||||
const handler = (done) => (err, stats) => {
|
||||
const doneHandler = (done) => (err, stats) => {
|
||||
if (err) {
|
||||
log.error(err.stack || err);
|
||||
if (err.details) {
|
||||
@@ -45,22 +56,31 @@ const handler = (done) => (err, stats) => {
|
||||
return;
|
||||
}
|
||||
|
||||
log(`Build done @ ${new Date().toLocaleTimeString()}`);
|
||||
|
||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||
log.warn(stats.toString("minimal"));
|
||||
console.log(stats.toString("minimal"));
|
||||
}
|
||||
|
||||
log(`Build done @ ${new Date().toLocaleTimeString()}`);
|
||||
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
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", () => {
|
||||
// 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(
|
||||
{ ignored: /build-translations/ },
|
||||
handler()
|
||||
doneHandler()
|
||||
);
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
@@ -68,15 +88,12 @@ gulp.task("webpack-watch-app", () => {
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"webpack-prod-app",
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
bothBuilds(createAppConfig, { isProdBuild: true }),
|
||||
handler(resolve)
|
||||
)
|
||||
)
|
||||
gulp.task("webpack-prod-app", () =>
|
||||
prodBuild(
|
||||
bothBuilds(createAppConfig, {
|
||||
isProdBuild: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-demo", () => {
|
||||
@@ -87,17 +104,12 @@ gulp.task("webpack-dev-server-demo", () => {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"webpack-prod-demo",
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
bothBuilds(createDemoConfig, {
|
||||
isProdBuild: true,
|
||||
}),
|
||||
handler(resolve)
|
||||
)
|
||||
)
|
||||
gulp.task("webpack-prod-demo", () =>
|
||||
prodBuild(
|
||||
bothBuilds(createDemoConfig, {
|
||||
isProdBuild: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-cast", () => {
|
||||
@@ -110,41 +122,30 @@ gulp.task("webpack-dev-server-cast", () => {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"webpack-prod-cast",
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
bothBuilds(createCastConfig, {
|
||||
isProdBuild: true,
|
||||
}),
|
||||
|
||||
handler(resolve)
|
||||
)
|
||||
)
|
||||
gulp.task("webpack-prod-cast", () =>
|
||||
prodBuild(
|
||||
bothBuilds(createCastConfig, {
|
||||
isProdBuild: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
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(
|
||||
createHassioConfig({
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({}, handler());
|
||||
).watch({}, doneHandler());
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"webpack-prod-hassio",
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
bothBuilds(createHassioConfig, {
|
||||
isProdBuild: true,
|
||||
}),
|
||||
handler(resolve)
|
||||
)
|
||||
)
|
||||
gulp.task("webpack-prod-hassio", () =>
|
||||
prodBuild(
|
||||
bothBuilds(createHassioConfig, {
|
||||
isProdBuild: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task("webpack-dev-server-gallery", () => {
|
||||
@@ -156,17 +157,11 @@ gulp.task("webpack-dev-server-gallery", () => {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"webpack-prod-gallery",
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
createGalleryConfig({
|
||||
isProdBuild: true,
|
||||
latestBuild: true,
|
||||
}),
|
||||
|
||||
handler(resolve)
|
||||
)
|
||||
)
|
||||
gulp.task("webpack-prod-gallery", () =>
|
||||
prodBuild(
|
||||
createGalleryConfig({
|
||||
isProdBuild: true,
|
||||
latestBuild: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
var path = require("path");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
polymer_dir: path.resolve(__dirname, ".."),
|
||||
|
@@ -1,5 +1,3 @@
|
||||
const path = require("path");
|
||||
|
||||
module.exports = function (userOptions = {}) {
|
||||
// Files need to be absolute paths.
|
||||
// This only works if the file has no exports
|
||||
|
@@ -3,7 +3,7 @@ const path = require("path");
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
const resolve = require("@rollup/plugin-node-resolve");
|
||||
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 visualizer = require("rollup-plugin-visualizer");
|
||||
const { string } = require("rollup-plugin-string");
|
||||
@@ -31,6 +31,7 @@ const createRollupConfig = ({
|
||||
isStatsBuild,
|
||||
publicPath,
|
||||
dontHash,
|
||||
isWDS,
|
||||
}) => {
|
||||
return {
|
||||
/**
|
||||
@@ -61,6 +62,7 @@ const createRollupConfig = ({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
exclude: bundle.babelExclude(),
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
// Import certain extensions as strings
|
||||
@@ -69,19 +71,21 @@ const createRollupConfig = ({
|
||||
replace(
|
||||
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
|
||||
),
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
worker(),
|
||||
dontHashPlugin({ dontHash }),
|
||||
isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
isStatsBuild &&
|
||||
!isWDS &&
|
||||
manifest({
|
||||
publicPath,
|
||||
}),
|
||||
!isWDS && worker(),
|
||||
!isWDS && dontHashPlugin({ dontHash }),
|
||||
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
|
||||
!isWDS &&
|
||||
isStatsBuild &&
|
||||
visualizer({
|
||||
// https://github.com/btd/rollup-plugin-visualizer#options
|
||||
open: true,
|
||||
sourcemap: true,
|
||||
}),
|
||||
],
|
||||
].filter(Boolean),
|
||||
},
|
||||
/**
|
||||
* @type { import("rollup").OutputOptions }
|
||||
@@ -108,12 +112,13 @@ const createRollupConfig = ({
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
|
||||
return createRollupConfig(
|
||||
bundle.config.app({
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
isWDS,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@@ -1,9 +1,24 @@
|
||||
const webpack = require("webpack");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle");
|
||||
const log = require("fancy-log");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
ignoredFirst = false;
|
||||
|
||||
apply(compiler) {
|
||||
compiler.hooks.beforeCompile.tap("LogStartCompilePlugin", () => {
|
||||
if (!this.ignoredFirst) {
|
||||
this.ignoredFirst = true;
|
||||
return;
|
||||
}
|
||||
log("Changes detected. Starting compilation");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const createWebpackConfig = ({
|
||||
entry,
|
||||
@@ -21,6 +36,7 @@ const createWebpackConfig = ({
|
||||
const ignorePackages = bundle.ignorePackages({ latestBuild });
|
||||
return {
|
||||
mode: isProdBuild ? "production" : "development",
|
||||
target: ["web", latestBuild ? "es2017" : "es5"],
|
||||
devtool: isProdBuild
|
||||
? "cheap-module-source-map"
|
||||
: "eval-cheap-module-source-map",
|
||||
@@ -52,7 +68,7 @@ const createWebpackConfig = ({
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new ManifestPlugin({
|
||||
new WebpackManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
}),
|
||||
@@ -104,7 +120,8 @@ const createWebpackConfig = ({
|
||||
),
|
||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
||||
),
|
||||
],
|
||||
!isProdBuild && new LogStartCompilePlugin(),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
},
|
||||
@@ -115,22 +132,6 @@ const createWebpackConfig = ({
|
||||
}
|
||||
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:
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
|
@@ -48,7 +48,7 @@ class HcCast extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
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 =
|
||||
|
@@ -98,8 +98,12 @@ class HcLayout extends LitElement {
|
||||
line-height: 32px;
|
||||
padding: 24px 16px 16px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hero {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: var(--secondary-text-color);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
property,
|
||||
internalProperty,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../src/resources/roboto";
|
||||
import "../../../src/resources/ha-style";
|
||||
import "../../../src/resources/roboto";
|
||||
import "./layout/hc-lovelace";
|
||||
|
@@ -54,6 +54,8 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
state: "21",
|
||||
attributes: {
|
||||
friendly_name: "Living room temperature",
|
||||
device_class: "temperature",
|
||||
unit_of_measurement: "°C",
|
||||
},
|
||||
},
|
||||
"sensor.study_temp_rounded": {
|
||||
@@ -61,6 +63,8 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
state: "23",
|
||||
attributes: {
|
||||
friendly_name: "Study temperature",
|
||||
device_class: "temperature",
|
||||
unit_of_measurement: "°C",
|
||||
},
|
||||
},
|
||||
"sensor.living_room": {
|
||||
@@ -261,7 +265,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen lights",
|
||||
friendly_name: "Kitchen Lights",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
@@ -484,7 +488,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
attributes: {
|
||||
min_mireds: 111,
|
||||
max_mireds: 400,
|
||||
friendly_name: "Garage lights",
|
||||
friendly_name: "Garage Lights",
|
||||
supported_features: 55,
|
||||
},
|
||||
},
|
||||
|
@@ -12,6 +12,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
{
|
||||
type: "entities",
|
||||
title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"),
|
||||
state_color: true,
|
||||
entities: [
|
||||
{
|
||||
entity: "light.kitchen_lights",
|
||||
|
@@ -3,22 +3,10 @@ import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { DemoConfig } from "./types";
|
||||
|
||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
() =>
|
||||
import(/* webpackChunkName: "arsaboo" */ "./arsaboo").then(
|
||||
(mod) => mod.demoArsaboo
|
||||
),
|
||||
() =>
|
||||
import(/* webpackChunkName: "teachingbirds" */ "./teachingbirds").then(
|
||||
(mod) => mod.demoTeachingbirds
|
||||
),
|
||||
() =>
|
||||
import(/* webpackChunkName: "kernehed" */ "./kernehed").then(
|
||||
(mod) => mod.demoKernehed
|
||||
),
|
||||
() =>
|
||||
import(/* webpackChunkName: "jimpower" */ "./jimpower").then(
|
||||
(mod) => mod.demoJimpower
|
||||
),
|
||||
() => import("./arsaboo").then((mod) => mod.demoArsaboo),
|
||||
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds),
|
||||
() => import("./kernehed").then((mod) => mod.demoKernehed),
|
||||
() => import("./jimpower").then((mod) => mod.demoJimpower),
|
||||
];
|
||||
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
|
@@ -653,7 +653,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||
entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7",
|
||||
state: "off",
|
||||
attributes: {
|
||||
Density: 0,
|
||||
density: 0,
|
||||
battery_level: 59,
|
||||
friendly_name: "Downstairs Smoke Detector",
|
||||
device_class: "smoke",
|
||||
@@ -663,7 +663,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||
entity_id: "binary_sensor.smoke_sensor_158d0001b8deba",
|
||||
state: "off",
|
||||
attributes: {
|
||||
Density: 0,
|
||||
density: 0,
|
||||
battery_level: 65,
|
||||
friendly_name: "Upstairs Smoke Detector",
|
||||
device_class: "smoke",
|
||||
|
@@ -3,49 +3,7 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
name: "Kingia Castle",
|
||||
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",
|
||||
// },
|
||||
],
|
||||
resources: [],
|
||||
views: [
|
||||
{
|
||||
cards: [
|
||||
@@ -603,89 +561,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
},
|
||||
{
|
||||
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: {
|
||||
"border-radius": "20px",
|
||||
@@ -722,46 +597,8 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
],
|
||||
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",
|
||||
name: "Home",
|
||||
background:
|
||||
@@ -881,26 +718,13 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
card: {
|
||||
image: "/assets/jimpower/security/air_8.jpg",
|
||||
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: {
|
||||
color: "hsl(120, 41%, 39%)",
|
||||
top: "50%",
|
||||
"font-weight": 600,
|
||||
"font-size": "20px",
|
||||
left: "44%",
|
||||
"font-size": "50px",
|
||||
left: "30%",
|
||||
},
|
||||
type: "state-label",
|
||||
entity: "sensor.us_air_pollution_level_2",
|
||||
@@ -920,7 +744,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
style: {
|
||||
color: "white",
|
||||
top: "80%",
|
||||
left: "52%",
|
||||
left: "48%",
|
||||
},
|
||||
type: "state-icon",
|
||||
entity: "sensor.us_main_pollutant_2",
|
||||
@@ -1411,6 +1235,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
||||
type: "vertical-stack",
|
||||
},
|
||||
],
|
||||
path: "security",
|
||||
icon: "hass:shield-home",
|
||||
name: "Security",
|
||||
background:
|
||||
|
@@ -101,7 +101,12 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
"sensor.zwave_battery_front_door": {
|
||||
entity_id: "sensor.zwave_battery_front_door",
|
||||
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": {
|
||||
entity_id: "sensor.oskar_devices",
|
||||
@@ -164,7 +169,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
},
|
||||
"input_select.christmas_pattern": {
|
||||
entity_id: "input_select.christmas_pattern",
|
||||
state: "None",
|
||||
state: "Rainbow",
|
||||
attributes: {
|
||||
options: [
|
||||
"None",
|
||||
@@ -186,7 +191,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
},
|
||||
"input_select.christmas_palette": {
|
||||
entity_id: "input_select.christmas_palette",
|
||||
state: "None",
|
||||
state: "Party",
|
||||
attributes: {
|
||||
options: [
|
||||
"None",
|
||||
@@ -457,7 +462,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
state: "0.0",
|
||||
attributes: {
|
||||
unit_of_measurement: "kB/s",
|
||||
friendly_name: "Nedladdning",
|
||||
friendly_name: "Downloading",
|
||||
icon: "mdi:file-download",
|
||||
},
|
||||
},
|
||||
@@ -471,7 +476,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
state: "0.0",
|
||||
attributes: {
|
||||
unit_of_measurement: "kB/s",
|
||||
friendly_name: "Uppladdning",
|
||||
friendly_name: "Uploading",
|
||||
icon: "mdi:file-upload",
|
||||
},
|
||||
},
|
||||
|
@@ -2,44 +2,7 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
name: "Hem",
|
||||
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",
|
||||
// },
|
||||
],
|
||||
resources: [],
|
||||
views: [
|
||||
{
|
||||
cards: [
|
||||
@@ -64,7 +27,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
style: {
|
||||
color: "white",
|
||||
top: "93%",
|
||||
left: "90%",
|
||||
left: "85%",
|
||||
},
|
||||
type: "state-label",
|
||||
entity: "sensor.battery_oskar",
|
||||
@@ -87,7 +50,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
{
|
||||
style: {
|
||||
color: "white",
|
||||
top: "92%",
|
||||
top: "93%",
|
||||
left: "20%",
|
||||
},
|
||||
type: "state-label",
|
||||
@@ -96,8 +59,8 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
{
|
||||
style: {
|
||||
color: "white",
|
||||
top: "92%",
|
||||
left: "90%",
|
||||
top: "93%",
|
||||
left: "85%",
|
||||
},
|
||||
type: "state-label",
|
||||
entity: "sensor.battery_bella",
|
||||
@@ -105,7 +68,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
{
|
||||
style: {
|
||||
color: "white",
|
||||
top: "92%",
|
||||
top: "93%",
|
||||
left: "55%",
|
||||
},
|
||||
type: "state-label",
|
||||
@@ -131,78 +94,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
type: "entities",
|
||||
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: [
|
||||
{
|
||||
@@ -226,10 +117,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
],
|
||||
type: "vertical-stack",
|
||||
},
|
||||
// {
|
||||
// url: "https://embed.windy.com/embed2.html",
|
||||
// type: "iframe",
|
||||
// },
|
||||
{
|
||||
entities: [
|
||||
{
|
||||
@@ -263,6 +150,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
],
|
||||
type: "glance",
|
||||
show_state: false,
|
||||
columns: 4,
|
||||
},
|
||||
{
|
||||
entities: ["sensor.oskar_bluetooth"],
|
||||
@@ -270,32 +158,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
type: "entities",
|
||||
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",
|
||||
entity: "weather.smhi_vader",
|
||||
@@ -378,41 +240,9 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
"binary_sensor.windows_server",
|
||||
"binary_sensor.teamspeak",
|
||||
"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,
|
||||
state_color: true,
|
||||
type: "entities",
|
||||
title: "Network",
|
||||
},
|
||||
@@ -422,29 +252,10 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
|
||||
"binary_sensor.ubiquiti_switch",
|
||||
"binary_sensor.ubiquiti_nvr",
|
||||
"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",
|
||||
],
|
||||
show_header_toggle: false,
|
||||
state_color: true,
|
||||
type: "entities",
|
||||
title: "Ubiquiti",
|
||||
},
|
||||
|
@@ -215,6 +215,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
card: {
|
||||
type: "glance",
|
||||
show_state: false,
|
||||
columns: 4,
|
||||
},
|
||||
state_filter: ["on"],
|
||||
},
|
||||
@@ -808,67 +809,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
],
|
||||
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",
|
||||
title: "Lights",
|
||||
@@ -918,10 +858,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
name: "Dafang",
|
||||
icon: "mdi:webcam",
|
||||
},
|
||||
{
|
||||
name: "IR Hallway",
|
||||
entity: "sensor.system_ir_blaster",
|
||||
},
|
||||
{
|
||||
name: "IR Bedroom",
|
||||
entity: "sensor.system_ir_blaster_bedroom",
|
||||
@@ -940,7 +876,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
"sensor.system_ring_chime",
|
||||
],
|
||||
type: "glance",
|
||||
columns: 5,
|
||||
columns: 4,
|
||||
show_state: false,
|
||||
},
|
||||
{
|
||||
|
@@ -9,5 +9,5 @@ export interface DemoConfig {
|
||||
authorUrl: string;
|
||||
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
|
||||
entities: (localize: LocalizeFunc) => Entity[];
|
||||
theme: () => { [key: string]: string } | null;
|
||||
theme: () => Record<string, string> | null;
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CastManager } from "../../../src/cast/cast_manager";
|
||||
|
@@ -3,9 +3,9 @@ import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
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-repeat";
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
import "./ha-demo";
|
||||
|
||||
/* polyfill for paper-dropdown */
|
||||
setTimeout(() => {
|
||||
import(
|
||||
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
||||
);
|
||||
import("web-animations-js/web-animations-next-lite.min");
|
||||
}, 1000);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// Compat needs to be first import
|
||||
import "../../src/resources/compatibility";
|
||||
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
||||
import { navigate } from "../../src/common/navigate";
|
||||
|
BIN
gallery/public/images/sunflowers.jpg
Normal file
BIN
gallery/public/images/sunflowers.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
@@ -21,15 +21,16 @@ class DemoCard extends PolymerElement {
|
||||
}
|
||||
pre {
|
||||
width: 400px;
|
||||
margin: 16px;
|
||||
margin: 0 16px;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
@media only screen and (max-width: 800px) {
|
||||
.root {
|
||||
flex-direction: column;
|
||||
}
|
||||
pre {
|
||||
margin-left: 0;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -2,10 +2,10 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "../../../src/components/ha-switch";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "./demo-card";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
import "./demo-card";
|
||||
|
||||
class DemoCards extends PolymerElement {
|
||||
static get template() {
|
||||
|
@@ -2,58 +2,62 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/state-summary/state-card-content";
|
||||
import "../../../src/dialogs/more-info/more-info-content";
|
||||
import "../../../src/state-summary/state-card-content";
|
||||
|
||||
class DemoMoreInfo extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
#card {
|
||||
max-width: 400px;
|
||||
width: 100vw;
|
||||
}
|
||||
ha-card {
|
||||
width: 333px;
|
||||
width: 352px;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
state-card-content {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
pre {
|
||||
width: 400px;
|
||||
margin: 16px;
|
||||
margin: 0 16px;
|
||||
overflow: auto;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
:host {
|
||||
.root {
|
||||
flex-direction: column;
|
||||
}
|
||||
pre {
|
||||
margin-left: 0;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<ha-card>
|
||||
<state-card-content
|
||||
state-obj="[[_stateObj]]"
|
||||
hass="[[hass]]"
|
||||
in-dialog
|
||||
></state-card-content>
|
||||
<div class="root">
|
||||
<div id="card">
|
||||
<ha-card>
|
||||
<state-card-content
|
||||
state-obj="[[_stateObj]]"
|
||||
hass="[[hass]]"
|
||||
in-dialog
|
||||
></state-card-content>
|
||||
|
||||
<more-info-content
|
||||
hass="[[hass]]"
|
||||
state-obj="[[_stateObj]]"
|
||||
></more-info-content>
|
||||
</ha-card>
|
||||
<template is="dom-if" if="[[showConfig]]">
|
||||
<pre>[[_jsonEntity(_stateObj)]]</pre>
|
||||
</template>
|
||||
<more-info-content
|
||||
hass="[[hass]]"
|
||||
state-obj="[[_stateObj]]"
|
||||
></more-info-content>
|
||||
</ha-card>
|
||||
</div>
|
||||
<template is="dom-if" if="[[showConfig]]">
|
||||
<pre>[[_jsonEntity(_stateObj)]]</pre>
|
||||
</template>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
import "./demo-more-info";
|
||||
|
||||
@@ -9,6 +11,10 @@ class DemoMoreInfos extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
#container {
|
||||
min-height: calc(100vh - 128px);
|
||||
background: var(--primary-background-color);
|
||||
}
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -23,20 +29,31 @@ class DemoMoreInfos extends PolymerElement {
|
||||
.filters {
|
||||
margin-left: 60px;
|
||||
}
|
||||
ha-formfield {
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
<app-toolbar>
|
||||
<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>
|
||||
</app-toolbar>
|
||||
<div class="cards">
|
||||
<template is="dom-repeat" items="[[entities]]">
|
||||
<demo-more-info
|
||||
entity-id="[[item]]"
|
||||
show-config="[[_showConfig]]"
|
||||
hass="[[hass]]"
|
||||
></demo-more-info>
|
||||
</template>
|
||||
<div id="container">
|
||||
<div class="cards">
|
||||
<template is="dom-repeat" items="[[entities]]">
|
||||
<demo-more-info
|
||||
entity-id="[[item]]"
|
||||
show-config="[[_showConfig]]"
|
||||
hass="[[hass]]"
|
||||
></demo-more-info>
|
||||
</template>
|
||||
</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);
|
||||
|
@@ -6,6 +6,8 @@ export const createMediaPlayerEntities = () => [
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover_2.jpg",
|
||||
media_duration: 300,
|
||||
@@ -14,13 +16,16 @@ export const createMediaPlayerEntities = () => [
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
}),
|
||||
getEntity("media_player", "music_playing", "playing", {
|
||||
friendly_name: "Playing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
supported_features: 64063,
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
|
||||
supported_features: 195135,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
@@ -28,6 +33,7 @@ export const createMediaPlayerEntities = () => [
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
}),
|
||||
getEntity("media_player", "stream_playing", "playing", {
|
||||
friendly_name: "Playing the Stream",
|
||||
@@ -35,50 +41,125 @@ export const createMediaPlayerEntities = () => [
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
supported_features: 33,
|
||||
// Pause + Next Track + Play + Browse Media
|
||||
supported_features: 147489,
|
||||
}),
|
||||
getEntity("media_player", "living_room", "playing", {
|
||||
friendly_name: "Pause, No skip, tvshow",
|
||||
getEntity("media_player", "stream_paused", "paused", {
|
||||
friendly_name: "Paused the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play
|
||||
supported_features: 16417,
|
||||
}),
|
||||
getEntity("media_player", "stream_playing_previous", "playing", {
|
||||
friendly_name: 'Playing the Stream (with "previous" support)',
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Previous Track + Play
|
||||
supported_features: 16401,
|
||||
}),
|
||||
getEntity("media_player", "tv_playing", "playing", {
|
||||
friendly_name: "Playing non-skip TV Show",
|
||||
media_content_type: "tvshow",
|
||||
media_title: "Chapter 1",
|
||||
media_series_title: "House of Cards",
|
||||
app_name: "Netflix",
|
||||
entity_picture: "/images/netflix.jpg",
|
||||
// Pause
|
||||
supported_features: 1,
|
||||
}),
|
||||
getEntity("media_player", "sonos_idle", "idle", {
|
||||
friendly_name: "Sonos Idle",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
volume_level: 0.33,
|
||||
is_volume_muted: true,
|
||||
}),
|
||||
getEntity("media_player", "theater", "off", {
|
||||
getEntity("media_player", "idle_browse_media", "idle", {
|
||||
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
|
||||
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Play + Shuffle Set + Browse Media
|
||||
supported_features: 182839,
|
||||
volume_level: 0.79,
|
||||
}),
|
||||
getEntity("media_player", "theater_off", "off", {
|
||||
friendly_name: "TV Off",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
}),
|
||||
getEntity("media_player", "theater_on", "on", {
|
||||
friendly_name: "TV On",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
}),
|
||||
getEntity("media_player", "theater_off_static", "off", {
|
||||
friendly_name: "TV Off (cannot be switched on)",
|
||||
// Off + Next + Pause
|
||||
supported_features: 289,
|
||||
}),
|
||||
getEntity("media_player", "theater_on_static", "on", {
|
||||
friendly_name: "TV On (cannot be switched off)",
|
||||
// On + Next + Pause
|
||||
supported_features: 161,
|
||||
}),
|
||||
getEntity("media_player", "android_cast", "playing", {
|
||||
friendly_name: "Casting App",
|
||||
friendly_name: "Casting App (no supported features)",
|
||||
media_title: "Android Screen Casting",
|
||||
app_name: "Screen Mirroring",
|
||||
// supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "image_display", "playing", {
|
||||
friendly_name: "Digital Picture Frame",
|
||||
media_content_type: "image",
|
||||
media_title: "Famous Painting",
|
||||
media_artist: "Famous Artist",
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
// On + Off + Browse Media
|
||||
supported_features: 131456,
|
||||
}),
|
||||
getEntity("media_player", "unavailable", "unavailable", {
|
||||
friendly_name: "Player Unavailable",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "unknown", "unknown", {
|
||||
friendly_name: "Player Unknown",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "playing", "playing", {
|
||||
friendly_name: "Player Playing (no Pause support)",
|
||||
// Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21436,
|
||||
volume_level: 1,
|
||||
}),
|
||||
getEntity("media_player", "idle", "idle", {
|
||||
friendly_name: "Player Idle",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
volume_level: 0,
|
||||
}),
|
||||
getEntity("media_player", "receiver_on", "on", {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
volume_level: 0.63,
|
||||
is_volume_muted: false,
|
||||
source: "TV",
|
||||
friendly_name: "Receiver",
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
}),
|
||||
getEntity("media_player", "receiver_off", "off", {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
friendly_name: "Receiver",
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
}),
|
||||
];
|
||||
|
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",
|
||||
}),
|
||||
];
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -71,35 +76,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoAlarmPanelEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id="demos"
|
||||
hass="[[hass]]"
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
@customElement("demo-hui-alarm-panel-card")
|
||||
class DemoAlarmPanelEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
hass: Object,
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
this._setupDemo();
|
||||
}
|
||||
|
||||
private async _setupDemo() {
|
||||
const hass = provideHass(this.$.demos);
|
||||
await hass.updateTranslations(null, "en");
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -53,31 +58,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoConditional extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id="demos"
|
||||
hass="[[hass]]"
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
@customElement("demo-hui-conditional-card")
|
||||
class DemoConditional extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
hass: Object,
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -217,24 +222,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoEntities extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-entities-card")
|
||||
class DemoEntities extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -20,10 +25,10 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "With Name",
|
||||
heading: "With Name (defined in card)",
|
||||
config: `
|
||||
- type: button
|
||||
name: Bedroom
|
||||
name: Custom Name
|
||||
entity: light.bed_light
|
||||
`,
|
||||
},
|
||||
@@ -32,7 +37,7 @@ const CONFIGS = [
|
||||
config: `
|
||||
- type: button
|
||||
entity: light.bed_light
|
||||
icon: mdi:hotel
|
||||
icon: mdi:tools
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -48,7 +53,7 @@ const CONFIGS = [
|
||||
config: `
|
||||
- type: button
|
||||
entity: light.bed_light
|
||||
tap_action:
|
||||
tap_action:
|
||||
action: toggle
|
||||
`,
|
||||
},
|
||||
@@ -69,31 +74,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoButtonEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id="demos"
|
||||
hass="[[hass]]"
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
@customElement("demo-hui-entity-button-card")
|
||||
class DemoButtonEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
hass: Object,
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -43,7 +48,7 @@ const ENTITIES = [
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "Controller",
|
||||
heading: "Unfiltered controller",
|
||||
config: `
|
||||
- type: entities
|
||||
entities:
|
||||
@@ -53,7 +58,7 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Basic",
|
||||
heading: "Filtered entities card",
|
||||
config: `
|
||||
- type: entity-filter
|
||||
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: `
|
||||
- type: entity-filter
|
||||
entities:
|
||||
@@ -84,31 +109,27 @@ const CONFIGS = [
|
||||
- not_home
|
||||
card:
|
||||
type: glance
|
||||
show_state: false
|
||||
show_state: true
|
||||
title: Custom Title
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
class DemoFilter extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-entity-filter-card")
|
||||
class DemoEntityFilter extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("sensor", "brightness", "12", {}),
|
||||
getEntity("sensor", "brightness_medium", "53", {}),
|
||||
getEntity("sensor", "brightness_high", "87", {}),
|
||||
getEntity("plant", "bonsai", "ok", {}),
|
||||
getEntity("sensor", "not_working", "unavailable", {}),
|
||||
getEntity("sensor", "outside_humidity", "54", {
|
||||
@@ -21,16 +28,10 @@ const CONFIGS = [
|
||||
{
|
||||
heading: "Basic example",
|
||||
config: `
|
||||
- type: gauge
|
||||
entity: sensor.brightness
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "With title",
|
||||
config: `
|
||||
- type: gauge
|
||||
title: Humidity
|
||||
entity: sensor.outside_humidity
|
||||
name: Outside Humidity
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -39,6 +40,7 @@ const CONFIGS = [
|
||||
- type: gauge
|
||||
entity: sensor.outside_temperature
|
||||
unit_of_measurement: C
|
||||
name: Outside Temperature
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -46,19 +48,45 @@ const CONFIGS = [
|
||||
config: `
|
||||
- type: gauge
|
||||
entity: sensor.brightness
|
||||
name: Brightness Low
|
||||
severity:
|
||||
red: 32
|
||||
red: 75
|
||||
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: `
|
||||
- type: gauge
|
||||
entity: sensor.brightness
|
||||
name: Brightness
|
||||
min: 0
|
||||
max: 38
|
||||
max: 15
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -84,24 +112,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoGaugeEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-gauge-card")
|
||||
class DemoGaugeEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -77,7 +82,8 @@ const CONFIGS = [
|
||||
heading: "With title",
|
||||
config: `
|
||||
- type: glance
|
||||
title: This is glance
|
||||
title: Custom title
|
||||
columns: 4
|
||||
entities:
|
||||
- device_tracker.demo_paulus
|
||||
- media_player.living_room
|
||||
@@ -104,9 +110,10 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "No name",
|
||||
heading: "No entity names",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
show_name: false
|
||||
entities:
|
||||
- device_tracker.demo_paulus
|
||||
@@ -119,9 +126,10 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "No state",
|
||||
heading: "No state labels",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
show_state: false
|
||||
entities:
|
||||
- device_tracker.demo_paulus
|
||||
@@ -134,9 +142,10 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "No name and no state",
|
||||
heading: "No names and no state labels",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
show_name: false
|
||||
show_state: false
|
||||
entities:
|
||||
@@ -150,47 +159,24 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Custom name, custom icon",
|
||||
heading: "Custom name + custom icon",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
entities:
|
||||
- entity: device_tracker.demo_paulus
|
||||
name: ¯\\_(ツ)_/¯
|
||||
icon: mdi:home-assistant
|
||||
- media_player.living_room
|
||||
- sun.sun
|
||||
- cover.kitchen_window
|
||||
- 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
|
||||
- entity: media_player.living_room
|
||||
name: ¯\\_(ツ)_/¯
|
||||
icon: mdi:home-assistant
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Selectively hidden name",
|
||||
config: `
|
||||
- type: glance
|
||||
columns: 4
|
||||
entities:
|
||||
- device_tracker.demo_paulus
|
||||
- entity: media_player.living_room
|
||||
@@ -199,45 +185,51 @@ const CONFIGS = [
|
||||
- entity: cover.kitchen_window
|
||||
name:
|
||||
- light.kitchen_lights
|
||||
- entity: lock.kitchen_door
|
||||
name:
|
||||
- light.ceiling_lights
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Primary theme",
|
||||
heading: "Custom tap action",
|
||||
config: `
|
||||
- type: glance
|
||||
theming: primary
|
||||
columns: 4
|
||||
entities:
|
||||
- device_tracker.demo_paulus
|
||||
- media_player.living_room
|
||||
- sun.sun
|
||||
- cover.kitchen_window
|
||||
- light.kitchen_lights
|
||||
- lock.kitchen_door
|
||||
- light.ceiling_lights
|
||||
- entity: lock.kitchen_door
|
||||
name: Custom
|
||||
tap_action:
|
||||
type: toggle
|
||||
- entity: light.ceiling_lights
|
||||
name: Custom
|
||||
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 {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-glance-card")
|
||||
class DemoGlanceEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import "../components/demo-cards";
|
||||
|
||||
const CONFIGS = [
|
||||
@@ -37,18 +35,10 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoIframe extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
@customElement("demo-hui-iframe-card")
|
||||
class DemoIframe extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -8,29 +13,43 @@ import "../components/demo-cards";
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
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,
|
||||
}),
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Lost Light",
|
||||
supported_features: 1,
|
||||
}),
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "Basic example",
|
||||
heading: "Switchable Light",
|
||||
config: `
|
||||
- type: light
|
||||
entity: light.bed_light
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Dim",
|
||||
heading: "Dimmable Light On",
|
||||
config: `
|
||||
- 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 {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-light-card")
|
||||
class DemoLightEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -161,31 +166,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoMap extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id="demos"
|
||||
hass="[[hass]]"
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
@customElement("demo-hui-map-card")
|
||||
class DemoMap extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
hass: Object,
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockTemplate } from "../../../demo/src/stubs/template";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -254,23 +259,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoMarkdown extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-markdown-card")
|
||||
class DemoMarkdown extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
mockTemplate(hass);
|
||||
}
|
||||
}
|
||||
|
@@ -1,46 +1,72 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
import { createMediaPlayerEntities } from "../data/media_players";
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "Paused music",
|
||||
heading: "Paused Music",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.music_paused
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Playing music",
|
||||
heading: "Playing Music",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.music_playing
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Playing stream",
|
||||
heading: "Playing Stream",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.stream_playing
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Pause, No skip, tvshow",
|
||||
heading: "Paused Stream",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.living_room
|
||||
entity: media_player.stream_paused
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Screen casting",
|
||||
heading: 'Playing Stream (with "previous" support)',
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.stream_playing_previous
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Playing non-skip TV Show",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.tv_playing
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Screen Casting",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.android_cast
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Digital Picture Frame",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.image_display
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Sonos Idle",
|
||||
config: `
|
||||
@@ -48,11 +74,53 @@ const CONFIGS = [
|
||||
entity: media_player.sonos_idle
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Idle waiting for Browse Media",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.idle_browse_media
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Player Off",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.theater
|
||||
entity: media_player.theater_off
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Player On",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.theater_on
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Player Off (cannot be switched on)",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.theater_off_static
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Player On (cannot be switched off)",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.theater_on_static
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Player Idle",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.idle
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Player Playing",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.playing
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -70,48 +138,46 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Receiver On",
|
||||
heading: "Receiver On (selectable sources)",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.receiver_on
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Receiver Off",
|
||||
heading: "Receiver Off (selectable sources)",
|
||||
config: `
|
||||
- type: media-control
|
||||
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 {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id="demos"
|
||||
hass="[[hass]]"
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
@customElement("demo-hui-media-control-card")
|
||||
class DemoHuiMediaControlCard extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
hass: Object,
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
import { createMediaPlayerEntities } from "../data/media_players";
|
||||
@@ -12,54 +17,64 @@ const CONFIGS = [
|
||||
- type: entities
|
||||
entities:
|
||||
- entity: media_player.music_paused
|
||||
name: Paused music
|
||||
name: Paused Music
|
||||
- entity: media_player.music_playing
|
||||
name: Playing music
|
||||
name: Playing Music
|
||||
- entity: media_player.stream_playing
|
||||
name: Paused, no play
|
||||
- entity: media_player.living_room
|
||||
name: Pause, No skip, tvshow
|
||||
name: Playing Stream
|
||||
- entity: media_player.stream_paused
|
||||
name: Paused Stream
|
||||
- entity: media_player.stream_playing_previous
|
||||
name: Playing Stream (with "previous" support)
|
||||
- entity: media_player.tv_playing
|
||||
name: Playing non-skip TV Show
|
||||
- entity: media_player.android_cast
|
||||
name: Screen casting
|
||||
- entity: media_player.image_display
|
||||
name: Digital Picture Frame
|
||||
- entity: media_player.sonos_idle
|
||||
name: Chromcast Idle
|
||||
- entity: media_player.theater
|
||||
name: Sonos Idle
|
||||
- entity: media_player.idle_browse_media
|
||||
name: Idle waiting for Browse Media
|
||||
- entity: media_player.theater_off
|
||||
name: Player Off
|
||||
- entity: media_player.theater_on
|
||||
name: Player On
|
||||
- entity: media_player.theater_off_static
|
||||
name: Player Off (cannot be switched on)
|
||||
- entity: media_player.theater_on_static
|
||||
name: Player On (cannot be switched off)
|
||||
- entity: media_player.idle
|
||||
name: Player Idle
|
||||
- entity: media_player.playing
|
||||
name: Player Playing
|
||||
- entity: media_player.unavailable
|
||||
name: Player Unavailable
|
||||
- entity: media_player.unknown
|
||||
name: Player Unknown
|
||||
- entity: media_player.receiver_on
|
||||
name: Receiver On (selectable sources)
|
||||
- entity: media_player.receiver_off
|
||||
name: Receiver Off (selectable sources)
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
class DemoHuiMediaPlayerRows extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<demo-cards
|
||||
id="demos"
|
||||
hass="[[hass]]"
|
||||
configs="[[_configs]]"
|
||||
></demo-cards>
|
||||
`;
|
||||
@customElement("demo-hui-media-player-row")
|
||||
class DemoHuiMediaPlayerRow extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
hass: Object,
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -125,26 +130,21 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoPicElements extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-picture-elements-card")
|
||||
class DemoPictureElements extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -80,26 +85,21 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoPicEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-picture-entity-card")
|
||||
class DemoPictureEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -121,26 +126,21 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoPicGlance extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-picture-glance-card")
|
||||
class DemoPictureGlance extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
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";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
|
||||
@@ -20,24 +25,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoShoppingListEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-shopping-list-card")
|
||||
class DemoShoppingListEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
|
||||
hass.mockAPI("shopping_list", () => [
|
||||
{ name: "list", id: 1, complete: false },
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { mockHistory } from "../../../demo/src/stubs/history";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
@@ -132,24 +137,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoStack extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-stack-card")
|
||||
class DemoStack extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockHistory(hass);
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-cards";
|
||||
@@ -74,24 +79,19 @@ const CONFIGS = [
|
||||
},
|
||||
];
|
||||
|
||||
class DemoThermostatEntity extends PolymerElement {
|
||||
static get template() {
|
||||
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
|
||||
@customElement("demo-hui-thermostat-card")
|
||||
class DemoThermostatEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_configs: {
|
||||
type: Object,
|
||||
value: CONFIGS,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
const hass = provideHass(this.$.demos);
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,29 @@
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-more-infos";
|
||||
import {
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_FLASH,
|
||||
SUPPORT_TRANSITION,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../src/data/light";
|
||||
import "../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../src/fake_data/provide_hass";
|
||||
import "../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
@@ -14,38 +31,52 @@ const ENTITIES = [
|
||||
}),
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 80,
|
||||
brightness: 200,
|
||||
supported_features: SUPPORT_BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
brightness: 128,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_effectslight", "on", {
|
||||
friendly_name: "Color Effets Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
white_value: 36,
|
||||
supported_features:
|
||||
SUPPORT_BRIGHTNESS +
|
||||
SUPPORT_EFFECT +
|
||||
SUPPORT_FLASH +
|
||||
SUPPORT_COLOR +
|
||||
SUPPORT_TRANSITION +
|
||||
SUPPORT_WHITE_VALUE,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
];
|
||||
|
||||
class DemoMoreInfoLight extends PolymerElement {
|
||||
static get template() {
|
||||
@customElement("demo-more-info-light")
|
||||
class DemoMoreInfoLight extends LitElement {
|
||||
@property() public hass!: MockHomeAssistant;
|
||||
|
||||
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
hass="[[hass]]"
|
||||
entities="[[_entities]]"
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_entities: {
|
||||
type: Array,
|
||||
value: ENTITIES.map((ent) => ent.entityId),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public ready() {
|
||||
super.ready();
|
||||
this._setupDemo();
|
||||
}
|
||||
|
||||
private async _setupDemo() {
|
||||
const hass = provideHass(this);
|
||||
await hass.updateTranslations(null, "en");
|
||||
protected firstUpdated(changedProperties: PropertyValues) {
|
||||
super.firstUpdated(changedProperties);
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
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 { ActionHandlerEvent } from "../../../src/data/lovelace";
|
||||
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||
|
||||
@customElement("demo-util-long-press")
|
||||
export class DemoUtilLongPress extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
@@ -20,7 +21,7 @@ export class DemoUtilLongPress extends LitElement {
|
||||
|
||||
<textarea></textarea>
|
||||
|
||||
<div>(try pressing and scrolling too!)</div>
|
||||
<div>Try pressing and scrolling too!</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
@@ -62,5 +63,3 @@ export class DemoUtilLongPress extends LitElement {
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("demo-util-long-press", DemoUtilLongPress);
|
||||
|
@@ -14,54 +14,51 @@ import "../../src/styles/polymer-ha-style";
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { DEMOS } from "../build/import-demos";
|
||||
|
||||
const fixPath = (path) => path.substr(2, path.length - 5);
|
||||
|
||||
class HaGallery extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-positioning ha-style">
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
app-header-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
ha-icon-button.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
app-header-layout {
|
||||
min-height: 100vh;
|
||||
}
|
||||
ha-icon-button.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.pickers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
}
|
||||
.pickers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.pickers ha-card {
|
||||
width: 400px;
|
||||
display: block;
|
||||
margin: 16px 8px;
|
||||
}
|
||||
.pickers ha-card {
|
||||
width: 400px;
|
||||
display: block;
|
||||
margin: 16px 8px;
|
||||
}
|
||||
|
||||
.pickers ha-card:last-child {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.pickers ha-card:last-child {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin: -1em 0;
|
||||
}
|
||||
.intro {
|
||||
margin: -1em 0;
|
||||
}
|
||||
|
||||
p a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
p a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout>
|
||||
@@ -70,32 +67,42 @@ class HaGallery extends PolymerElement {
|
||||
<ha-icon-button
|
||||
icon="hass:arrow-left"
|
||||
on-click="_backTapped"
|
||||
class$='[[_computeHeaderButtonClass(_demo)]]'
|
||||
class$="[[_computeHeaderButtonClass(_demo)]]"
|
||||
></ha-icon-button>
|
||||
<div main-title>[[_withDefault(_demo, "Home Assistant Gallery")]]</div>
|
||||
<div main-title>
|
||||
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
||||
</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class='content'>
|
||||
<div id='demo'></div>
|
||||
<template is='dom-if' if='[[!_demo]]'>
|
||||
<div class='pickers'>
|
||||
<ha-card header="Lovelace card demos">
|
||||
<div class='card-content intro'>
|
||||
<div class="content">
|
||||
<div id="demo"></div>
|
||||
<template is="dom-if" if="[[!_demo]]">
|
||||
<div class="pickers">
|
||||
<ha-card header="Lovelace Card Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
Lovelace has many different cards. Each card allows the user to tell a different story about what is going on in their house. These cards are very customizable, as no household is the same.
|
||||
Lovelace has many different cards. Each card allows the user
|
||||
to tell a different story about what is going on in their
|
||||
house. These cards are very customizable, as no household is
|
||||
the same.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This gallery helps our developers and designers to see all the different states that each card can be in.
|
||||
This gallery helps our developers and designers to see all
|
||||
the different states that each card can be in.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Check <a href='https://www.home-assistant.io/lovelace'>the official website</a> for instructions on how to get started with Lovelace.</a>.
|
||||
Check
|
||||
<a href="https://www.home-assistant.io/lovelace"
|
||||
>the official website</a
|
||||
>
|
||||
for instructions on how to get started with Lovelace.
|
||||
</p>
|
||||
</div>
|
||||
<template is='dom-repeat' items='[[_lovelaceDemos]]'>
|
||||
<a href='#[[item]]'>
|
||||
<template is="dom-repeat" items="[[_lovelaceDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
@@ -104,14 +111,14 @@ class HaGallery extends PolymerElement {
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="More Info demos">
|
||||
<div class='card-content intro'>
|
||||
<ha-card header="More Info Demos">
|
||||
<div class="card-content intro">
|
||||
<p>
|
||||
More info screens show up when an entity is clicked.
|
||||
</p>
|
||||
</div>
|
||||
<template is='dom-repeat' items='[[_moreInfoDemos]]'>
|
||||
<a href='#[[item]]'>
|
||||
<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>
|
||||
@@ -120,14 +127,14 @@ class HaGallery extends PolymerElement {
|
||||
</template>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Util demos">
|
||||
<div class='card-content intro'>
|
||||
<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]]'>
|
||||
<template is="dom-repeat" items="[[_utilDemos]]">
|
||||
<a href="#[[item]]">
|
||||
<paper-item>
|
||||
<paper-item-body>{{ item }}</paper-item-body>
|
||||
<ha-icon icon="hass:chevron-right"></ha-icon>
|
||||
@@ -139,7 +146,10 @@ class HaGallery extends PolymerElement {
|
||||
</template>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
<notification-manager hass=[[_fakeHass]] id='notifications'></notification-manager>
|
||||
<notification-manager
|
||||
hass="[[_fakeHass]]"
|
||||
id="notifications"
|
||||
></notification-manager>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -11,17 +11,18 @@ import {
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import "../../../src/common/search/search-input";
|
||||
import "../../../src/components/ha-button-menu";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
fetchHassioAddonsInfo,
|
||||
HassioAddonInfo,
|
||||
HassioAddonRepository,
|
||||
reloadHassioAddons,
|
||||
} 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-tabs-subpage";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
@@ -49,46 +50,27 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
|
||||
class HassioAddonStore extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ attribute: false }) private _addons?: HassioAddonInfo[];
|
||||
|
||||
@property({ attribute: false }) private _repos?: HassioAddonRepository[];
|
||||
|
||||
@internalProperty() private _filter?: string;
|
||||
|
||||
public async refreshData() {
|
||||
this._repos = undefined;
|
||||
this._addons = undefined;
|
||||
this._filter = undefined;
|
||||
await reloadHassioAddons(this.hass);
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const repos: TemplateResult[] = [];
|
||||
let repos: TemplateResult[] = [];
|
||||
|
||||
if (this._repos) {
|
||||
for (const repo of this._repos) {
|
||||
const addons = this._addons!.filter(
|
||||
(addon) => addon.repository === repo.slug
|
||||
);
|
||||
|
||||
if (addons.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
repos.push(html`
|
||||
<hassio-addon-repository
|
||||
.hass=${this.hass}
|
||||
.repo=${repo}
|
||||
.addons=${addons}
|
||||
.filter=${this._filter!}
|
||||
></hassio-addon-repository>
|
||||
`);
|
||||
}
|
||||
if (this.supervisor.addon.repositories) {
|
||||
repos = this.addonRepositories(
|
||||
this.supervisor.addon.repositories,
|
||||
this.supervisor.addon.addons
|
||||
);
|
||||
}
|
||||
|
||||
return html`
|
||||
@@ -157,6 +139,27 @@ class HassioAddonStore extends LitElement {
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
private addonRepositories = memoizeOne(
|
||||
(repositories: HassioAddonRepository[], addons: HassioAddonInfo[]) => {
|
||||
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=${this._filter!}
|
||||
></hassio-addon-repository>
|
||||
`
|
||||
: html``;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
@@ -179,7 +182,7 @@ class HassioAddonStore extends LitElement {
|
||||
|
||||
private async _manageRepositories() {
|
||||
showRepositoriesDialog(this, {
|
||||
repos: this._repos!,
|
||||
repos: this.supervisor.addon.repositories,
|
||||
loadData: () => this._loadData(),
|
||||
});
|
||||
}
|
||||
@@ -189,14 +192,10 @@ class HassioAddonStore extends LitElement {
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
try {
|
||||
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
||||
this._repos = addonsInfo.repositories;
|
||||
this._repos.sort(sortRepos);
|
||||
this._addons = addonsInfo.addons;
|
||||
} catch (err) {
|
||||
alert(extractApiErrorMessage(err));
|
||||
}
|
||||
fireEvent(this, "supervisor-colllection-refresh", { colllection: "addon" });
|
||||
fireEvent(this, "supervisor-colllection-refresh", {
|
||||
colllection: "supervisor",
|
||||
});
|
||||
}
|
||||
|
||||
private async _filterChanged(e) {
|
||||
|
@@ -7,13 +7,14 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-card";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
@@ -28,7 +29,6 @@ import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
|
||||
@customElement("hassio-addon-audio")
|
||||
class HassioAddonAudio extends LitElement {
|
||||
|
@@ -7,11 +7,11 @@ import {
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "./hassio-addon-audio";
|
||||
import "./hassio-addon-config";
|
||||
import "./hassio-addon-network";
|
||||
@@ -26,28 +26,41 @@ class HassioAddonConfigDashboard extends LitElement {
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
}
|
||||
const hasOptions =
|
||||
this.addon.options && Object.keys(this.addon.options).length;
|
||||
const hasSchema =
|
||||
hasOptions && this.addon.schema && Object.keys(this.addon.schema).length;
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
<hassio-addon-config
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-config>
|
||||
${this.addon.network
|
||||
${hasOptions || hasSchema || this.addon.network || this.addon.audio
|
||||
? html`
|
||||
<hassio-addon-network
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-network>
|
||||
${hasOptions || hasSchema
|
||||
? html`
|
||||
<hassio-addon-config
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-config>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.network
|
||||
? html`
|
||||
<hassio-addon-network
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-network>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.audio
|
||||
? html`
|
||||
<hassio-addon-audio
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-audio>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
${this.addon.audio
|
||||
? html`
|
||||
<hassio-addon-audio
|
||||
.hass=${this.hass}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-audio>
|
||||
`
|
||||
: ""}
|
||||
: "This add-on does not expose configuration for you to mess with.... 👋"}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@@ -1,4 +1,7 @@
|
||||
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 {
|
||||
css,
|
||||
@@ -12,9 +15,15 @@ import {
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-button-menu";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-switch";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||
import {
|
||||
@@ -29,35 +38,100 @@ import type { HomeAssistant } from "../../../../src/types";
|
||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
|
||||
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
|
||||
|
||||
@customElement("hassio-addon-config")
|
||||
class HassioAddonConfig extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) private _configHasChanged = false;
|
||||
|
||||
@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;
|
||||
|
||||
private _filteredShchema = memoizeOne(
|
||||
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
|
||||
return schema.filter((entry) => entry.name in options || entry.required);
|
||||
}
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const showForm =
|
||||
!this._yamlMode && this._canShowSchema && this.addon.schema;
|
||||
const hasHiddenOptions =
|
||||
showForm &&
|
||||
JSON.stringify(this.addon.schema) !==
|
||||
JSON.stringify(
|
||||
this._filteredShchema(this.addon.options, this.addon.schema!)
|
||||
);
|
||||
return html`
|
||||
<h1>${this.addon.name}</h1>
|
||||
<ha-card header="Configuration">
|
||||
<div class="card-content">
|
||||
<ha-yaml-editor
|
||||
@value-changed=${this._configChanged}
|
||||
></ha-yaml-editor>
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
${this._valid ? "" : html` <div class="errors">Invalid YAML</div> `}
|
||||
<ha-card>
|
||||
<div class="header">
|
||||
<h2>Configuration</h2>
|
||||
<div class="card-menu">
|
||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||
<mwc-icon-button slot="trigger">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item .disabled=${!this._canShowSchema}>
|
||||
${this._yamlMode ? "Edit in UI" : "Edit in YAML"}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning">
|
||||
Reset to defaults
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
||||
Reset to defaults
|
||||
</ha-progress-button>
|
||||
|
||||
<div class="card-content">
|
||||
${showForm
|
||||
? html`<ha-form
|
||||
.data=${this._options!}
|
||||
@value-changed=${this._configChanged}
|
||||
.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">Invalid YAML</div> `}
|
||||
</div>
|
||||
${hasHiddenOptions
|
||||
? html`<ha-formfield
|
||||
class="show-additional"
|
||||
label="Show unused optional configuration options"
|
||||
>
|
||||
<ha-switch
|
||||
@change=${this._toggleOptional}
|
||||
.checked=${this._showOptional}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>`
|
||||
: ""}
|
||||
<div class="card-actions right">
|
||||
<ha-progress-button
|
||||
@click=${this._saveTapped}
|
||||
.disabled=${!this._configHasChanged || !this._valid}
|
||||
@@ -69,16 +143,59 @@ class HassioAddonConfig extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._canShowSchema =
|
||||
Object.keys(this.addon.options).length !== 0 &&
|
||||
!this.addon.schema!.find(
|
||||
// @ts-ignore
|
||||
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
|
||||
);
|
||||
this._yamlMode = !this._canShowSchema;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
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 {
|
||||
this._configHasChanged = true;
|
||||
this._valid = ev.detail.isValid;
|
||||
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
|
||||
this._valid = true;
|
||||
this._configHasChanged = true;
|
||||
this._options! = ev.detail.value;
|
||||
} else {
|
||||
this._configHasChanged = true;
|
||||
this._valid = ev.detail.isValid;
|
||||
}
|
||||
}
|
||||
|
||||
private async _resetTapped(ev: CustomEvent): Promise<void> {
|
||||
@@ -122,18 +239,13 @@ class HassioAddonConfig extends LitElement {
|
||||
const button = ev.currentTarget as any;
|
||||
button.progress = true;
|
||||
|
||||
let data: HassioAddonSetOptionParams;
|
||||
this._error = undefined;
|
||||
|
||||
try {
|
||||
data = {
|
||||
options: this._editor.value,
|
||||
};
|
||||
} catch (err) {
|
||||
this._error = err;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, {
|
||||
options: this._yamlMode ? this._editor?.value : this._options,
|
||||
});
|
||||
|
||||
this._configHasChanged = false;
|
||||
const eventdata = {
|
||||
success: true,
|
||||
@@ -178,6 +290,36 @@ class HassioAddonConfig extends LitElement {
|
||||
.syntaxerror {
|
||||
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;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -9,17 +9,25 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
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 {
|
||||
fetchHassioAddonInfo,
|
||||
HassioAddonDetails,
|
||||
} 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/components/ha-circular-progress";
|
||||
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
@@ -35,12 +43,16 @@ import "./log/hassio-addon-logs";
|
||||
class HassioAddonDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@internalProperty() _error?: string;
|
||||
|
||||
private _computeTail = memoizeOne((route: Route) => {
|
||||
const dividerPos = route.path.indexOf("/", 1);
|
||||
return dividerPos === -1
|
||||
@@ -55,8 +67,14 @@ class HassioAddonDashboard extends LitElement {
|
||||
});
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._error) {
|
||||
return html`<hass-error-screen
|
||||
.error=${this._error}
|
||||
></hass-error-screen>`;
|
||||
}
|
||||
|
||||
if (!this.addon) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||
}
|
||||
|
||||
const addonTabs: PageNavigation[] = [
|
||||
@@ -106,6 +124,7 @@ class HassioAddonDashboard extends LitElement {
|
||||
.route=${route}
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this.supervisor}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-router>
|
||||
</hass-tabs-subpage>
|
||||
@@ -152,30 +171,53 @@ class HassioAddonDashboard extends LitElement {
|
||||
}
|
||||
|
||||
protected async firstUpdated(): Promise<void> {
|
||||
await this._routeDataChanged(this.route);
|
||||
if (this.route.path === "") {
|
||||
const addon = extractSearchParam("addon");
|
||||
if (addon) {
|
||||
navigate(this, `/hassio/addon/${addon}`, true);
|
||||
}
|
||||
}
|
||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const path: string = pathSplit[pathSplit.length - 1];
|
||||
|
||||
if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
|
||||
fireEvent(this, "supervisor-colllection-refresh", {
|
||||
colllection: "supervisor",
|
||||
});
|
||||
}
|
||||
|
||||
if (path === "uninstall") {
|
||||
history.back();
|
||||
window.history.back();
|
||||
} else {
|
||||
await this._routeDataChanged(this.route);
|
||||
await this._routeDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async _routeDataChanged(routeData: Route): Promise<void> {
|
||||
const addon = routeData.path.split("/")[1];
|
||||
protected updated(changedProperties) {
|
||||
if (changedProperties.has("route") && !this.addon) {
|
||||
this._routeDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async _routeDataChanged(): Promise<void> {
|
||||
const addon = this.route.path.split("/")[1];
|
||||
if (!addon) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
||||
this.addon = addoninfo;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
||||
this.addon = undefined;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { customElement, property } from "lit-element";
|
||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import {
|
||||
HassRouterPage,
|
||||
RouterOptions,
|
||||
@@ -17,6 +18,8 @@ class HassioAddonRouter extends HassRouterPage {
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
@@ -41,6 +44,7 @@ class HassioAddonRouter extends HassRouterPage {
|
||||
protected updatePageEl(el) {
|
||||
el.route = this.routeTail;
|
||||
el.hass = this.hass;
|
||||
el.supervisor = this.supervisor;
|
||||
el.addon = this.addon;
|
||||
el.narrow = this.narrow;
|
||||
}
|
||||
|
@@ -7,8 +7,9 @@ import {
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
@@ -20,6 +21,8 @@ class HassioAddonInfoDashboard extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -32,6 +35,7 @@ class HassioAddonInfoDashboard extends LitElement {
|
||||
<hassio-addon-info
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this.supervisor}
|
||||
.addon=${this.addon}
|
||||
></hassio-addon-info>
|
||||
</div>
|
||||
|
@@ -25,6 +25,7 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { navigate } from "../../../../src/common/navigate";
|
||||
@@ -43,22 +44,34 @@ import {
|
||||
HassioAddonSetOptionParams,
|
||||
HassioAddonSetSecurityParams,
|
||||
installHassioAddon,
|
||||
restartHassioAddon,
|
||||
setHassioAddonOption,
|
||||
setHassioAddonSecurity,
|
||||
startHassioAddon,
|
||||
stopHassioAddon,
|
||||
uninstallHassioAddon,
|
||||
validateHassioAddonOption,
|
||||
} 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 {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
||||
import "../../components/hassio-card-content";
|
||||
import "../../components/supervisor-metric";
|
||||
import { showDialogSupervisorAddonUpdate } from "../../dialogs/addon/show-dialog-addon-update";
|
||||
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import { addonArchIsSupported } from "../../util/addon";
|
||||
|
||||
const STAGE_ICON = {
|
||||
stable: mdiCheckCircle,
|
||||
@@ -69,7 +82,7 @@ const STAGE_ICON = {
|
||||
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.`,
|
||||
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",
|
||||
@@ -131,9 +144,35 @@ class HassioAddonInfo extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@internalProperty() private _metrics?: HassioStats;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
private _addonStoreInfo = memoizeOne(
|
||||
(slug: string, storeAddons: StoreAddon[]) =>
|
||||
storeAddons.find((addon) => addon.slug === slug)
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const addonStoreInfo =
|
||||
!this.addon.detached && !this.addon.available
|
||||
? this._addonStoreInfo(this.addon.slug, this.supervisor.store.addons)
|
||||
: undefined;
|
||||
const metrics = [
|
||||
{
|
||||
description: "Add-on CPU Usage",
|
||||
value: this._metrics?.cpu_percent,
|
||||
},
|
||||
{
|
||||
description: "Add-on RAM Usage",
|
||||
value: this._metrics?.memory_percent,
|
||||
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
|
||||
this._metrics?.memory_limit
|
||||
)}`,
|
||||
},
|
||||
];
|
||||
return html`
|
||||
${this.addon.update_available
|
||||
? html`
|
||||
@@ -148,22 +187,32 @@ class HassioAddonInfo extends LitElement {
|
||||
icon=${mdiArrowUpBoldCircle}
|
||||
iconClass="update"
|
||||
></hassio-card-content>
|
||||
${!this.addon.available
|
||||
? html`
|
||||
<p>
|
||||
This update is no longer compatible with your system.
|
||||
</p>
|
||||
`
|
||||
${!this.addon.available && addonStoreInfo
|
||||
? !addonArchIsSupported(
|
||||
this.supervisor.info.supported_arch,
|
||||
this.addon.arch
|
||||
)
|
||||
? html`
|
||||
<p class="warning">
|
||||
This add-on is not compatible with the processor of
|
||||
your device or the operating system you have installed
|
||||
on your device.
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<p class="warning">
|
||||
You are running Home Assistant
|
||||
${this.supervisor.core.version}, to update to this
|
||||
version of the add-on you need at least version
|
||||
${addonStoreInfo.homeassistant} of Home Assistant
|
||||
</p>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-api-button
|
||||
.hass=${this.hass}
|
||||
.disabled=${!this.addon.available}
|
||||
path="hassio/addons/${this.addon.slug}/update"
|
||||
>
|
||||
<mwc-button @click=${this._updateClicked}>
|
||||
Update
|
||||
</ha-call-api-button>
|
||||
</mwc-button>
|
||||
${this.addon.changelog
|
||||
? html`
|
||||
<mwc-button @click=${this._openChangelog}>
|
||||
@@ -237,331 +286,378 @@ class HassioAddonInfo extends LitElement {
|
||||
>
|
||||
for details.
|
||||
</div>
|
||||
${this.addon.logo
|
||||
? html`
|
||||
<img
|
||||
class="logo"
|
||||
src="/api/hassio/addons/${this.addon.slug}/logo"
|
||||
/>
|
||||
`
|
||||
: ""}
|
||||
<div class="security">
|
||||
${this.addon.stage !== "stable"
|
||||
? html` <ha-label-badge
|
||||
<div class="addon-container">
|
||||
<div>
|
||||
${this.addon.logo
|
||||
? html`
|
||||
<img
|
||||
class="logo"
|
||||
src="/api/hassio/addons/${this.addon.slug}/logo"
|
||||
/>
|
||||
`
|
||||
: ""}
|
||||
<div class="security">
|
||||
${this.addon.stage !== "stable"
|
||||
? html` <ha-label-badge
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
label="stage"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
></ha-svg-icon>
|
||||
</ha-label-badge>`
|
||||
: ""}
|
||||
|
||||
<ha-label-badge
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
green: [5, 6].includes(Number(this.addon.rating)),
|
||||
yellow: [3, 4].includes(Number(this.addon.rating)),
|
||||
red: [1, 2].includes(Number(this.addon.rating)),
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
label="stage"
|
||||
id="rating"
|
||||
.value=${this.addon.rating}
|
||||
label="rating"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
></ha-svg-icon>
|
||||
</ha-label-badge>`
|
||||
: ""}
|
||||
></ha-label-badge>
|
||||
${this.addon.host_network
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_network"
|
||||
label="host"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.full_access
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="full_access"
|
||||
label="hardware"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.homeassistant_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="homeassistant_api"
|
||||
label="hass"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this._computeHassioApi
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="hassio_api"
|
||||
label="hassio"
|
||||
.description=${this.addon.hassio_role}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.docker_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="docker_api"
|
||||
label="docker"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.host_pid
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_pid"
|
||||
label="host pid"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.apparmor
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
class=${this._computeApparmorClassName}
|
||||
id="apparmor"
|
||||
label="apparmor"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auth_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="auth_api"
|
||||
label="auth"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="ingress"
|
||||
label="ingress"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${mdiCursorDefaultClickOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
<ha-label-badge
|
||||
class=${classMap({
|
||||
green: [5, 6].includes(Number(this.addon.rating)),
|
||||
yellow: [3, 4].includes(Number(this.addon.rating)),
|
||||
red: [1, 2].includes(Number(this.addon.rating)),
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="rating"
|
||||
.value=${this.addon.rating}
|
||||
label="rating"
|
||||
description=""
|
||||
></ha-label-badge>
|
||||
${this.addon.host_network
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_network"
|
||||
label="host"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.full_access
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="full_access"
|
||||
label="hardware"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.homeassistant_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="homeassistant_api"
|
||||
label="hass"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this._computeHassioApi
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="hassio_api"
|
||||
label="hassio"
|
||||
.description=${this.addon.hassio_role}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.docker_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="docker_api"
|
||||
label="docker"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.host_pid
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_pid"
|
||||
label="host pid"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.apparmor
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
class=${this._computeApparmorClassName}
|
||||
id="apparmor"
|
||||
label="apparmor"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auth_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="auth_api"
|
||||
label="auth"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="ingress"
|
||||
label="ingress"
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${mdiCursorDefaultClickOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.version
|
||||
? html`
|
||||
<div
|
||||
class="${classMap({
|
||||
"addon-options": true,
|
||||
started: this.addon.state === "started",
|
||||
})}"
|
||||
>
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Start on boot
|
||||
</span>
|
||||
<span slot="description">
|
||||
Make the add-on start during a system boot
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._startOnBootToggled}
|
||||
.checked=${this.addon.boot === "auto"}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
|
||||
${this.addon.startup !== "once"
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Watchdog
|
||||
</span>
|
||||
<span slot="description">
|
||||
This will start the add-on if it crashes
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._watchdogToggled}
|
||||
.checked=${this.addon.watchdog}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auto_update ||
|
||||
this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Auto update
|
||||
</span>
|
||||
<span slot="description">
|
||||
Auto update the add-on when there is a new
|
||||
version available
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._autoUpdateToggled}
|
||||
.checked=${this.addon.auto_update}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Show in sidebar
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this._computeCannotIngressSidebar
|
||||
? "This option requires Home Assistant 0.92 or later."
|
||||
: "Add this add-on to your sidebar"}
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._panelToggled}
|
||||
.checked=${this.addon.ingress_panel}
|
||||
.disabled=${this._computeCannotIngressSidebar}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this._computeUsesProtectedOptions
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Protection mode
|
||||
</span>
|
||||
<span slot="description">
|
||||
Blocks elevated system access from the add-on
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._protectionToggled}
|
||||
.checked=${this.addon.protected}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div>
|
||||
${this.addon.state === "started"
|
||||
? html`<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
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.addon.version
|
||||
? html`
|
||||
<div class="addon-options">
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Start on boot
|
||||
</span>
|
||||
<span slot="description">
|
||||
Make the add-on start during a system boot
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._startOnBootToggled}
|
||||
.checked=${this.addon.boot === "auto"}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
|
||||
${this.addon.startup !== "once"
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Watchdog
|
||||
</span>
|
||||
<span slot="description">
|
||||
This will start the add-on if it crashes
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._watchdogToggled}
|
||||
.checked=${this.addon.watchdog}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auto_update || this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Auto update
|
||||
</span>
|
||||
<span slot="description">
|
||||
Auto update the add-on when there is a new version
|
||||
available
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._autoUpdateToggled}
|
||||
.checked=${this.addon.auto_update}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Show in sidebar
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this._computeCannotIngressSidebar
|
||||
? "This option requires Home Assistant 0.92 or later."
|
||||
: "Add this add-on to your sidebar"}
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._panelToggled}
|
||||
.checked=${this.addon.ingress_panel}
|
||||
.disabled=${this._computeCannotIngressSidebar}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this._computeUsesProtectedOptions
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Protection mode
|
||||
</span>
|
||||
<span slot="description">
|
||||
Blocks elevated system access from the add-on
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._protectionToggled}
|
||||
.checked=${this.addon.protected}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
</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 add-on is not compatible with the processor of your
|
||||
device or the operating system you have installed on your
|
||||
device.
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<p class="warning">
|
||||
You are running Home Assistant
|
||||
${this.supervisor.core.version}, to install this add-on you
|
||||
need at least version ${addonStoreInfo!.homeassistant} of
|
||||
Home Assistant
|
||||
</p>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
${this.addon.version
|
||||
? html`
|
||||
${this._computeIsRunning
|
||||
? html`
|
||||
<ha-call-api-button
|
||||
class="warning"
|
||||
.hass=${this.hass}
|
||||
.path="hassio/addons/${this.addon.slug}/stop"
|
||||
>
|
||||
Stop
|
||||
</ha-call-api-button>
|
||||
<ha-call-api-button
|
||||
class="warning"
|
||||
.hass=${this.hass}
|
||||
.path="hassio/addons/${this.addon.slug}/restart"
|
||||
>
|
||||
Restart
|
||||
</ha-call-api-button>
|
||||
`
|
||||
: html`
|
||||
<ha-progress-button @click=${this._startClicked}>
|
||||
Start
|
||||
</ha-progress-button>
|
||||
`}
|
||||
${this._computeShowWebUI
|
||||
? html`
|
||||
<a
|
||||
href=${this._pathWebui!}
|
||||
tabindex="-1"
|
||||
target="_blank"
|
||||
class="right"
|
||||
rel="noopener"
|
||||
>
|
||||
<mwc-button>
|
||||
<div>
|
||||
${this.addon.version
|
||||
? this._computeIsRunning
|
||||
? html`
|
||||
<ha-progress-button
|
||||
class="warning"
|
||||
@click=${this._stopClicked}
|
||||
>
|
||||
Stop
|
||||
</ha-progress-button>
|
||||
<ha-progress-button
|
||||
class="warning"
|
||||
@click=${this._restartClicked}
|
||||
>
|
||||
Restart
|
||||
</ha-progress-button>
|
||||
`
|
||||
: html`
|
||||
<ha-progress-button @click=${this._startClicked}>
|
||||
Start
|
||||
</ha-progress-button>
|
||||
`
|
||||
: html`
|
||||
<ha-progress-button
|
||||
.disabled=${!this.addon.available}
|
||||
@click=${this._installClicked}
|
||||
>
|
||||
Install
|
||||
</ha-progress-button>
|
||||
`}
|
||||
</div>
|
||||
<div>
|
||||
${this.addon.version
|
||||
? html` ${this._computeShowWebUI
|
||||
? html`
|
||||
<a
|
||||
href=${this._pathWebui!}
|
||||
tabindex="-1"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<mwc-button>
|
||||
Open web UI
|
||||
</mwc-button>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${this._computeShowIngressUI
|
||||
? html`
|
||||
<mwc-button @click=${this._openIngress}>
|
||||
Open web UI
|
||||
</mwc-button>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${this._computeShowIngressUI
|
||||
? html`
|
||||
<mwc-button class="right" @click=${this._openIngress}>
|
||||
Open web UI
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
<ha-progress-button
|
||||
class=" right warning"
|
||||
@click=${this._uninstallClicked}
|
||||
>
|
||||
Uninstall
|
||||
</ha-progress-button>
|
||||
${this.addon.build
|
||||
? html`
|
||||
<ha-call-api-button
|
||||
class="warning right"
|
||||
.hass=${this.hass}
|
||||
.path="hassio/addons/${this.addon.slug}/rebuild"
|
||||
>
|
||||
Rebuild
|
||||
</ha-call-api-button>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: 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>
|
||||
`}
|
||||
`
|
||||
: ""}
|
||||
<ha-progress-button
|
||||
class="warning"
|
||||
@click=${this._uninstallClicked}
|
||||
>
|
||||
Uninstall
|
||||
</ha-progress-button>
|
||||
${this.addon.build
|
||||
? html`
|
||||
<ha-call-api-button
|
||||
class="warning"
|
||||
.hass=${this.hass}
|
||||
.path="hassio/addons/${this.addon.slug}/rebuild"
|
||||
>
|
||||
Rebuild
|
||||
</ha-call-api-button>
|
||||
`
|
||||
: ""}`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
@@ -579,6 +675,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 {
|
||||
return (
|
||||
this.addon.hassio_api &&
|
||||
@@ -779,6 +891,52 @@ class HassioAddonInfo extends LitElement {
|
||||
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: "Failed to stop addon",
|
||||
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: "Failed to restart addon",
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
}
|
||||
button.progress = false;
|
||||
}
|
||||
|
||||
private async _updateClicked(): Promise<void> {
|
||||
showDialogSupervisorAddonUpdate(this, { addon: this.addon });
|
||||
}
|
||||
|
||||
private async _startClicked(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
button.progress = true;
|
||||
@@ -787,10 +945,10 @@ class HassioAddonInfo extends LitElement {
|
||||
this.hass,
|
||||
this.addon.slug
|
||||
);
|
||||
if (!validate.data.valid) {
|
||||
if (!validate.valid) {
|
||||
await showConfirmationDialog(this, {
|
||||
title: "Failed to start addon - configuration validation failed!",
|
||||
text: validate.data.message.split(" Got ")[0],
|
||||
text: validate.message.split(" Got ")[0],
|
||||
confirm: () => this._openConfiguration(),
|
||||
confirmText: "Go to configuration",
|
||||
dismissText: "Cancel",
|
||||
@@ -810,6 +968,12 @@ class HassioAddonInfo extends LitElement {
|
||||
try {
|
||||
await startHassioAddon(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) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to start addon",
|
||||
@@ -925,9 +1089,6 @@ class HassioAddonInfo extends LitElement {
|
||||
font-weight: 500;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
protection-enable mwc-button {
|
||||
--mdc-theme-primary: white;
|
||||
}
|
||||
@@ -950,7 +1111,8 @@ class HassioAddonInfo extends LitElement {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-actions {
|
||||
display: flow-root;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
}
|
||||
.security h3 {
|
||||
margin-bottom: 8px;
|
||||
@@ -986,12 +1148,26 @@ class HassioAddonInfo extends LitElement {
|
||||
}
|
||||
|
||||
.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) {
|
||||
.addon-options {
|
||||
max-width: 100%;
|
||||
}
|
||||
.addon-container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -7,8 +7,8 @@ import {
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
|
@@ -27,6 +27,8 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
||||
|
||||
@customElement("hassio-upload-snapshot")
|
||||
export class HassioUploadSnapshot extends LitElement {
|
||||
public hass!: HomeAssistant;
|
||||
@@ -51,6 +53,20 @@ export class HassioUploadSnapshot extends LitElement {
|
||||
private async _uploadFile(ev) {
|
||||
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)) {
|
||||
showAlertDialog(this, {
|
||||
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 { compare } from "../../../src/common/string/compare";
|
||||
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 { HomeAssistant } from "../../../src/types";
|
||||
import "../components/hassio-card-content";
|
||||
@@ -22,14 +22,14 @@ import { hassioStyle } from "../resources/hassio-style";
|
||||
class HassioAddons extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public addons?: HassioAddonInfo[];
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="content">
|
||||
<h1>Add-ons</h1>
|
||||
<div class="card-group">
|
||||
${!this.addons?.length
|
||||
${!this.supervisor.supervisor.addons?.length
|
||||
? html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
@@ -41,7 +41,7 @@ class HassioAddons extends LitElement {
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: this.addons
|
||||
: this.supervisor.supervisor.addons
|
||||
.sort((a, b) => compare(a.name, b.name))
|
||||
.map(
|
||||
(addon) => html`
|
||||
|
@@ -7,11 +7,7 @@ import {
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
||||
import {
|
||||
HassioHomeAssistantInfo,
|
||||
HassioSupervisorInfo,
|
||||
} from "../../../src/data/hassio/supervisor";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import "../../../src/layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
@@ -23,16 +19,12 @@ import "./hassio-update";
|
||||
class HassioDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@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 {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
@@ -47,13 +39,11 @@ class HassioDashboard extends LitElement {
|
||||
<div class="content">
|
||||
<hassio-update
|
||||
.hass=${this.hass}
|
||||
.hassInfo=${this.hassInfo}
|
||||
.supervisorInfo=${this.supervisorInfo}
|
||||
.hassOsInfo=${this.hassOsInfo}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-update>
|
||||
<hassio-addons
|
||||
.hass=${this.hass}
|
||||
.addons=${this.supervisorInfo.addons}
|
||||
.supervisor=${this.supervisor}
|
||||
></hassio-addons>
|
||||
</div>
|
||||
</hass-tabs-subpage>
|
||||
|
@@ -10,6 +10,7 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import "../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
@@ -23,43 +24,34 @@ import {
|
||||
HassioHomeAssistantInfo,
|
||||
HassioSupervisorInfo,
|
||||
} from "../../../src/data/hassio/supervisor";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { showDialogSupervisorCoreUpdate } from "../dialogs/core/show-dialog-core-update";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
|
||||
@customElement("hassio-update")
|
||||
export class HassioUpdate extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public hassInfo?: HassioHomeAssistantInfo;
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public hassOsInfo?: HassioHassOSInfo;
|
||||
|
||||
@property({ attribute: false }) public supervisorInfo?: HassioSupervisorInfo;
|
||||
|
||||
private _pendingUpdates = memoizeOne(
|
||||
(
|
||||
core?: HassioHomeAssistantInfo,
|
||||
supervisor?: HassioSupervisorInfo,
|
||||
os?: HassioHassOSInfo
|
||||
): number => {
|
||||
return [core, supervisor, os].filter(
|
||||
(value) => !!value && value?.update_available
|
||||
).length;
|
||||
}
|
||||
);
|
||||
private _pendingUpdates = memoizeOne((supervisor: Supervisor): number => {
|
||||
return Object.keys(supervisor).filter(
|
||||
(value) => supervisor[value].update_available
|
||||
).length;
|
||||
});
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const updatesAvailable = this._pendingUpdates(
|
||||
this.hassInfo,
|
||||
this.supervisorInfo,
|
||||
this.hassOsInfo
|
||||
);
|
||||
if (!this.supervisor) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const updatesAvailable = this._pendingUpdates(this.supervisor);
|
||||
if (!updatesAvailable) {
|
||||
return html``;
|
||||
}
|
||||
@@ -74,26 +66,27 @@ export class HassioUpdate extends LitElement {
|
||||
<div class="card-group">
|
||||
${this._renderUpdateCard(
|
||||
"Home Assistant Core",
|
||||
this.hassInfo!,
|
||||
"core",
|
||||
this.supervisor.core,
|
||||
"hassio/homeassistant/update",
|
||||
`https://${
|
||||
this.hassInfo?.version_latest.includes("b") ? "rc" : "www"
|
||||
this.supervisor.core.version_latest.includes("b") ? "rc" : "www"
|
||||
}.home-assistant.io/latest-release-notes/`
|
||||
)}
|
||||
${this._renderUpdateCard(
|
||||
"Supervisor",
|
||||
this.supervisorInfo!,
|
||||
"supervisor",
|
||||
this.supervisor.supervisor,
|
||||
"hassio/supervisor/update",
|
||||
`https://github.com//home-assistant/hassio/releases/tag/${
|
||||
this.supervisorInfo!.version_latest
|
||||
}`
|
||||
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
||||
)}
|
||||
${this.hassOsInfo
|
||||
${this.supervisor.host.features.includes("hassos")
|
||||
? this._renderUpdateCard(
|
||||
"Operating System",
|
||||
this.hassOsInfo,
|
||||
"os",
|
||||
this.supervisor.os,
|
||||
"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>
|
||||
@@ -103,6 +96,7 @@ export class HassioUpdate extends LitElement {
|
||||
|
||||
private _renderUpdateCard(
|
||||
name: string,
|
||||
key: string,
|
||||
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo,
|
||||
apiPath: string,
|
||||
releaseNotesUrl: string
|
||||
@@ -128,6 +122,7 @@ export class HassioUpdate extends LitElement {
|
||||
<ha-progress-button
|
||||
.apiPath=${apiPath}
|
||||
.name=${name}
|
||||
.key=${key}
|
||||
.version=${object.version_latest}
|
||||
@click=${this._confirmUpdate}
|
||||
>
|
||||
@@ -140,6 +135,10 @@ export class HassioUpdate extends LitElement {
|
||||
|
||||
private async _confirmUpdate(ev): Promise<void> {
|
||||
const item = ev.currentTarget;
|
||||
if (item.key === "core") {
|
||||
showDialogSupervisorCoreUpdate(this, { core: this.supervisor.core });
|
||||
return;
|
||||
}
|
||||
item.progress = true;
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: `Update ${item.name}`,
|
||||
@@ -154,10 +153,17 @@ export class HassioUpdate extends LitElement {
|
||||
}
|
||||
try {
|
||||
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
||||
fireEvent(this, "supervisor-colllection-refresh", {
|
||||
colllection: item.key,
|
||||
});
|
||||
} catch (err) {
|
||||
// Only show an error if the status code was not expected (user behind proxy)
|
||||
// or no status at all(connection terminated)
|
||||
if (err.status_code && !ignoredStatusCodes.has(err.status_code)) {
|
||||
if (
|
||||
this.hass.connection.connected &&
|
||||
err.status_code &&
|
||||
!ignoredStatusCodes.has(err.status_code)
|
||||
) {
|
||||
showAlertDialog(this, {
|
||||
title: "Update failed",
|
||||
text: extractApiErrorMessage(err),
|
||||
|
178
hassio/src/dialogs/addon/dialog-supervisor-addon-update.ts
Normal file
178
hassio/src/dialogs/addon/dialog-supervisor-addon-update.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-switch";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
updateHassioAddon,
|
||||
} from "../../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import { createHassioPartialSnapshot } from "../../../../src/data/hassio/snapshot";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import { SupervisorDialogSupervisorAddonUpdateParams } from "./show-dialog-addon-update";
|
||||
|
||||
@customElement("dialog-supervisor-addon-update")
|
||||
class DialogSupervisorAddonUpdate extends LitElement {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
public addon!: HassioAddonDetails;
|
||||
|
||||
@internalProperty() private _opened = false;
|
||||
|
||||
@internalProperty() private _createSnapshot = true;
|
||||
|
||||
@internalProperty() private _action: "snapshot" | "update" | null = null;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
public async showDialog(
|
||||
params: SupervisorDialogSupervisorAddonUpdateParams
|
||||
): Promise<void> {
|
||||
this._opened = true;
|
||||
this.addon = params.addon;
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._action = null;
|
||||
this._createSnapshot = true;
|
||||
this._opened = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.updateComplete.then(() =>
|
||||
(this.shadowRoot?.querySelector(
|
||||
"[dialogInitialFocus]"
|
||||
) as HTMLElement)?.focus()
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-dialog
|
||||
.heading="Update ${this.addon.name}"
|
||||
.open=${this._opened}
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
>
|
||||
${this._action === null
|
||||
? html`<div>
|
||||
Are you sure you want to update this add-on to version
|
||||
${this.addon.version_latest}?
|
||||
</div>
|
||||
|
||||
<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Snapshot
|
||||
</span>
|
||||
<span slot="description">
|
||||
Create a snapshot of the add-on before updating
|
||||
</span>
|
||||
<ha-switch
|
||||
.checked=${this._createSnapshot}
|
||||
haptic
|
||||
title="Create snapshot"
|
||||
@click=${this._toggleSnapshot}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-settings-row>
|
||||
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||
Cancel
|
||||
</mwc-button>
|
||||
<mwc-button @click=${this._update} slot="primaryAction">
|
||||
Update
|
||||
</mwc-button>`
|
||||
: html`<ha-circular-progress alt="Updating" size="large" active>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this._action === "update"
|
||||
? `Update to version ${this.addon.version_latest} in progress`
|
||||
: "Creating snapshot in progress"}
|
||||
</p>`}
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleSnapshot() {
|
||||
this._createSnapshot = !this._createSnapshot;
|
||||
}
|
||||
|
||||
private async _update() {
|
||||
if (this._createSnapshot) {
|
||||
this._action = "snapshot";
|
||||
try {
|
||||
await createHassioPartialSnapshot(this.hass, {
|
||||
name: `addon_${this.addon.slug}_${this.addon.version}`,
|
||||
addons: [this.addon.slug],
|
||||
homeassistant: false,
|
||||
});
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
this._action = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._action = "update";
|
||||
try {
|
||||
await updateHassioAddon(this.hass, this.addon.slug);
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
this._action = null;
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "supervisor-colllection-refresh", { colllection: "addon" });
|
||||
fireEvent(this, "supervisor-colllection-refresh", {
|
||||
colllection: "supervisor",
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
.form {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
ha-settings-row {
|
||||
margin-top: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ha-circular-progress {
|
||||
display: block;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-supervisor-addon-update": DialogSupervisorAddonUpdate;
|
||||
}
|
||||
}
|
17
hassio/src/dialogs/addon/show-dialog-addon-update.ts
Normal file
17
hassio/src/dialogs/addon/show-dialog-addon-update.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
|
||||
|
||||
export interface SupervisorDialogSupervisorAddonUpdateParams {
|
||||
addon: HassioAddonDetails;
|
||||
}
|
||||
|
||||
export const showDialogSupervisorAddonUpdate = (
|
||||
element: HTMLElement,
|
||||
dialogParams: SupervisorDialogSupervisorAddonUpdateParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-supervisor-addon-update",
|
||||
dialogImport: () => import("./dialog-supervisor-addon-update"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
175
hassio/src/dialogs/core/dialog-supervisor-core-update.ts
Normal file
175
hassio/src/dialogs/core/dialog-supervisor-core-update.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/ha-switch";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import { createHassioPartialSnapshot } from "../../../../src/data/hassio/snapshot";
|
||||
import { HassioHomeAssistantInfo } from "../../../../src/data/hassio/supervisor";
|
||||
import { updateCore } from "../../../../src/data/supervisor/core";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import { SupervisorDialogSupervisorCoreUpdateParams } from "./show-dialog-core-update";
|
||||
|
||||
@customElement("dialog-supervisor-core-update")
|
||||
class DialogSupervisorCoreUpdate extends LitElement {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
public core!: HassioHomeAssistantInfo;
|
||||
|
||||
@internalProperty() private _opened = false;
|
||||
|
||||
@internalProperty() private _createSnapshot = true;
|
||||
|
||||
@internalProperty() private _action: "snapshot" | "update" | null = null;
|
||||
|
||||
@internalProperty() private _error?: string;
|
||||
|
||||
public async showDialog(
|
||||
params: SupervisorDialogSupervisorCoreUpdateParams
|
||||
): Promise<void> {
|
||||
this._opened = true;
|
||||
this.core = params.core;
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._action = null;
|
||||
this._createSnapshot = true;
|
||||
this._opened = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.updateComplete.then(() =>
|
||||
(this.shadowRoot?.querySelector(
|
||||
"[dialogInitialFocus]"
|
||||
) as HTMLElement)?.focus()
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-dialog
|
||||
.open=${this._opened}
|
||||
heading="Update Home Assistant Core"
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
>
|
||||
${this._action === null
|
||||
? html`<div>
|
||||
Are you sure you want to update Home Assistant Core to version
|
||||
${this.core.version_latest}?
|
||||
</div>
|
||||
|
||||
<ha-settings-row three-rows>
|
||||
<span slot="heading">
|
||||
Snapshot
|
||||
</span>
|
||||
<span slot="description">
|
||||
Create a snapshot of Home Assistant Core before updating
|
||||
</span>
|
||||
<ha-switch
|
||||
.checked=${this._createSnapshot}
|
||||
haptic
|
||||
title="Create snapshot"
|
||||
@click=${this._toggleSnapshot}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-settings-row>
|
||||
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
|
||||
Cancel
|
||||
</mwc-button>
|
||||
<mwc-button @click=${this._update} slot="primaryAction">
|
||||
Update
|
||||
</mwc-button>`
|
||||
: html`<ha-circular-progress alt="Updating" size="large" active>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this._action === "update"
|
||||
? `Update to version ${this.core.version_latest} in progress`
|
||||
: "Creating snapshot in progress"}
|
||||
</p>`}
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleSnapshot() {
|
||||
this._createSnapshot = !this._createSnapshot;
|
||||
}
|
||||
|
||||
private async _update() {
|
||||
if (this._createSnapshot) {
|
||||
this._action = "snapshot";
|
||||
try {
|
||||
await createHassioPartialSnapshot(this.hass, {
|
||||
name: `core_${this.core.version}`,
|
||||
folders: ["homeassistant"],
|
||||
homeassistant: true,
|
||||
});
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
this._action = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._action = "update";
|
||||
try {
|
||||
await updateCore(this.hass);
|
||||
} catch (err) {
|
||||
if (this.hass.connection.connected) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
this._action = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fireEvent(this, "supervisor-colllection-refresh", { colllection: "core" });
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
.form {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
ha-settings-row {
|
||||
margin-top: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ha-circular-progress {
|
||||
display: block;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-supervisor-core-update": DialogSupervisorCoreUpdate;
|
||||
}
|
||||
}
|
17
hassio/src/dialogs/core/show-dialog-core-update.ts
Normal file
17
hassio/src/dialogs/core/show-dialog-core-update.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { HassioHomeAssistantInfo } from "../../../../src/data/hassio/supervisor";
|
||||
|
||||
export interface SupervisorDialogSupervisorCoreUpdateParams {
|
||||
core: HassioHomeAssistantInfo;
|
||||
}
|
||||
|
||||
export const showDialogSupervisorCoreUpdate = (
|
||||
element: HTMLElement,
|
||||
dialogParams: SupervisorDialogSupervisorCoreUpdateParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-supervisor-core-update",
|
||||
dialogImport: () => import("./dialog-supervisor-core-update"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -3,9 +3,9 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
|
@@ -11,10 +11,7 @@ export const showHassioMarkdownDialog = (
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-markdown",
|
||||
dialogImport: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dialog-hassio-markdown" */ "./dialog-hassio-markdown"
|
||||
),
|
||||
dialogImport: () => import("./dialog-hassio-markdown"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-icon-button";
|
||||
import "@material/mwc-list/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@material/mwc-tab";
|
||||
import "@material/mwc-tab-bar";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
@@ -16,18 +18,22 @@ import {
|
||||
} from "lit-element";
|
||||
import { cache } from "lit-html/directives/cache";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-chips";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-expansion-panel";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-header-bar";
|
||||
import "../../../../src/components/ha-radio";
|
||||
import type { HaRadio } from "../../../../src/components/ha-radio";
|
||||
import "../../../../src/components/ha-related-items";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
AccessPoints,
|
||||
accesspointScan,
|
||||
NetworkInterface,
|
||||
updateNetworkInterface,
|
||||
WifiConfiguration,
|
||||
} from "../../../../src/data/hassio/network";
|
||||
import {
|
||||
showAlertDialog,
|
||||
@@ -38,54 +44,51 @@ import { haStyleDialog } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import { HassioNetworkDialogParams } from "./show-dialog-network";
|
||||
|
||||
const IP_VERSIONS = ["ipv4", "ipv6"];
|
||||
|
||||
@customElement("dialog-hassio-network")
|
||||
export class DialogHassioNetwork extends LitElement
|
||||
implements HassDialog<HassioNetworkDialogParams> {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _prosessing = false;
|
||||
|
||||
@internalProperty() private _params?: HassioNetworkDialogParams;
|
||||
|
||||
@internalProperty() private _network!: {
|
||||
interface: string;
|
||||
data: NetworkInterface;
|
||||
}[];
|
||||
@internalProperty() private _accessPoints?: AccessPoints;
|
||||
|
||||
@internalProperty() private _curTabIndex = 0;
|
||||
|
||||
@internalProperty() private _device?: {
|
||||
interface: string;
|
||||
data: NetworkInterface;
|
||||
};
|
||||
|
||||
@internalProperty() private _dirty = false;
|
||||
|
||||
@internalProperty() private _interface?: NetworkInterface;
|
||||
|
||||
@internalProperty() private _interfaces!: NetworkInterface[];
|
||||
|
||||
@internalProperty() private _params?: HassioNetworkDialogParams;
|
||||
|
||||
@internalProperty() private _processing = false;
|
||||
|
||||
@internalProperty() private _scanning = false;
|
||||
|
||||
@internalProperty() private _wifiConfiguration?: WifiConfiguration;
|
||||
|
||||
public async showDialog(params: HassioNetworkDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
this._dirty = false;
|
||||
this._curTabIndex = 0;
|
||||
this._network = Object.keys(params.network?.interfaces)
|
||||
.map((device) => ({
|
||||
interface: device,
|
||||
data: params.network.interfaces[device],
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
return a.data.primary > b.data.primary ? -1 : 1;
|
||||
});
|
||||
this._device = this._network[this._curTabIndex];
|
||||
this._device.data.nameservers = String(this._device.data.nameservers);
|
||||
this._interfaces = params.network.interfaces.sort((a, b) => {
|
||||
return a.primary > b.primary ? -1 : 1;
|
||||
});
|
||||
this._interface = { ...this._interfaces[this._curTabIndex] };
|
||||
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._params = undefined;
|
||||
this._prosessing = false;
|
||||
this._processing = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params || !this._network) {
|
||||
if (!this._params || !this._interface) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
@@ -107,11 +110,11 @@ export class DialogHassioNetwork extends LitElement
|
||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</ha-header-bar>
|
||||
${this._network.length > 1
|
||||
${this._interfaces.length > 1
|
||||
? html` <mwc-tab-bar
|
||||
.activeIndex=${this._curTabIndex}
|
||||
@MDCTabBar:activated=${this._handleTabActivated}
|
||||
>${this._network.map(
|
||||
>${this._interfaces.map(
|
||||
(device) =>
|
||||
html`<mwc-tab
|
||||
.id=${device.interface}
|
||||
@@ -129,81 +132,302 @@ export class DialogHassioNetwork extends LitElement
|
||||
|
||||
private _renderTab() {
|
||||
return html` <div class="form container">
|
||||
<ha-formfield label="DHCP">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
value="dhcp"
|
||||
name="method"
|
||||
?checked=${this._device!.data.method === "dhcp"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Static">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
value="static"
|
||||
name="method"
|
||||
?checked=${this._device!.data.method === "static"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
${this._device!.data.method !== "dhcp"
|
||||
? html` <paper-input
|
||||
class="flex-auto"
|
||||
id="ip_address"
|
||||
label="IP address/Netmask"
|
||||
.value="${this._device!.data.ip_address}"
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="gateway"
|
||||
label="Gateway address"
|
||||
.value="${this._device!.data.gateway}"
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="nameservers"
|
||||
label="DNS servers"
|
||||
.value="${this._device!.data.nameservers as string}"
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
></paper-input>
|
||||
NB!: If you are changing IP or gateway addresses, you might lose
|
||||
the connection.`
|
||||
${IP_VERSIONS.map((version) =>
|
||||
this._interface![version] ? this._renderIPConfiguration(version) : ""
|
||||
)}
|
||||
${this._interface?.type === "wireless"
|
||||
? html`
|
||||
<ha-expansion-panel header="Wi-Fi" outlined>
|
||||
${this._interface?.wifi?.ssid
|
||||
? html`<p>Connected to: ${this._interface?.wifi?.ssid}</p>`
|
||||
: ""}
|
||||
<mwc-button
|
||||
class="scan"
|
||||
@click=${this._scanForAP}
|
||||
.disabled=${this._scanning}
|
||||
>
|
||||
${this._scanning
|
||||
? html`<ha-circular-progress active size="small">
|
||||
</ha-circular-progress>`
|
||||
: "Scan for accesspoints"}
|
||||
</mwc-button>
|
||||
${this._accessPoints &&
|
||||
this._accessPoints.accesspoints &&
|
||||
this._accessPoints.accesspoints.length !== 0
|
||||
? html`
|
||||
<mwc-list>
|
||||
${this._accessPoints.accesspoints
|
||||
.filter((ap) => ap.ssid)
|
||||
.map(
|
||||
(ap) =>
|
||||
html`
|
||||
<mwc-list-item
|
||||
twoline
|
||||
@click=${this._selectAP}
|
||||
.activated=${ap.ssid ===
|
||||
this._wifiConfiguration?.ssid}
|
||||
.ap=${ap}
|
||||
>
|
||||
<span>${ap.ssid}</span>
|
||||
<span slot="secondary">
|
||||
${ap.mac} - Strength: ${ap.signal}
|
||||
</span>
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</mwc-list>
|
||||
`
|
||||
: ""}
|
||||
${this._wifiConfiguration
|
||||
? html`
|
||||
<div class="radio-row">
|
||||
<ha-formfield label="open">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChangedAp}
|
||||
.ap=${this._wifiConfiguration}
|
||||
value="open"
|
||||
name="auth"
|
||||
.checked=${this._wifiConfiguration.auth ===
|
||||
undefined ||
|
||||
this._wifiConfiguration.auth === "open"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="wep">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChangedAp}
|
||||
.ap=${this._wifiConfiguration}
|
||||
value="wep"
|
||||
name="auth"
|
||||
.checked=${this._wifiConfiguration.auth === "wep"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="wpa-psk">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChangedAp}
|
||||
.ap=${this._wifiConfiguration}
|
||||
value="wpa-psk"
|
||||
name="auth"
|
||||
.checked=${this._wifiConfiguration.auth ===
|
||||
"wpa-psk"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
${this._wifiConfiguration.auth === "wpa-psk" ||
|
||||
this._wifiConfiguration.auth === "wep"
|
||||
? html`
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
type="password"
|
||||
id="psk"
|
||||
label="Password"
|
||||
version="wifi"
|
||||
@value-changed=${this
|
||||
._handleInputValueChangedWifi}
|
||||
>
|
||||
</paper-input>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
</ha-expansion-panel>
|
||||
`
|
||||
: ""}
|
||||
${this._dirty
|
||||
? html`<div class="warning">
|
||||
If you are changing the Wi-Fi, IP or gateway addresses, you might
|
||||
lose the connection!
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<mwc-button label="close" @click=${this.closeDialog}> </mwc-button>
|
||||
<mwc-button @click=${this._updateNetwork} ?disabled=${!this._dirty}>
|
||||
${this._prosessing
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
: "Update"}
|
||||
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
|
||||
${this._processing
|
||||
? html`<ha-circular-progress active size="small">
|
||||
</ha-circular-progress>`
|
||||
: "Save"}
|
||||
</mwc-button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private async _updateNetwork() {
|
||||
this._prosessing = true;
|
||||
let options: Partial<NetworkInterface> = {
|
||||
method: this._device!.data.method,
|
||||
};
|
||||
if (options.method !== "dhcp") {
|
||||
options = {
|
||||
...options,
|
||||
address: this._device!.data.ip_address,
|
||||
gateway: this._device!.data.gateway,
|
||||
dns: String(this._device!.data.nameservers).split(","),
|
||||
};
|
||||
private _selectAP(event) {
|
||||
this._wifiConfiguration = event.currentTarget.ap;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private async _scanForAP() {
|
||||
if (!this._interface) {
|
||||
return;
|
||||
}
|
||||
this._scanning = true;
|
||||
try {
|
||||
await updateNetworkInterface(this.hass, this._device!.interface, options);
|
||||
this._accessPoints = await accesspointScan(
|
||||
this.hass,
|
||||
this._interface.interface
|
||||
);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to scan for accesspoints",
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
} finally {
|
||||
this._scanning = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _renderIPConfiguration(version: string) {
|
||||
return html`
|
||||
<ha-expansion-panel
|
||||
.header=${`IPv${version.charAt(version.length - 1)}`}
|
||||
outlined
|
||||
>
|
||||
<div class="radio-row">
|
||||
<ha-formfield label="DHCP">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
.version=${version}
|
||||
value="auto"
|
||||
name="${version}method"
|
||||
.checked=${this._interface![version]?.method === "auto"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Static">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
.version=${version}
|
||||
value="static"
|
||||
name="${version}method"
|
||||
.checked=${this._interface![version]?.method === "static"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Disabled" class="warning">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
.version=${version}
|
||||
value="disabled"
|
||||
name="${version}method"
|
||||
.checked=${this._interface![version]?.method === "disabled"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
${this._interface![version].method === "static"
|
||||
? html`
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="address"
|
||||
label="IP address/Netmask"
|
||||
.version=${version}
|
||||
.value=${this._toString(this._interface![version].address)}
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="gateway"
|
||||
label="Gateway address"
|
||||
.version=${version}
|
||||
.value=${this._interface![version].gateway}
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="nameservers"
|
||||
label="DNS servers"
|
||||
.version=${version}
|
||||
.value=${this._toString(this._interface![version].nameservers)}
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
`
|
||||
: ""}
|
||||
</ha-expansion-panel>
|
||||
`;
|
||||
}
|
||||
|
||||
_toArray(data: string | string[]): string[] {
|
||||
if (Array.isArray(data)) {
|
||||
if (data && typeof data[0] === "string") {
|
||||
data = data[0];
|
||||
}
|
||||
}
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
if (typeof data === "string") {
|
||||
return data.replace(/ /g, "").split(",");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
_toString(data: string | string[]): string {
|
||||
if (!data) {
|
||||
return "";
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
return data.join(", ");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private async _updateNetwork() {
|
||||
this._processing = true;
|
||||
let interfaceOptions: Partial<NetworkInterface> = {};
|
||||
|
||||
IP_VERSIONS.forEach((version) => {
|
||||
interfaceOptions[version] = {
|
||||
method: this._interface![version]?.method || "auto",
|
||||
};
|
||||
if (this._interface![version]?.method === "static") {
|
||||
interfaceOptions[version] = {
|
||||
...interfaceOptions[version],
|
||||
address: this._toArray(this._interface![version]?.address),
|
||||
gateway: this._interface![version]?.gateway,
|
||||
nameservers: this._toArray(this._interface![version]?.nameservers),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (this._wifiConfiguration) {
|
||||
interfaceOptions = {
|
||||
...interfaceOptions,
|
||||
wifi: {
|
||||
ssid: this._wifiConfiguration.ssid,
|
||||
mode: this._wifiConfiguration.mode,
|
||||
auth: this._wifiConfiguration.auth || "open",
|
||||
},
|
||||
};
|
||||
if (interfaceOptions.wifi!.auth !== "open") {
|
||||
interfaceOptions.wifi = {
|
||||
...interfaceOptions.wifi,
|
||||
psk: this._wifiConfiguration.psk,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interfaceOptions.enabled =
|
||||
this._wifiConfiguration !== undefined ||
|
||||
interfaceOptions.ipv4?.method !== "disabled" ||
|
||||
interfaceOptions.ipv6?.method !== "disabled";
|
||||
|
||||
try {
|
||||
await updateNetworkInterface(
|
||||
this.hass,
|
||||
this._interface!.interface,
|
||||
interfaceOptions
|
||||
);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to change network settings",
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
this._prosessing = false;
|
||||
this._processing = false;
|
||||
return;
|
||||
}
|
||||
this._params?.loadData();
|
||||
@@ -219,40 +443,73 @@ export class DialogHassioNetwork extends LitElement
|
||||
dismissText: "no",
|
||||
});
|
||||
if (!confirm) {
|
||||
this.requestUpdate("_device");
|
||||
this.requestUpdate("_interface");
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._curTabIndex = ev.detail.index;
|
||||
this._device = this._network[ev.detail.index];
|
||||
this._device.data.nameservers = String(this._device.data.nameservers);
|
||||
this._interface = { ...this._interfaces[ev.detail.index] };
|
||||
}
|
||||
|
||||
private _handleRadioValueChanged(ev: CustomEvent): void {
|
||||
const value = (ev.target as HaRadio).value as "dhcp" | "static";
|
||||
const value = (ev.target as any).value as "disabled" | "auto" | "static";
|
||||
const version = (ev.target as any).version as "ipv4" | "ipv6";
|
||||
|
||||
if (!value || !this._device || this._device!.data.method === value) {
|
||||
if (
|
||||
!value ||
|
||||
!this._interface ||
|
||||
this._interface[version]!.method === value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._dirty = true;
|
||||
|
||||
this._device!.data.method = value;
|
||||
this.requestUpdate("_device");
|
||||
this._interface[version]!.method = value;
|
||||
this.requestUpdate("_interface");
|
||||
}
|
||||
|
||||
private _handleRadioValueChangedAp(ev: CustomEvent): void {
|
||||
const value = ((ev.target as any).value as string) as
|
||||
| "open"
|
||||
| "wep"
|
||||
| "wpa-psk";
|
||||
this._wifiConfiguration!.auth = value;
|
||||
this._dirty = true;
|
||||
this.requestUpdate("_wifiConfiguration");
|
||||
}
|
||||
|
||||
private _handleInputValueChanged(ev: CustomEvent): void {
|
||||
const value: string | null | undefined = (ev.target as PaperInputElement)
|
||||
.value;
|
||||
const version = (ev.target as any).version as "ipv4" | "ipv6";
|
||||
const id = (ev.target as PaperInputElement).id;
|
||||
|
||||
if (!value || !this._device || this._device.data[id] === value) {
|
||||
if (
|
||||
!value ||
|
||||
!this._interface ||
|
||||
this._toString(this._interface[version]![id]) === this._toString(value)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._dirty = true;
|
||||
this._interface[version]![id] = value;
|
||||
}
|
||||
|
||||
this._device.data[id] = value;
|
||||
private _handleInputValueChangedWifi(ev: CustomEvent): void {
|
||||
const value: string | null | undefined = (ev.target as PaperInputElement)
|
||||
.value;
|
||||
const id = (ev.target as PaperInputElement).id;
|
||||
|
||||
if (
|
||||
!value ||
|
||||
!this._wifiConfiguration ||
|
||||
this._wifiConfiguration![id] === value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._dirty = true;
|
||||
this._wifiConfiguration![id] = value;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@@ -299,12 +556,16 @@ export class DialogHassioNetwork extends LitElement
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
|
||||
mwc-button.scan {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
:host([rtl]) app-toolbar {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
.container {
|
||||
padding: 20px 24px;
|
||||
padding: 0 8px 4px;
|
||||
}
|
||||
.form {
|
||||
margin-bottom: 53px;
|
||||
@@ -322,6 +583,24 @@ export class DialogHassioNetwork extends LitElement
|
||||
padding-bottom: max(env(safe-area-inset-bottom), 8px);
|
||||
background-color: var(--mdc-theme-surface, #fff);
|
||||
}
|
||||
.warning {
|
||||
color: var(--error-color);
|
||||
--primary-color: var(--error-color);
|
||||
}
|
||||
div.warning {
|
||||
margin: 12px 4px -12px;
|
||||
}
|
||||
|
||||
ha-expansion-panel {
|
||||
--expansion-panel-summary-padding: 0 16px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
paper-input {
|
||||
padding: 0 14px;
|
||||
}
|
||||
mwc-list-item {
|
||||
--mdc-list-side-padding: 10px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user