mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-04 19:05:22 +00:00
Compare commits
429 Commits
blocking-u
...
20211014.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3f2cce936c | ||
![]() |
6e8e9824f9 | ||
![]() |
33e1d34cb1 | ||
![]() |
48948d5854 | ||
![]() |
7fc00ce1cb | ||
![]() |
0c940be5fb | ||
![]() |
bddb505b7f | ||
![]() |
a91d25b27d | ||
![]() |
4ad005f0bf | ||
![]() |
7472545204 | ||
![]() |
164c9c8e73 | ||
![]() |
e52118db93 | ||
![]() |
9e7acacb06 | ||
![]() |
56deb15bca | ||
![]() |
a3d4969d7b | ||
![]() |
4a00957b71 | ||
![]() |
56bd731361 | ||
![]() |
b6c470edf1 | ||
![]() |
5bc0feacf0 | ||
![]() |
dc8d837e88 | ||
![]() |
83f405b695 | ||
![]() |
9bf41a37b4 | ||
![]() |
774f22b7e7 | ||
![]() |
aaa3964bb3 | ||
![]() |
6f6fc759cc | ||
![]() |
4358b7f924 | ||
![]() |
2841369d3d | ||
![]() |
ad031d4bda | ||
![]() |
588ee2c3b1 | ||
![]() |
038033cf27 | ||
![]() |
84c4bbd380 | ||
![]() |
807ce468d6 | ||
![]() |
a839494a1e | ||
![]() |
fa52442c1c | ||
![]() |
919ce2afb1 | ||
![]() |
db55be6d33 | ||
![]() |
2dc7c1afed | ||
![]() |
85956dc7fd | ||
![]() |
910cd98a38 | ||
![]() |
8022bd2868 | ||
![]() |
d5ca7e1719 | ||
![]() |
066a0771b3 | ||
![]() |
9e35c1ab68 | ||
![]() |
fb1deb838c | ||
![]() |
8e010618bb | ||
![]() |
365cf1f7ef | ||
![]() |
b226b20e3d | ||
![]() |
ec21f4c2c6 | ||
![]() |
a696d849b2 | ||
![]() |
ea3fae2ce4 | ||
![]() |
2fb3ac74eb | ||
![]() |
2d5c8ec3e9 | ||
![]() |
25c1156c88 | ||
![]() |
c44624282c | ||
![]() |
370f2eb9e4 | ||
![]() |
1793c68aae | ||
![]() |
cba6bbdc74 | ||
![]() |
6f4593508b | ||
![]() |
dc3bad56f2 | ||
![]() |
784e5e6e39 | ||
![]() |
13fe62975d | ||
![]() |
b97fd9918a | ||
![]() |
dc56c2de52 | ||
![]() |
375a5323d5 | ||
![]() |
8e3011807d | ||
![]() |
ec7c6ab96c | ||
![]() |
8a4097a366 | ||
![]() |
792a736e48 | ||
![]() |
cce0a02ebb | ||
![]() |
2ddab4eecc | ||
![]() |
f66755cbf1 | ||
![]() |
257e60a2b1 | ||
![]() |
75a3566760 | ||
![]() |
7a9f17e059 | ||
![]() |
abbfe7200a | ||
![]() |
419942112b | ||
![]() |
597d4a0426 | ||
![]() |
e023d60be7 | ||
![]() |
41a7b42037 | ||
![]() |
2936865c55 | ||
![]() |
ff2bf1f3c1 | ||
![]() |
1bccbd4173 | ||
![]() |
d7f00df391 | ||
![]() |
22f88c59c7 | ||
![]() |
8721776839 | ||
![]() |
a89da0dac0 | ||
![]() |
e4b4dc4ae9 | ||
![]() |
b26c44b2b9 | ||
![]() |
68095417b9 | ||
![]() |
b6344eb6e8 | ||
![]() |
224302cfef | ||
![]() |
abc4816888 | ||
![]() |
21e14bd644 | ||
![]() |
a89caccd32 | ||
![]() |
03dc3e52b7 | ||
![]() |
f04be8efa6 | ||
![]() |
2c32f6bcb3 | ||
![]() |
a3a08ff5c7 | ||
![]() |
ea51186767 | ||
![]() |
49494c572b | ||
![]() |
fcac3fa164 | ||
![]() |
9c1153ef37 | ||
![]() |
0adc4b33ef | ||
![]() |
c0f3215340 | ||
![]() |
bab1e6a95f | ||
![]() |
53b26a43c0 | ||
![]() |
2240d019f5 | ||
![]() |
cb11c6b3ea | ||
![]() |
5893559951 | ||
![]() |
8408d25cef | ||
![]() |
1ac2ffcf02 | ||
![]() |
6b6c38c2c8 | ||
![]() |
e55df73a91 | ||
![]() |
360c2cbfa3 | ||
![]() |
aba96674f3 | ||
![]() |
5c3d85fc90 | ||
![]() |
6486b7fd4c | ||
![]() |
5f3e980de0 | ||
![]() |
d0edbec5fb | ||
![]() |
5d46963e8a | ||
![]() |
321f441b63 | ||
![]() |
d55bade070 | ||
![]() |
6ba6b821f5 | ||
![]() |
b3dedae115 | ||
![]() |
5a1070c30f | ||
![]() |
40664997e1 | ||
![]() |
c6e83cb7c0 | ||
![]() |
e7e27e794c | ||
![]() |
1073dbe6ab | ||
![]() |
2bd9b5a015 | ||
![]() |
bc09febd2c | ||
![]() |
b2a87c90a2 | ||
![]() |
d6dbbcb0de | ||
![]() |
9ccb5360b3 | ||
![]() |
0187c4faff | ||
![]() |
605172a0bc | ||
![]() |
8565a0d911 | ||
![]() |
61c8d23a7e | ||
![]() |
5e3487ed59 | ||
![]() |
d5a161769c | ||
![]() |
1692f9c2dd | ||
![]() |
0cbac8bb44 | ||
![]() |
35a81e7f11 | ||
![]() |
ac64d293e7 | ||
![]() |
708b8787c5 | ||
![]() |
2bddd151eb | ||
![]() |
43a585187c | ||
![]() |
324658a36b | ||
![]() |
dd9a9b34d1 | ||
![]() |
2ab0e40952 | ||
![]() |
dfea80ae96 | ||
![]() |
6e38f5accf | ||
![]() |
7c952d92bf | ||
![]() |
2fae0d2d95 | ||
![]() |
67ab63f00e | ||
![]() |
719f9c28af | ||
![]() |
035d621109 | ||
![]() |
791f3b896d | ||
![]() |
fe2172a660 | ||
![]() |
640fbd616b | ||
![]() |
900efe8a36 | ||
![]() |
5bd92d04d9 | ||
![]() |
b15684bcbd | ||
![]() |
a93222dbb2 | ||
![]() |
20744e90a0 | ||
![]() |
32777b4259 | ||
![]() |
271120999c | ||
![]() |
68fe13a67d | ||
![]() |
f3606014c6 | ||
![]() |
efbf4482b2 | ||
![]() |
21a3b4f8e2 | ||
![]() |
de23b2d046 | ||
![]() |
bd8f436c1d | ||
![]() |
e963735dba | ||
![]() |
46c981103d | ||
![]() |
f6d02d8fc6 | ||
![]() |
e08f691510 | ||
![]() |
af9199aaff | ||
![]() |
8576b13f74 | ||
![]() |
2270d8a795 | ||
![]() |
f4dcce6d6c | ||
![]() |
b802a410b9 | ||
![]() |
9e3d339ec5 | ||
![]() |
fb97a98b97 | ||
![]() |
72773f3bc8 | ||
![]() |
b0fd93e0c3 | ||
![]() |
7aa2ec78f2 | ||
![]() |
047e856a61 | ||
![]() |
dbe209e3f2 | ||
![]() |
e0d23ee6cf | ||
![]() |
7a35f46370 | ||
![]() |
4a4465efb6 | ||
![]() |
3a112531cc | ||
![]() |
456209dded | ||
![]() |
2556b0d157 | ||
![]() |
1e8903fd76 | ||
![]() |
ad9f18c231 | ||
![]() |
63e3de00cb | ||
![]() |
d06ffeeede | ||
![]() |
3479fb9d94 | ||
![]() |
304bd002ae | ||
![]() |
5dad18c85f | ||
![]() |
19e4c0657a | ||
![]() |
44548fdc33 | ||
![]() |
d8929074b5 | ||
![]() |
e11532ae92 | ||
![]() |
eff48acdf4 | ||
![]() |
a9850f9641 | ||
![]() |
aab0b8a3ce | ||
![]() |
b12e062d94 | ||
![]() |
b36e342f15 | ||
![]() |
f686816c86 | ||
![]() |
dc50e54afc | ||
![]() |
3897e3d452 | ||
![]() |
2557b03b11 | ||
![]() |
29d29a337f | ||
![]() |
34f8e5e28d | ||
![]() |
afd1a68c62 | ||
![]() |
dbcf1cb907 | ||
![]() |
9ca64f9789 | ||
![]() |
4c247ac49d | ||
![]() |
e8a406526b | ||
![]() |
7fcea16c6b | ||
![]() |
028b799d2c | ||
![]() |
3485296e23 | ||
![]() |
03078cdd45 | ||
![]() |
740310800d | ||
![]() |
6d7c558482 | ||
![]() |
fdb10515c3 | ||
![]() |
5a0f13c485 | ||
![]() |
80b330ad7b | ||
![]() |
d929e1c134 | ||
![]() |
5e40dcdc38 | ||
![]() |
1dd3e2a83b | ||
![]() |
a62742fad9 | ||
![]() |
1f9c45b11c | ||
![]() |
68bec5e158 | ||
![]() |
37f1bd7d63 | ||
![]() |
5ba24211e2 | ||
![]() |
d699647418 | ||
![]() |
b246502cb6 | ||
![]() |
9b33ead8aa | ||
![]() |
32e8c1dc6d | ||
![]() |
e09ef7862e | ||
![]() |
85420304d0 | ||
![]() |
1c097a669d | ||
![]() |
4e1497c5da | ||
![]() |
49d426675f | ||
![]() |
dc6ac668b4 | ||
![]() |
4ee24b0845 | ||
![]() |
ba20aef206 | ||
![]() |
41ef6133c1 | ||
![]() |
50bd5ee8f7 | ||
![]() |
285f3fe330 | ||
![]() |
4d01199986 | ||
![]() |
bcc0052fe0 | ||
![]() |
4b592d81bd | ||
![]() |
884e323288 | ||
![]() |
78b799dd05 | ||
![]() |
847fa2e700 | ||
![]() |
481a79e311 | ||
![]() |
f19f2ff321 | ||
![]() |
6dc4d7bb70 | ||
![]() |
83460a34f4 | ||
![]() |
2adbb47373 | ||
![]() |
2159a5419a | ||
![]() |
044d6a15d9 | ||
![]() |
b6055062c6 | ||
![]() |
6fd85e043b | ||
![]() |
e07ac52356 | ||
![]() |
0f16ba9325 | ||
![]() |
378e6d28bc | ||
![]() |
539d2b880c | ||
![]() |
2982adbfa7 | ||
![]() |
5147dff670 | ||
![]() |
2cdf78c504 | ||
![]() |
cfad45b7c2 | ||
![]() |
5234e9bce5 | ||
![]() |
0ed5454d02 | ||
![]() |
03080973be | ||
![]() |
a4f51b0cb3 | ||
![]() |
749079c1c3 | ||
![]() |
ae10ff42e1 | ||
![]() |
d4cbdab4a3 | ||
![]() |
1bd6392a4c | ||
![]() |
1531e99528 | ||
![]() |
6e7af18494 | ||
![]() |
e1bae65aeb | ||
![]() |
65bfdf94c9 | ||
![]() |
9a92825954 | ||
![]() |
469faf509b | ||
![]() |
f87d4a5ab6 | ||
![]() |
9c31b749d7 | ||
![]() |
521d5df064 | ||
![]() |
4d330fba8a | ||
![]() |
2e04a55d5c | ||
![]() |
0d9d0aa18b | ||
![]() |
f8dd1795bc | ||
![]() |
1d7007584c | ||
![]() |
8cd9f891fb | ||
![]() |
6ab0f1db57 | ||
![]() |
37d754d069 | ||
![]() |
e12b194d41 | ||
![]() |
07d7fa26fe | ||
![]() |
73b9b87ef3 | ||
![]() |
0c0091375c | ||
![]() |
21a29ed3a5 | ||
![]() |
a6312f4279 | ||
![]() |
f459abdf85 | ||
![]() |
586fa1d0f0 | ||
![]() |
bf4192a1f0 | ||
![]() |
ac31eedf65 | ||
![]() |
b05dc5141c | ||
![]() |
32c6fb14dd | ||
![]() |
982c940381 | ||
![]() |
a7a8aaa887 | ||
![]() |
bf83a9980e | ||
![]() |
11be603ed3 | ||
![]() |
a432cf8405 | ||
![]() |
9dd6b3b72d | ||
![]() |
faca62b55f | ||
![]() |
a339de89f5 | ||
![]() |
e40c90e9c0 | ||
![]() |
3f447bb8a7 | ||
![]() |
21dca3fbf8 | ||
![]() |
1078bb4287 | ||
![]() |
daeed06e70 | ||
![]() |
1206e2d75f | ||
![]() |
cc81239b9d | ||
![]() |
e797c01761 | ||
![]() |
12f7366968 | ||
![]() |
b7fd7abe85 | ||
![]() |
a5e1f3d165 | ||
![]() |
212d047ada | ||
![]() |
2e16127fde | ||
![]() |
e8b53a619d | ||
![]() |
df1ca1fd96 | ||
![]() |
8f85132d48 | ||
![]() |
349144599c | ||
![]() |
979093923b | ||
![]() |
137f8ad4cb | ||
![]() |
c1f462b8f8 | ||
![]() |
6701c4c371 | ||
![]() |
fc6e459c09 | ||
![]() |
9e28b3447e | ||
![]() |
5c737e1969 | ||
![]() |
569765e77e | ||
![]() |
bc0d63ed12 | ||
![]() |
02f9893522 | ||
![]() |
b4bbe63f0f | ||
![]() |
fabbcac99f | ||
![]() |
b1b5ab6949 | ||
![]() |
4b9487183b | ||
![]() |
de5a817953 | ||
![]() |
4970f640fa | ||
![]() |
18996535b7 | ||
![]() |
2a1e31b5e9 | ||
![]() |
8ca9a0f409 | ||
![]() |
fcc89a67ba | ||
![]() |
1f377d43c5 | ||
![]() |
30d6c68908 | ||
![]() |
dc781da93a | ||
![]() |
36c20e4348 | ||
![]() |
4466950bb8 | ||
![]() |
be29828454 | ||
![]() |
7bab245073 | ||
![]() |
f5dcf0b760 | ||
![]() |
8141f78a92 | ||
![]() |
be244b3d00 | ||
![]() |
805f5ff9b6 | ||
![]() |
76daeb7e55 | ||
![]() |
9594c8106e | ||
![]() |
ed4052365c | ||
![]() |
377ebadc10 | ||
![]() |
ed4809b71e | ||
![]() |
db37dffdbb | ||
![]() |
13cab7e301 | ||
![]() |
0a50fc66e5 | ||
![]() |
a3d1a3566d | ||
![]() |
ba0be927ed | ||
![]() |
4260606267 | ||
![]() |
4665db4f27 | ||
![]() |
43503ba085 | ||
![]() |
0a83a704f1 | ||
![]() |
08de941c90 | ||
![]() |
62228ef144 | ||
![]() |
9731257782 | ||
![]() |
4ec9c9c16e | ||
![]() |
45436731e2 | ||
![]() |
27730e65e7 | ||
![]() |
a4aba93d57 | ||
![]() |
d93db16963 | ||
![]() |
c327fe11b8 | ||
![]() |
4fbc31d0b0 | ||
![]() |
9a4a1cb4ec | ||
![]() |
202d6957bc | ||
![]() |
14fcff7774 | ||
![]() |
2c9aa1cab4 | ||
![]() |
7745c10d07 | ||
![]() |
c1d571de42 | ||
![]() |
cecb66451c | ||
![]() |
0ef3421fa2 | ||
![]() |
f88e238d41 | ||
![]() |
ce3c8f264d | ||
![]() |
9fbd594f37 | ||
![]() |
5ad95cad90 | ||
![]() |
7e507b40c4 | ||
![]() |
446a9b5c02 | ||
![]() |
e02e61384e | ||
![]() |
5deb570fdf | ||
![]() |
915c46f144 | ||
![]() |
30d6c5eaf3 | ||
![]() |
6e50d1166a | ||
![]() |
0e3eed0563 | ||
![]() |
1b1676cecc | ||
![]() |
d911fe6a0b | ||
![]() |
22253a3385 | ||
![]() |
8666e6baae | ||
![]() |
2db9f33c41 | ||
![]() |
3d788d6056 | ||
![]() |
7560f98d70 | ||
![]() |
1533c22d5c | ||
![]() |
cf03f103ab | ||
![]() |
4a8a7c997e | ||
![]() |
9612bc78fe | ||
![]() |
2b86137388 | ||
![]() |
8fdbe447c1 | ||
![]() |
764ae7e0b6 | ||
![]() |
6b7e78320d |
102
.eslintrc.json
102
.eslintrc.json
@@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": [
|
||||||
|
"airbnb-base",
|
||||||
"airbnb-typescript/base",
|
"airbnb-typescript/base",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
"plugin:wc/recommended",
|
"plugin:wc/recommended",
|
||||||
"plugin:lit/recommended",
|
"plugin:lit/all",
|
||||||
"prettier"
|
"prettier"
|
||||||
],
|
],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"__BUILD__": false,
|
"__BUILD__": false,
|
||||||
"__VERSION__": false,
|
"__VERSION__": false,
|
||||||
"__STATIC_PATH__": false,
|
"__STATIC_PATH__": false,
|
||||||
|
"__SUPERVISOR__": false,
|
||||||
"Polymer": true
|
"Polymer": true
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
@@ -35,55 +37,51 @@
|
|||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"class-methods-use-this": 0,
|
"class-methods-use-this": "off",
|
||||||
"new-cap": 0,
|
"new-cap": "off",
|
||||||
"prefer-template": 0,
|
"prefer-template": "off",
|
||||||
"object-shorthand": 0,
|
"object-shorthand": "off",
|
||||||
"func-names": 0,
|
"func-names": "off",
|
||||||
"prefer-arrow-callback": 0,
|
"no-underscore-dangle": "off",
|
||||||
"no-underscore-dangle": 0,
|
"strict": "off",
|
||||||
"strict": 0,
|
"no-plusplus": "off",
|
||||||
"prefer-spread": 0,
|
"no-bitwise": "error",
|
||||||
"no-plusplus": 0,
|
"comma-dangle": "off",
|
||||||
"no-bitwise": 2,
|
"vars-on-top": "off",
|
||||||
"comma-dangle": 0,
|
"no-continue": "off",
|
||||||
"vars-on-top": 0,
|
"no-param-reassign": "off",
|
||||||
"no-continue": 0,
|
"no-multi-assign": "off",
|
||||||
"no-param-reassign": 0,
|
"no-console": "error",
|
||||||
"no-multi-assign": 0,
|
"radix": "off",
|
||||||
"no-console": 2,
|
"no-alert": "off",
|
||||||
"radix": 0,
|
"no-nested-ternary": "off",
|
||||||
"no-alert": 0,
|
"prefer-destructuring": "off",
|
||||||
"no-return-await": 0,
|
|
||||||
"no-nested-ternary": 0,
|
|
||||||
"prefer-destructuring": 0,
|
|
||||||
"no-restricted-globals": [2, "event"],
|
"no-restricted-globals": [2, "event"],
|
||||||
"prefer-promise-reject-errors": 0,
|
"prefer-promise-reject-errors": "off",
|
||||||
"import/order": 0,
|
"import/prefer-default-export": "off",
|
||||||
"import/prefer-default-export": 0,
|
"import/no-default-export": "off",
|
||||||
"import/no-unresolved": 0,
|
"import/no-unresolved": "off",
|
||||||
"import/no-cycle": 0,
|
"import/no-cycle": "off",
|
||||||
"import/extensions": [
|
"import/extensions": [
|
||||||
2,
|
"error",
|
||||||
"ignorePackages",
|
"ignorePackages",
|
||||||
{ "ts": "never", "js": "never" }
|
{ "ts": "never", "js": "never" }
|
||||||
],
|
],
|
||||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||||
"object-curly-newline": 0,
|
"object-curly-newline": "off",
|
||||||
"default-case": 0,
|
"default-case": "off",
|
||||||
"wc/no-self-class": 0,
|
"wc/no-self-class": "off",
|
||||||
"no-shadow": 0,
|
"no-shadow": "off",
|
||||||
"@typescript-eslint/camelcase": 0,
|
"@typescript-eslint/camelcase": "off",
|
||||||
"@typescript-eslint/ban-ts-comment": 0,
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/no-use-before-define": 0,
|
"@typescript-eslint/no-use-before-define": "off",
|
||||||
"@typescript-eslint/no-non-null-assertion": 0,
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
"@typescript-eslint/no-explicit-any": 0,
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"@typescript-eslint/no-unused-vars": 0,
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
"@typescript-eslint/explicit-function-return-type": 0,
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
|
||||||
"@typescript-eslint/no-shadow": ["error"],
|
"@typescript-eslint/no-shadow": ["error"],
|
||||||
"@typescript-eslint/naming-convention": [
|
"@typescript-eslint/naming-convention": [
|
||||||
0,
|
"off",
|
||||||
{
|
{
|
||||||
"selector": "default",
|
"selector": "default",
|
||||||
"format": ["camelCase", "snake_case"],
|
"format": ["camelCase", "snake_case"],
|
||||||
@@ -101,9 +99,21 @@
|
|||||||
"format": ["PascalCase"]
|
"format": ["PascalCase"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lit/attribute-value-entities": 0
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"unused-imports/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"vars": "all",
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"args": "after-used",
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"ignoreRestSiblings": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
"lit/attribute-value-entities": "off",
|
||||||
|
"lit/no-template-map": "off"
|
||||||
},
|
},
|
||||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
"plugins": ["disable", "unused-imports"],
|
||||||
"processor": "disable/disable",
|
"processor": "disable/disable"
|
||||||
"ignorePatterns": ["src/resources/lit-virtualizer/*"]
|
|
||||||
}
|
}
|
||||||
|
82
.github/workflows/ci.yaml
vendored
82
.github/workflows/ci.yaml
vendored
@@ -10,83 +10,64 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: 14
|
||||||
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setting up Node.js
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
- name: Get yarn cache path
|
cache: yarn
|
||||||
id: yarn-cache-dir-path
|
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
|
||||||
- name: Fetching Yarn cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Build resources
|
- name: Build resources
|
||||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
|
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-demos
|
||||||
- name: Run eslint
|
- name: Run eslint
|
||||||
run: yarn run lint:eslint
|
run: yarn run lint:eslint
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: yarn run lint:types
|
run: yarn run lint:types
|
||||||
- name: Run prettier
|
- name: Run prettier
|
||||||
run: yarn run lint:prettier
|
run: yarn run lint:prettier
|
||||||
|
- name: Check for duplicate dependencies
|
||||||
|
run: yarn dedupe --check
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setting up Node.js
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
- name: Get yarn cache path
|
cache: yarn
|
||||||
id: yarn-cache-dir-path
|
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
|
||||||
- name: Fetching Yarn cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
- name: Run Mocha
|
- name: Build resources
|
||||||
run: npm run mocha
|
run: ./node_modules/.bin/gulp build-translations build-locale-data
|
||||||
|
- name: Run Tests
|
||||||
|
run: yarn run test
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setting up Node.js
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
- name: Get yarn cache path
|
cache: yarn
|
||||||
id: yarn-cache-dir-path
|
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
|
||||||
- name: Fetching Yarn cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
@@ -101,20 +82,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setting up Node.js
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
- name: Get yarn cache path
|
cache: yarn
|
||||||
id: yarn-cache-dir-path
|
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
|
||||||
- name: Fetching Yarn cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
|
22
.github/workflows/demo.yaml
vendored
22
.github/workflows/demo.yaml
vendored
@@ -4,26 +4,22 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: 14
|
||||||
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setting up Node.js
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
- name: Get yarn cache path
|
cache: yarn
|
||||||
id: yarn-cache-dir-path
|
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
|
||||||
- name: Fetching Yarn cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-yarn-
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
env:
|
env:
|
||||||
|
14
.github/workflows/release.yaml
vendored
14
.github/workflows/release.yaml
vendored
@@ -7,7 +7,8 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: 3.8
|
PYTHON_VERSION: 3.8
|
||||||
NODE_VERSION: 12.1
|
NODE_VERSION: 14
|
||||||
|
NODE_OPTIONS: --max_old_space_size=6144
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@@ -29,7 +30,15 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Download Translations
|
||||||
|
run: ./script/translations_download
|
||||||
|
env:
|
||||||
|
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||||
- name: Build and release package
|
- name: Build and release package
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install twine
|
python3 -m pip install twine
|
||||||
@@ -64,8 +73,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||||
tag:
|
tag:
|
||||||
- "3.8-alpine3.12"
|
- "3.9-alpine3.14"
|
||||||
- "3.9-alpine3.13"
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download requirements.txt
|
- name: Download requirements.txt
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
|
42
.github/workflows/translations.yaml
vendored
42
.github/workflows/translations.yaml
vendored
@@ -1,8 +1,6 @@
|
|||||||
name: Translations
|
name: Translations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
|
||||||
- cron: "30 0 * * *"
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
@@ -10,7 +8,7 @@ on:
|
|||||||
- src/translations/en.json
|
- src/translations/en.json
|
||||||
|
|
||||||
env:
|
env:
|
||||||
NODE_VERSION: 12
|
NODE_VERSION: 14
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload:
|
upload:
|
||||||
@@ -20,46 +18,8 @@ jobs:
|
|||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v2
|
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
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||||
|
|
||||||
./script/translations_upload_base
|
./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
|
|
||||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@@ -3,14 +3,19 @@
|
|||||||
|
|
||||||
# build
|
# build
|
||||||
build
|
build
|
||||||
build-translations/*
|
|
||||||
hass_frontend/*
|
hass_frontend/*
|
||||||
dist
|
dist
|
||||||
|
|
||||||
# yarn
|
# yarn
|
||||||
.yarn
|
.yarn/*
|
||||||
yarn-error.log
|
!.yarn/patches
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
.pnp.*
|
||||||
node_modules/*
|
node_modules/*
|
||||||
|
yarn-error.log
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
# Python stuff
|
# Python stuff
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
require: "test-mocha/testconf.js",
|
|
||||||
timeout: 10000,
|
|
||||||
};
|
|
@@ -1,5 +1,4 @@
|
|||||||
build
|
build
|
||||||
build-translations/*
|
|
||||||
translations/*
|
translations/*
|
||||||
node_modules/*
|
node_modules/*
|
||||||
hass_frontend/*
|
hass_frontend/*
|
||||||
|
1536
.yarn/patches/@lit-labs/virtualizer/0.7.0.patch
Normal file
1536
.yarn/patches/@lit-labs/virtualizer/0.7.0.patch
Normal file
File diff suppressed because one or more lines are too long
29
.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
Normal file
29
.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
diff --git a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||||
|
index d92179f7fd5315203f870a6963e871dc8ddf6c0c..362e284121b97e0fba0925225777aebc32e26b8d 100644
|
||||||
|
--- a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||||
|
+++ b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||||
|
@@ -1,14 +1,15 @@
|
||||||
|
-let _ET, ET;
|
||||||
|
+let _ET;
|
||||||
|
+let ET;
|
||||||
|
export default async function EventTarget() {
|
||||||
|
- return ET || init();
|
||||||
|
+ return ET || init();
|
||||||
|
}
|
||||||
|
async function init() {
|
||||||
|
- _ET = window.EventTarget;
|
||||||
|
- try {
|
||||||
|
- new _ET();
|
||||||
|
- }
|
||||||
|
- catch (_a) {
|
||||||
|
- _ET = (await import('event-target-shim')).EventTarget;
|
||||||
|
- }
|
||||||
|
- return (ET = _ET);
|
||||||
|
+ _ET = window.EventTarget;
|
||||||
|
+ try {
|
||||||
|
+ new _ET();
|
||||||
|
+ } catch (_a) {
|
||||||
|
+ _ET = (await import("event-target-shim")).default.EventTarget;
|
||||||
|
+ }
|
||||||
|
+ return (ET = _ET);
|
||||||
|
}
|
34
.yarn/patches/@polymer/polymer/pr-5569.patch
Normal file
34
.yarn/patches/@polymer/polymer/pr-5569.patch
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
|
||||||
|
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
|
||||||
|
--- a/lib/legacy/class.js
|
||||||
|
+++ b/lib/legacy/class.js
|
||||||
|
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
|
||||||
|
// only proceed if the generated class' prototype has not been registered.
|
||||||
|
const generatedProto = PolymerGenerated.prototype;
|
||||||
|
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
|
||||||
|
- generatedProto.__hasRegisterFinished = true;
|
||||||
|
+ // make sure legacy lifecycle is called on the *element*'s prototype
|
||||||
|
+ // and not the generated class prototype; if the element has been
|
||||||
|
+ // extended, these are *not* the same.
|
||||||
|
+ const proto = Object.getPrototypeOf(this);
|
||||||
|
+ // Only set flag when generated prototype itself is registered,
|
||||||
|
+ // as this element may be extended from, and needs to run `registered`
|
||||||
|
+ // on all behaviors on the subclass as well.
|
||||||
|
+ if (proto === generatedProto) {
|
||||||
|
+ generatedProto.__hasRegisterFinished = true;
|
||||||
|
+ }
|
||||||
|
// ensure superclass is registered first.
|
||||||
|
super._registered();
|
||||||
|
// copy properties onto the generated class lazily if we're optimizing,
|
||||||
|
- if (legacyOptimizations) {
|
||||||
|
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
|
||||||
|
+ generatedProto.__hasCopiedProperties = true;
|
||||||
|
copyPropertiesToProto(generatedProto);
|
||||||
|
}
|
||||||
|
- // make sure legacy lifecycle is called on the *element*'s prototype
|
||||||
|
- // and not the generated class prototype; if the element has been
|
||||||
|
- // extended, these are *not* the same.
|
||||||
|
- const proto = Object.getPrototypeOf(this);
|
||||||
|
let list = lifecycle.beforeRegister;
|
||||||
|
if (list) {
|
||||||
|
for (let i=0; i < list.length; i++) {
|
77
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
77
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
631
.yarn/releases/yarn-3.0.2.cjs
vendored
Executable file
631
.yarn/releases/yarn-3.0.2.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
9
.yarnrc.yml
Normal file
9
.yarnrc.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||||
|
spec: "@yarnpkg/plugin-typescript"
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||||
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.0.2.cjs
|
170
build-scripts/babel-plugins/inline-constants-plugin.js
Normal file
170
build-scripts/babel-plugins/inline-constants-plugin.js
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||||
|
module.exports = function inlineConstants(babel, options, cwd) {
|
||||||
|
const t = babel.types;
|
||||||
|
|
||||||
|
if (!Array.isArray(options.modules)) {
|
||||||
|
throw new TypeError(
|
||||||
|
"babel-plugin-inline-constants: expected a `modules` array to be passed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) {
|
||||||
|
throw new TypeError(
|
||||||
|
"babel-plugin-inline-constants: expected `resolveExtensions` to be an array"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoreModuleNotFound = options.ignoreModuleNotFound;
|
||||||
|
const resolveExtensions = options.resolveExtensions;
|
||||||
|
|
||||||
|
const hasRelativeModules = options.modules.some(
|
||||||
|
(module) => module.startsWith(".") || module.startsWith("/")
|
||||||
|
);
|
||||||
|
|
||||||
|
const modules = Object.fromEntries(
|
||||||
|
options.modules.map((module) => {
|
||||||
|
const absolute = module.startsWith(".")
|
||||||
|
? require.resolve(module, { paths: [cwd] })
|
||||||
|
: module;
|
||||||
|
// eslint-disable-next-line import/no-dynamic-require
|
||||||
|
return [absolute, require(absolute)];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const toLiteral = (value) => {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
return t.stringLiteral(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === "number") {
|
||||||
|
return t.numericLiteral(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
return t.booleanLiteral(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === null) {
|
||||||
|
return t.nullLiteral();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
"babel-plugin-inline-constants: cannot handle non-literal `" + value + "`"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveAbsolute = (value, state, resolveExtensionIndex) => {
|
||||||
|
if (!state.filename) {
|
||||||
|
throw new TypeError(
|
||||||
|
"babel-plugin-inline-constants: expected a `filename` to be set for files"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolveExtensions && resolveExtensionIndex !== undefined) {
|
||||||
|
value += resolveExtensions[resolveExtensionIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return require.resolve(value, { paths: [path.dirname(state.filename)] });
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
error.code === "MODULE_NOT_FOUND" &&
|
||||||
|
resolveExtensions &&
|
||||||
|
(resolveExtensionIndex === undefined ||
|
||||||
|
resolveExtensionIndex < resolveExtensions.length - 1)
|
||||||
|
) {
|
||||||
|
const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1;
|
||||||
|
return resolveAbsolute(value, state, resolveExtensionIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const importDeclaration = (p, state) => {
|
||||||
|
if (p.node.type !== "ImportDeclaration") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const absolute =
|
||||||
|
hasRelativeModules && p.node.source.value.startsWith(".")
|
||||||
|
? resolveAbsolute(p.node.source.value, state)
|
||||||
|
: p.node.source.value;
|
||||||
|
|
||||||
|
if (!absolute || !(absolute in modules)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const module = modules[absolute];
|
||||||
|
|
||||||
|
for (const specifier of p.node.specifiers) {
|
||||||
|
if (
|
||||||
|
specifier.type === "ImportDefaultSpecifier" &&
|
||||||
|
specifier.local &&
|
||||||
|
specifier.local.type === "Identifier"
|
||||||
|
) {
|
||||||
|
if (!("default" in module)) {
|
||||||
|
throw new Error(
|
||||||
|
"babel-plugin-inline-constants: cannot access default export from `" +
|
||||||
|
p.node.source.value +
|
||||||
|
"`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const variableValue = toLiteral(module.default);
|
||||||
|
const variable = t.variableDeclarator(
|
||||||
|
t.identifier(specifier.local.name),
|
||||||
|
variableValue
|
||||||
|
);
|
||||||
|
|
||||||
|
p.insertBefore({
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
kind: "const",
|
||||||
|
declarations: [variable],
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
specifier.type === "ImportSpecifier" &&
|
||||||
|
specifier.imported &&
|
||||||
|
specifier.imported.type === "Identifier" &&
|
||||||
|
specifier.local &&
|
||||||
|
specifier.local.type === "Identifier"
|
||||||
|
) {
|
||||||
|
if (!(specifier.imported.name in module)) {
|
||||||
|
throw new Error(
|
||||||
|
"babel-plugin-inline-constants: cannot access `" +
|
||||||
|
specifier.imported.name +
|
||||||
|
"` from `" +
|
||||||
|
p.node.source.value +
|
||||||
|
"`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const variableValue = toLiteral(module[specifier.imported.name]);
|
||||||
|
const variable = t.variableDeclarator(
|
||||||
|
t.identifier(specifier.local.name),
|
||||||
|
variableValue
|
||||||
|
);
|
||||||
|
|
||||||
|
p.insertBefore({
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
kind: "const",
|
||||||
|
declarations: [variable],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error("Cannot handle specifier `" + specifier.type + "`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
visitor: {
|
||||||
|
ImportDeclaration: importDeclaration,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@@ -5,8 +5,6 @@ const paths = require("./paths.js");
|
|||||||
|
|
||||||
// Files from NPM Packages that should not be imported
|
// Files from NPM Packages that should not be imported
|
||||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||||
// Bloats bundle and it's not used.
|
|
||||||
path.resolve(require.resolve("moment"), "../locale"),
|
|
||||||
// Part of yaml.js and only used for !!js functions that we don't use
|
// Part of yaml.js and only used for !!js functions that we don't use
|
||||||
require.resolve("esprima"),
|
require.resolve("esprima"),
|
||||||
];
|
];
|
||||||
@@ -20,7 +18,8 @@ module.exports.emptyPackages = ({ latestBuild }) =>
|
|||||||
require.resolve("@polymer/paper-styles/default-theme.js"),
|
require.resolve("@polymer/paper-styles/default-theme.js"),
|
||||||
// Loads stuff from a CDN
|
// Loads stuff from a CDN
|
||||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
|
||||||
|
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
|
||||||
// Compatibility not needed for latest builds
|
// Compatibility not needed for latest builds
|
||||||
latestBuild &&
|
latestBuild &&
|
||||||
// wrapped in require.resolve so it blows up if file no longer exists
|
// wrapped in require.resolve so it blows up if file no longer exists
|
||||||
@@ -36,6 +35,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
|
|||||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||||
__VERSION__: JSON.stringify(env.version()),
|
__VERSION__: JSON.stringify(env.version()),
|
||||||
__DEMO__: false,
|
__DEMO__: false,
|
||||||
|
__SUPERVISOR__: false,
|
||||||
__BACKWARDS_COMPAT__: false,
|
__BACKWARDS_COMPAT__: false,
|
||||||
__STATIC_PATH__: "/static/",
|
__STATIC_PATH__: "/static/",
|
||||||
"process.env.NODE_ENV": JSON.stringify(
|
"process.env.NODE_ENV": JSON.stringify(
|
||||||
@@ -58,12 +58,23 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
|||||||
"@babel/preset-env",
|
"@babel/preset-env",
|
||||||
{
|
{
|
||||||
useBuiltIns: "entry",
|
useBuiltIns: "entry",
|
||||||
corejs: "3.6",
|
corejs: "3.15",
|
||||||
|
bugfixes: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"@babel/preset-typescript",
|
"@babel/preset-typescript",
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
plugins: [
|
plugins: [
|
||||||
|
[
|
||||||
|
path.resolve(
|
||||||
|
paths.polymer_dir,
|
||||||
|
"build-scripts/babel-plugins/inline-constants-plugin.js"
|
||||||
|
),
|
||||||
|
{
|
||||||
|
modules: ["@mdi/js"],
|
||||||
|
ignoreModuleNotFound: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||||
!latestBuild && [
|
!latestBuild && [
|
||||||
"@babel/plugin-proposal-object-rest-spread",
|
"@babel/plugin-proposal-object-rest-spread",
|
||||||
@@ -72,12 +83,19 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
|||||||
// Only support the syntax, Webpack will handle it.
|
// Only support the syntax, Webpack will handle it.
|
||||||
"@babel/plugin-syntax-import-meta",
|
"@babel/plugin-syntax-import-meta",
|
||||||
"@babel/plugin-syntax-dynamic-import",
|
"@babel/plugin-syntax-dynamic-import",
|
||||||
|
"@babel/plugin-syntax-top-level-await",
|
||||||
"@babel/plugin-proposal-optional-chaining",
|
"@babel/plugin-proposal-optional-chaining",
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||||
["@babel/plugin-proposal-private-methods", { loose: true }],
|
["@babel/plugin-proposal-private-methods", { loose: true }],
|
||||||
|
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||||
["@babel/plugin-proposal-class-properties", { loose: true }],
|
["@babel/plugin-proposal-class-properties", { loose: true }],
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
|
exclude: [
|
||||||
|
// \\ for Windows, / for Mac OS and Linux
|
||||||
|
/node_modules[\\/]core-js/,
|
||||||
|
/node_modules[\\/]webpack[\\/]buildin/,
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const outputPath = (outputRoot, latestBuild) =>
|
const outputPath = (outputRoot, latestBuild) =>
|
||||||
@@ -177,6 +195,9 @@ module.exports.config = {
|
|||||||
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
|
defineOverlay: {
|
||||||
|
__SUPERVISOR__: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -189,6 +210,9 @@ module.exports.config = {
|
|||||||
publicPath: publicPath(latestBuild),
|
publicPath: publicPath(latestBuild),
|
||||||
isProdBuild,
|
isProdBuild,
|
||||||
latestBuild,
|
latestBuild,
|
||||||
|
defineOverlay: {
|
||||||
|
__DEMO__: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -5,6 +5,7 @@ const env = require("../env");
|
|||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./translations.js");
|
require("./translations.js");
|
||||||
|
require("./locale-data.js");
|
||||||
require("./gen-icons-json.js");
|
require("./gen-icons-json.js");
|
||||||
require("./gather-static.js");
|
require("./gather-static.js");
|
||||||
require("./compress.js");
|
require("./compress.js");
|
||||||
@@ -26,7 +27,8 @@ gulp.task(
|
|||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"gen-pages-dev",
|
"gen-pages-dev",
|
||||||
"gen-index-app-dev",
|
"gen-index-app-dev",
|
||||||
"build-translations"
|
"build-translations",
|
||||||
|
"build-locale-data"
|
||||||
),
|
),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useWDS()
|
env.useWDS()
|
||||||
@@ -44,11 +46,11 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "production";
|
process.env.NODE_ENV = "production";
|
||||||
},
|
},
|
||||||
"clean",
|
"clean",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||||
...// Don't compress running tests
|
// Don't compress running tests
|
||||||
(env.isTest() ? [] : ["compress-app"]),
|
...(env.isTest() ? [] : ["compress-app"]),
|
||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-pages-prod",
|
"gen-pages-prod",
|
||||||
"gen-index-app-prod",
|
"gen-index-app-prod",
|
||||||
|
@@ -18,7 +18,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-cast",
|
"clean-cast",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||||
"copy-static-cast",
|
"copy-static-cast",
|
||||||
"gen-index-cast-dev",
|
"gen-index-cast-dev",
|
||||||
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
||||||
@@ -33,7 +33,7 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-cast",
|
"clean-cast",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||||
"copy-static-cast",
|
"copy-static-cast",
|
||||||
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
|
env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast",
|
||||||
"gen-index-cast-prod"
|
"gen-index-cast-prod"
|
||||||
|
@@ -5,32 +5,32 @@ require("./translations");
|
|||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"clean",
|
"clean",
|
||||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
gulp.parallel("clean-translations", () =>
|
||||||
return del([paths.app_output_root, paths.build_dir]);
|
del([paths.app_output_root, paths.build_dir])
|
||||||
})
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"clean-demo",
|
"clean-demo",
|
||||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
gulp.parallel("clean-translations", () =>
|
||||||
return del([paths.demo_output_root, paths.build_dir]);
|
del([paths.demo_output_root, paths.build_dir])
|
||||||
})
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"clean-cast",
|
"clean-cast",
|
||||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
gulp.parallel("clean-translations", () =>
|
||||||
return del([paths.cast_output_root, paths.build_dir]);
|
del([paths.cast_output_root, paths.build_dir])
|
||||||
})
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("clean-hassio", function cleanOutputAndBuildDir() {
|
gulp.task("clean-hassio", () =>
|
||||||
return del([paths.hassio_output_root, paths.build_dir]);
|
del([paths.hassio_output_root, paths.build_dir])
|
||||||
});
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"clean-gallery",
|
"clean-gallery",
|
||||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
gulp.parallel("clean-translations", () =>
|
||||||
return del([paths.gallery_output_root, paths.build_dir]);
|
del([paths.gallery_output_root, paths.build_dir])
|
||||||
})
|
)
|
||||||
);
|
);
|
||||||
|
@@ -20,7 +20,12 @@ gulp.task(
|
|||||||
},
|
},
|
||||||
"clean-demo",
|
"clean-demo",
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
|
gulp.parallel(
|
||||||
|
"gen-icons-json",
|
||||||
|
"gen-index-demo-dev",
|
||||||
|
"build-translations",
|
||||||
|
"build-locale-data"
|
||||||
|
),
|
||||||
"copy-static-demo",
|
"copy-static-demo",
|
||||||
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
|
env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo"
|
||||||
)
|
)
|
||||||
@@ -35,7 +40,7 @@ gulp.task(
|
|||||||
"clean-demo",
|
"clean-demo",
|
||||||
// Cast needs to be backwards compatible and older HA has no translations
|
// Cast needs to be backwards compatible and older HA has no translations
|
||||||
"translations-enable-merge-backend",
|
"translations-enable-merge-backend",
|
||||||
gulp.parallel("gen-icons-json", "build-translations"),
|
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||||
"copy-static-demo",
|
"copy-static-demo",
|
||||||
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
|
env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo",
|
||||||
"gen-index-demo-prod"
|
"gen-index-demo-prod"
|
||||||
|
@@ -302,15 +302,23 @@ gulp.task("gen-index-hassio-prod", async () => {
|
|||||||
|
|
||||||
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
||||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||||
|
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||||
`
|
`
|
||||||
try {
|
function loadES5() {
|
||||||
new Function("import('${latestEntrypoint}')")();
|
|
||||||
} catch (err) {
|
|
||||||
var el = document.createElement('script');
|
var el = document.createElement('script');
|
||||||
el.src = '${es5Entrypoint}';
|
el.src = '${es5Entrypoint}';
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
|
}
|
||||||
|
if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) {
|
||||||
|
loadES5();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
new Function("import('${latestEntrypoint}')")();
|
||||||
|
} catch (err) {
|
||||||
|
loadES5();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
{ encoding: "utf-8" }
|
{ encoding: "utf-8" }
|
||||||
|
@@ -51,6 +51,7 @@ gulp.task(
|
|||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"build-translations",
|
"build-translations",
|
||||||
|
"build-locale-data",
|
||||||
"gather-gallery-demos"
|
"gather-gallery-demos"
|
||||||
),
|
),
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
@@ -70,6 +71,7 @@ gulp.task(
|
|||||||
gulp.parallel(
|
gulp.parallel(
|
||||||
"gen-icons-json",
|
"gen-icons-json",
|
||||||
"build-translations",
|
"build-translations",
|
||||||
|
"build-locale-data",
|
||||||
"gather-gallery-demos"
|
"gather-gallery-demos"
|
||||||
),
|
),
|
||||||
"copy-static-gallery",
|
"copy-static-gallery",
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const cpx = require("cpx");
|
|
||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const paths = require("../paths");
|
const paths = require("../paths");
|
||||||
|
|
||||||
@@ -13,19 +12,28 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
|
|||||||
const copyFileDir = (fromFile, toDir) =>
|
const copyFileDir = (fromFile, toDir) =>
|
||||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||||
|
|
||||||
const genStaticPath = (staticDir) => (...parts) =>
|
const genStaticPath =
|
||||||
path.resolve(staticDir, ...parts);
|
(staticDir) =>
|
||||||
|
(...parts) =>
|
||||||
|
path.resolve(staticDir, ...parts);
|
||||||
|
|
||||||
function copyTranslations(staticDir) {
|
function copyTranslations(staticDir) {
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
|
|
||||||
// Translation output
|
// Translation output
|
||||||
fs.copySync(
|
fs.copySync(
|
||||||
polyPath("build-translations/output"),
|
polyPath("build/translations/output"),
|
||||||
staticPath("translations")
|
staticPath("translations")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyLocaleData(staticDir) {
|
||||||
|
const staticPath = genStaticPath(staticDir);
|
||||||
|
|
||||||
|
// Locale data output
|
||||||
|
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
|
||||||
|
}
|
||||||
|
|
||||||
function copyMdiIcons(staticDir) {
|
function copyMdiIcons(staticDir) {
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
|
|
||||||
@@ -62,9 +70,12 @@ function copyLoaderJS(staticDir) {
|
|||||||
function copyFonts(staticDir) {
|
function copyFonts(staticDir) {
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
// Local fonts
|
// Local fonts
|
||||||
cpx.copySync(
|
fs.copySync(
|
||||||
npmPath("roboto-fontface/fonts/roboto/*.woff2"),
|
npmPath("roboto-fontface/fonts/roboto/"),
|
||||||
staticPath("fonts/roboto")
|
staticPath("fonts/roboto/"),
|
||||||
|
{
|
||||||
|
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +91,11 @@ function copyMapPanel(staticDir) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gulp.task("copy-locale-data", async () => {
|
||||||
|
const staticDir = paths.app_output_static;
|
||||||
|
copyLocaleData(staticDir);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("copy-translations-app", async () => {
|
gulp.task("copy-translations-app", async () => {
|
||||||
const staticDir = paths.app_output_static;
|
const staticDir = paths.app_output_static;
|
||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
@@ -90,6 +106,11 @@ gulp.task("copy-translations-supervisor", async () => {
|
|||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("copy-locale-data-supervisor", async () => {
|
||||||
|
const staticDir = paths.hassio_output_static;
|
||||||
|
copyLocaleData(staticDir);
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("copy-static-app", async () => {
|
gulp.task("copy-static-app", async () => {
|
||||||
const staticDir = paths.app_output_static;
|
const staticDir = paths.app_output_static;
|
||||||
// Basic static files
|
// Basic static files
|
||||||
@@ -99,6 +120,7 @@ gulp.task("copy-static-app", async () => {
|
|||||||
copyPolyfills(staticDir);
|
copyPolyfills(staticDir);
|
||||||
copyFonts(staticDir);
|
copyFonts(staticDir);
|
||||||
copyTranslations(staticDir);
|
copyTranslations(staticDir);
|
||||||
|
copyLocaleData(staticDir);
|
||||||
copyMdiIcons(staticDir);
|
copyMdiIcons(staticDir);
|
||||||
|
|
||||||
// Panel assets
|
// Panel assets
|
||||||
@@ -119,6 +141,7 @@ gulp.task("copy-static-demo", async () => {
|
|||||||
copyMapPanel(paths.demo_output_static);
|
copyMapPanel(paths.demo_output_static);
|
||||||
copyFonts(paths.demo_output_static);
|
copyFonts(paths.demo_output_static);
|
||||||
copyTranslations(paths.demo_output_static);
|
copyTranslations(paths.demo_output_static);
|
||||||
|
copyLocaleData(paths.demo_output_static);
|
||||||
copyMdiIcons(paths.demo_output_static);
|
copyMdiIcons(paths.demo_output_static);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -133,6 +156,7 @@ gulp.task("copy-static-cast", async () => {
|
|||||||
copyMapPanel(paths.cast_output_static);
|
copyMapPanel(paths.cast_output_static);
|
||||||
copyFonts(paths.cast_output_static);
|
copyFonts(paths.cast_output_static);
|
||||||
copyTranslations(paths.cast_output_static);
|
copyTranslations(paths.cast_output_static);
|
||||||
|
copyLocaleData(paths.cast_output_static);
|
||||||
copyMdiIcons(paths.cast_output_static);
|
copyMdiIcons(paths.cast_output_static);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -148,5 +172,6 @@ gulp.task("copy-static-gallery", async () => {
|
|||||||
copyMapPanel(paths.gallery_output_static);
|
copyMapPanel(paths.gallery_output_static);
|
||||||
copyFonts(paths.gallery_output_static);
|
copyFonts(paths.gallery_output_static);
|
||||||
copyTranslations(paths.gallery_output_static);
|
copyTranslations(paths.gallery_output_static);
|
||||||
|
copyLocaleData(paths.gallery_output_static);
|
||||||
copyMdiIcons(paths.gallery_output_static);
|
copyMdiIcons(paths.gallery_output_static);
|
||||||
});
|
});
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const env = require("../env");
|
const env = require("../env");
|
||||||
const paths = require("../paths");
|
|
||||||
|
|
||||||
require("./clean.js");
|
require("./clean.js");
|
||||||
require("./gen-icons-json.js");
|
require("./gen-icons-json.js");
|
||||||
@@ -20,10 +17,11 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "development";
|
process.env.NODE_ENV = "development";
|
||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
|
||||||
"gen-index-hassio-dev",
|
"gen-index-hassio-dev",
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
"copy-translations-supervisor",
|
"copy-translations-supervisor",
|
||||||
|
"build-locale-data",
|
||||||
|
"copy-locale-data-supervisor",
|
||||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -35,9 +33,10 @@ gulp.task(
|
|||||||
process.env.NODE_ENV = "production";
|
process.env.NODE_ENV = "production";
|
||||||
},
|
},
|
||||||
"clean-hassio",
|
"clean-hassio",
|
||||||
"gen-icons-json",
|
|
||||||
"build-supervisor-translations",
|
"build-supervisor-translations",
|
||||||
"copy-translations-supervisor",
|
"copy-translations-supervisor",
|
||||||
|
"build-locale-data",
|
||||||
|
"copy-locale-data-supervisor",
|
||||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||||
"gen-index-hassio-prod",
|
"gen-index-hassio-prod",
|
||||||
...// Don't compress running tests
|
...// Don't compress running tests
|
||||||
|
74
build-scripts/gulp/locale-data.js
Executable file
74
build-scripts/gulp/locale-data.js
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
|
||||||
|
const del = require("del");
|
||||||
|
const path = require("path");
|
||||||
|
const gulp = require("gulp");
|
||||||
|
const fs = require("fs");
|
||||||
|
const paths = require("../paths");
|
||||||
|
|
||||||
|
const outDir = "build/locale-data";
|
||||||
|
|
||||||
|
gulp.task("clean-locale-data", () => del([outDir]));
|
||||||
|
|
||||||
|
gulp.task("ensure-locale-data-build-dir", (done) => {
|
||||||
|
if (!fs.existsSync(outDir)) {
|
||||||
|
fs.mkdirSync(outDir, { recursive: true });
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
const modules = {
|
||||||
|
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||||
|
"intl-datetimeformat": "DateTimeFormat",
|
||||||
|
"intl-numberformat": "NumberFormat",
|
||||||
|
};
|
||||||
|
|
||||||
|
gulp.task("create-locale-data", (done) => {
|
||||||
|
const translationMeta = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(paths.translations_src, "translationMetadata.json")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Object.entries(modules).forEach(([module, className]) => {
|
||||||
|
Object.keys(translationMeta).forEach((lang) => {
|
||||||
|
try {
|
||||||
|
const localeData = String(
|
||||||
|
fs.readFileSync(
|
||||||
|
require.resolve(`@formatjs/${module}/locale-data/${lang}.js`)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
new RegExp(
|
||||||
|
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
||||||
|
"im"
|
||||||
|
),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
.replace(/\)\s*}/im, "");
|
||||||
|
// make sure we have valid JSON
|
||||||
|
JSON.parse(localeData);
|
||||||
|
if (!fs.existsSync(path.join(outDir, module))) {
|
||||||
|
fs.mkdirSync(path.join(outDir, module), { recursive: true });
|
||||||
|
}
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(outDir, `${module}/${lang}.json`),
|
||||||
|
localeData
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code !== "MODULE_NOT_FOUND") {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
"build-locale-data",
|
||||||
|
gulp.series(
|
||||||
|
"clean-locale-data",
|
||||||
|
"ensure-locale-data-build-dir",
|
||||||
|
"create-locale-data"
|
||||||
|
)
|
||||||
|
);
|
@@ -1,3 +1,5 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const del = require("del");
|
const del = require("del");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
@@ -15,7 +17,7 @@ const paths = require("../paths");
|
|||||||
|
|
||||||
const inFrontendDir = "translations/frontend";
|
const inFrontendDir = "translations/frontend";
|
||||||
const inBackendDir = "translations/backend";
|
const inBackendDir = "translations/backend";
|
||||||
const workDir = "build-translations";
|
const workDir = "build/translations";
|
||||||
const fullDir = workDir + "/full";
|
const fullDir = workDir + "/full";
|
||||||
const coreDir = workDir + "/core";
|
const coreDir = workDir + "/core";
|
||||||
const outDir = workDir + "/output";
|
const outDir = workDir + "/output";
|
||||||
@@ -26,13 +28,6 @@ gulp.task("translations-enable-merge-backend", (done) => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
String.prototype.rsplit = function (sep, maxsplit) {
|
|
||||||
var split = this.split(sep);
|
|
||||||
return maxsplit
|
|
||||||
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
|
|
||||||
: split;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Panel translations which should be split from the core translations.
|
// Panel translations which should be split from the core translations.
|
||||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||||
require("../../src/translations/en.json").ui.panel
|
require("../../src/translations/en.json").ui.panel
|
||||||
@@ -40,7 +35,7 @@ const TRANSLATION_FRAGMENTS = Object.keys(
|
|||||||
|
|
||||||
function recursiveFlatten(prefix, data) {
|
function recursiveFlatten(prefix, data) {
|
||||||
let output = {};
|
let output = {};
|
||||||
Object.keys(data).forEach(function (key) {
|
Object.keys(data).forEach((key) => {
|
||||||
if (typeof data[key] === "object") {
|
if (typeof data[key] === "object") {
|
||||||
output = {
|
output = {
|
||||||
...output,
|
...output,
|
||||||
@@ -101,15 +96,19 @@ function lokaliseTransform(data, original, file) {
|
|||||||
if (value instanceof Object) {
|
if (value instanceof Object) {
|
||||||
output[key] = lokaliseTransform(value, original, file);
|
output[key] = lokaliseTransform(value, original, file);
|
||||||
} else {
|
} else {
|
||||||
output[key] = value.replace(re_key_reference, (match, key) => {
|
output[key] = value.replace(re_key_reference, (_match, lokalise_key) => {
|
||||||
const replace = key.split("::").reduce((tr, k) => {
|
const replace = lokalise_key.split("::").reduce((tr, k) => {
|
||||||
if (!tr) {
|
if (!tr) {
|
||||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
throw Error(
|
||||||
|
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return tr[k];
|
return tr[k];
|
||||||
}, original);
|
}, original);
|
||||||
if (typeof replace !== "string") {
|
if (typeof replace !== "string") {
|
||||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
throw Error(
|
||||||
|
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return replace;
|
return replace;
|
||||||
});
|
});
|
||||||
@@ -118,18 +117,16 @@ function lokaliseTransform(data, original, file) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp.task("clean-translations", function () {
|
gulp.task("clean-translations", () => del([workDir]));
|
||||||
return del([workDir]);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("ensure-translations-build-dir", (done) => {
|
gulp.task("ensure-translations-build-dir", (done) => {
|
||||||
if (!fs.existsSync(workDir)) {
|
if (!fs.existsSync(workDir)) {
|
||||||
fs.mkdirSync(workDir);
|
fs.mkdirSync(workDir, { recursive: true });
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("create-test-metadata", function (cb) {
|
gulp.task("create-test-metadata", (cb) => {
|
||||||
fs.writeFile(
|
fs.writeFile(
|
||||||
workDir + "/testMetadata.json",
|
workDir + "/testMetadata.json",
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -143,17 +140,13 @@ gulp.task("create-test-metadata", function (cb) {
|
|||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"create-test-translation",
|
"create-test-translation",
|
||||||
gulp.series("create-test-metadata", function createTestTranslation() {
|
gulp.series("create-test-metadata", () =>
|
||||||
return gulp
|
gulp
|
||||||
.src(path.join(paths.translations_src, "en.json"))
|
.src(path.join(paths.translations_src, "en.json"))
|
||||||
.pipe(
|
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||||
transform(function (data, file) {
|
|
||||||
return recursiveEmpty(data);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(rename("test.json"))
|
.pipe(rename("test.json"))
|
||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(workDir))
|
||||||
})
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,7 +158,7 @@ gulp.task(
|
|||||||
* project is buildable immediately after merging new translation keys, since
|
* project is buildable immediately after merging new translation keys, since
|
||||||
* the Lokalise update to translations/en.json will not happen immediately.
|
* the Lokalise update to translations/en.json will not happen immediately.
|
||||||
*/
|
*/
|
||||||
gulp.task("build-master-translation", function () {
|
gulp.task("build-master-translation", () => {
|
||||||
const src = [path.join(paths.translations_src, "en.json")];
|
const src = [path.join(paths.translations_src, "en.json")];
|
||||||
|
|
||||||
if (mergeBackend) {
|
if (mergeBackend) {
|
||||||
@@ -174,11 +167,7 @@ gulp.task("build-master-translation", function () {
|
|||||||
|
|
||||||
return gulp
|
return gulp
|
||||||
.src(src)
|
.src(src)
|
||||||
.pipe(
|
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||||
transform(function (data, file) {
|
|
||||||
return lokaliseTransform(data, data, file);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
merge({
|
merge({
|
||||||
fileName: "translationMaster.json",
|
fileName: "translationMaster.json",
|
||||||
@@ -187,18 +176,14 @@ gulp.task("build-master-translation", function () {
|
|||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(workDir));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("build-merged-translations", function () {
|
gulp.task("build-merged-translations", () =>
|
||||||
return gulp
|
gulp
|
||||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
||||||
allowEmpty: true,
|
allowEmpty: true,
|
||||||
})
|
})
|
||||||
|
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||||
.pipe(
|
.pipe(
|
||||||
transform(function (data, file) {
|
foreach((stream, file) => {
|
||||||
return lokaliseTransform(data, data, file);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.pipe(
|
|
||||||
foreach(function (stream, file) {
|
|
||||||
// For each language generate a merged json file. It begins with the master
|
// For each language generate a merged json file. It begins with the master
|
||||||
// translation as a failsafe for untranslated strings, and merges all parent
|
// translation as a failsafe for untranslated strings, and merges all parent
|
||||||
// tags into one file for each specific subtag
|
// tags into one file for each specific subtag
|
||||||
@@ -230,17 +215,17 @@ gulp.task("build-merged-translations", function () {
|
|||||||
)
|
)
|
||||||
.pipe(gulp.dest(fullDir));
|
.pipe(gulp.dest(fullDir));
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
});
|
);
|
||||||
|
|
||||||
var taskName;
|
let taskName;
|
||||||
|
|
||||||
const splitTasks = [];
|
const splitTasks = [];
|
||||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||||
taskName = "build-translation-fragment-" + fragment;
|
taskName = "build-translation-fragment-" + fragment;
|
||||||
gulp.task(taskName, function () {
|
gulp.task(taskName, () =>
|
||||||
// Return only the translations for this fragment.
|
// Return only the translations for this fragment.
|
||||||
return gulp
|
gulp
|
||||||
.src(fullDir + "/*.json")
|
.src(fullDir + "/*.json")
|
||||||
.pipe(
|
.pipe(
|
||||||
transform((data) => ({
|
transform((data) => ({
|
||||||
@@ -251,18 +236,18 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(workDir + "/" + fragment));
|
.pipe(gulp.dest(workDir + "/" + fragment))
|
||||||
});
|
);
|
||||||
splitTasks.push(taskName);
|
splitTasks.push(taskName);
|
||||||
});
|
});
|
||||||
|
|
||||||
taskName = "build-translation-core";
|
taskName = "build-translation-core";
|
||||||
gulp.task(taskName, function () {
|
gulp.task(taskName, () =>
|
||||||
// Remove the fragment translations from the core translation.
|
// Remove the fragment translations from the core translation.
|
||||||
return gulp
|
gulp
|
||||||
.src(fullDir + "/*.json")
|
.src(fullDir + "/*.json")
|
||||||
.pipe(
|
.pipe(
|
||||||
transform((data, file) => {
|
transform((data, _file) => {
|
||||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||||
delete data.ui.panel[fragment];
|
delete data.ui.panel[fragment];
|
||||||
});
|
});
|
||||||
@@ -270,14 +255,14 @@ gulp.task(taskName, function () {
|
|||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(coreDir));
|
.pipe(gulp.dest(coreDir))
|
||||||
});
|
);
|
||||||
|
|
||||||
splitTasks.push(taskName);
|
splitTasks.push(taskName);
|
||||||
|
|
||||||
gulp.task("build-flattened-translations", function () {
|
gulp.task("build-flattened-translations", () =>
|
||||||
// Flatten the split versions of our translations, and move them into outDir
|
// Flatten the split versions of our translations, and move them into outDir
|
||||||
return gulp
|
gulp
|
||||||
.src(
|
.src(
|
||||||
TRANSLATION_FRAGMENTS.map(
|
TRANSLATION_FRAGMENTS.map(
|
||||||
(fragment) => workDir + "/" + fragment + "/*.json"
|
(fragment) => workDir + "/" + fragment + "/*.json"
|
||||||
@@ -285,41 +270,45 @@ gulp.task("build-flattened-translations", function () {
|
|||||||
{ base: workDir }
|
{ base: workDir }
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
transform(function (data) {
|
transform((data) =>
|
||||||
// Polymer.AppLocalizeBehavior requires flattened json
|
// Polymer.AppLocalizeBehavior requires flattened json
|
||||||
return flatten(data);
|
flatten(data)
|
||||||
})
|
)
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
rename((filePath) => {
|
rename((filePath) => {
|
||||||
if (filePath.dirname === "core") {
|
if (filePath.dirname === "core") {
|
||||||
filePath.dirname = "";
|
filePath.dirname = "";
|
||||||
}
|
}
|
||||||
|
// In dev we create the file with the fake hash in the filename
|
||||||
|
if (!env.isProdBuild()) {
|
||||||
|
filePath.basename += "-dev";
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(outDir));
|
.pipe(gulp.dest(outDir))
|
||||||
});
|
);
|
||||||
|
|
||||||
const fingerprints = {};
|
const fingerprints = {};
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("build-translation-fingerprints", () => {
|
||||||
"build-translation-fingerprints",
|
// Fingerprint full file of each language
|
||||||
function fingerprintTranslationFiles() {
|
const files = fs.readdirSync(fullDir);
|
||||||
// Fingerprint full file of each language
|
|
||||||
const files = fs.readdirSync(fullDir);
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
fingerprints[files[i].split(".")[0]] = {
|
fingerprints[files[i].split(".")[0]] = {
|
||||||
// In dev we create fake hashes
|
// In dev we create fake hashes
|
||||||
hash: env.isProdBuild()
|
hash: env.isProdBuild()
|
||||||
? crypto
|
? crypto
|
||||||
.createHash("md5")
|
.createHash("md5")
|
||||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||||
.digest("hex")
|
.digest("hex")
|
||||||
: "dev",
|
: "dev",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In dev we create the file with the fake hash in the filename
|
||||||
|
if (env.isProdBuild()) {
|
||||||
mapFiles(outDir, ".json", (filename) => {
|
mapFiles(outDir, ".json", (filename) => {
|
||||||
const parsed = path.parse(filename);
|
const parsed = path.parse(filename);
|
||||||
|
|
||||||
@@ -335,35 +324,43 @@ gulp.task(
|
|||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const stream = source("translationFingerprints.json");
|
|
||||||
stream.write(JSON.stringify(fingerprints));
|
|
||||||
process.nextTick(() => stream.end());
|
|
||||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
gulp.task("build-translation-fragment-supervisor", function () {
|
const stream = source("translationFingerprints.json");
|
||||||
return gulp
|
stream.write(JSON.stringify(fingerprints));
|
||||||
|
process.nextTick(() => stream.end());
|
||||||
|
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("build-translation-fragment-supervisor", () =>
|
||||||
|
gulp
|
||||||
.src(fullDir + "/*.json")
|
.src(fullDir + "/*.json")
|
||||||
.pipe(transform((data) => data.supervisor))
|
.pipe(transform((data) => data.supervisor))
|
||||||
.pipe(gulp.dest(workDir + "/supervisor"));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("build-translation-flatten-supervisor", function () {
|
|
||||||
return gulp
|
|
||||||
.src(workDir + "/supervisor/*.json")
|
|
||||||
.pipe(
|
.pipe(
|
||||||
transform(function (data) {
|
rename((filePath) => {
|
||||||
// Polymer.AppLocalizeBehavior requires flattened json
|
// In dev we create the file with the fake hash in the filename
|
||||||
return flatten(data);
|
if (!env.isProdBuild()) {
|
||||||
|
filePath.basename += "-dev";
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.pipe(gulp.dest(outDir));
|
.pipe(gulp.dest(workDir + "/supervisor"))
|
||||||
});
|
);
|
||||||
|
|
||||||
gulp.task("build-translation-write-metadata", function writeMetadata() {
|
gulp.task("build-translation-flatten-supervisor", () =>
|
||||||
return gulp
|
gulp
|
||||||
|
.src(workDir + "/supervisor/*.json")
|
||||||
|
.pipe(
|
||||||
|
transform((data) =>
|
||||||
|
// Polymer.AppLocalizeBehavior requires flattened json
|
||||||
|
flatten(data)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(outDir))
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task("build-translation-write-metadata", () =>
|
||||||
|
gulp
|
||||||
.src(
|
.src(
|
||||||
[
|
[
|
||||||
path.join(paths.translations_src, "translationMetadata.json"),
|
path.join(paths.translations_src, "translationMetadata.json"),
|
||||||
@@ -374,13 +371,14 @@ gulp.task("build-translation-write-metadata", function writeMetadata() {
|
|||||||
)
|
)
|
||||||
.pipe(merge({}))
|
.pipe(merge({}))
|
||||||
.pipe(
|
.pipe(
|
||||||
transform(function (data) {
|
transform((data) => {
|
||||||
const newData = {};
|
const newData = {};
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
// Filter out translations without native name.
|
// Filter out translations without native name.
|
||||||
if (value.nativeName) {
|
if (value.nativeName) {
|
||||||
newData[key] = value;
|
newData[key] = value;
|
||||||
} else {
|
} else {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
`Skipping language ${key}. Native name was not translated.`
|
`Skipping language ${key}. Native name was not translated.`
|
||||||
);
|
);
|
||||||
@@ -396,19 +394,26 @@ gulp.task("build-translation-write-metadata", function writeMetadata() {
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
.pipe(rename("translationMetadata.json"))
|
.pipe(rename("translationMetadata.json"))
|
||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(workDir))
|
||||||
});
|
);
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
"create-translations",
|
||||||
|
gulp.series(
|
||||||
|
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||||
|
"build-master-translation",
|
||||||
|
"build-merged-translations",
|
||||||
|
gulp.parallel(...splitTasks),
|
||||||
|
"build-flattened-translations"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"build-translations",
|
"build-translations",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
"clean-translations",
|
"clean-translations",
|
||||||
"ensure-translations-build-dir",
|
"ensure-translations-build-dir",
|
||||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
"create-translations",
|
||||||
"build-master-translation",
|
|
||||||
"build-merged-translations",
|
|
||||||
gulp.parallel(...splitTasks),
|
|
||||||
"build-flattened-translations",
|
|
||||||
"build-translation-fingerprints",
|
"build-translation-fingerprints",
|
||||||
"build-translation-write-metadata"
|
"build-translation-write-metadata"
|
||||||
)
|
)
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
// Tasks to run webpack.
|
// Tasks to run webpack.
|
||||||
|
const fs = require("fs");
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
const WebpackDevServer = require("webpack-dev-server");
|
const WebpackDevServer = require("webpack-dev-server");
|
||||||
@@ -18,6 +20,13 @@ const bothBuilds = (createConfigFunc, params) => [
|
|||||||
createConfigFunc({ ...params, latestBuild: false }),
|
createConfigFunc({ ...params, latestBuild: false }),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const isWsl =
|
||||||
|
fs.existsSync("/proc/version") &&
|
||||||
|
fs
|
||||||
|
.readFileSync("/proc/version", "utf-8")
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes("microsoft");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{
|
* @param {{
|
||||||
* compiler: import("webpack").Compiler,
|
* compiler: import("webpack").Compiler,
|
||||||
@@ -26,26 +35,29 @@ const bothBuilds = (createConfigFunc, params) => [
|
|||||||
* listenHost?: string
|
* listenHost?: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
const runDevServer = ({
|
const runDevServer = async ({
|
||||||
compiler,
|
compiler,
|
||||||
contentBase,
|
contentBase,
|
||||||
port,
|
port,
|
||||||
listenHost = "localhost",
|
listenHost = "localhost",
|
||||||
}) =>
|
}) => {
|
||||||
new WebpackDevServer(compiler, {
|
const server = new WebpackDevServer(
|
||||||
open: true,
|
{
|
||||||
watchContentBase: true,
|
open: true,
|
||||||
contentBase,
|
host: listenHost,
|
||||||
}).listen(port, listenHost, function (err) {
|
port,
|
||||||
if (err) {
|
static: {
|
||||||
throw err;
|
directory: contentBase,
|
||||||
}
|
watch: true,
|
||||||
// Server listening
|
},
|
||||||
log(
|
},
|
||||||
"[webpack-dev-server]",
|
compiler
|
||||||
`Project is running at http://localhost:${port}`
|
);
|
||||||
);
|
|
||||||
});
|
await server.start();
|
||||||
|
// Server listening
|
||||||
|
log("[webpack-dev-server]", `Project is running at http://localhost:${port}`);
|
||||||
|
};
|
||||||
|
|
||||||
const doneHandler = (done) => (err, stats) => {
|
const doneHandler = (done) => (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -57,6 +69,7 @@ const doneHandler = (done) => (err, stats) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(stats.toString("minimal"));
|
console.log(stats.toString("minimal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,13 +91,14 @@ const prodBuild = (conf) =>
|
|||||||
|
|
||||||
gulp.task("webpack-watch-app", () => {
|
gulp.task("webpack-watch-app", () => {
|
||||||
// This command will run forever because we don't close compiler
|
// This command will run forever because we don't close compiler
|
||||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
webpack(
|
||||||
{ ignored: /build-translations/ },
|
process.env.ES5
|
||||||
doneHandler()
|
? bothBuilds(createAppConfig, { isProdBuild: false })
|
||||||
);
|
: createAppConfig({ isProdBuild: false, latestBuild: true })
|
||||||
|
).watch({ poll: isWsl }, doneHandler());
|
||||||
gulp.watch(
|
gulp.watch(
|
||||||
path.join(paths.translations_src, "en.json"),
|
path.join(paths.translations_src, "en.json"),
|
||||||
gulp.series("build-translations", "copy-translations-app")
|
gulp.series("create-translations", "copy-translations-app")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,13 +110,13 @@ gulp.task("webpack-prod-app", () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-dev-server-demo", () => {
|
gulp.task("webpack-dev-server-demo", () =>
|
||||||
runDevServer({
|
runDevServer({
|
||||||
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
||||||
contentBase: paths.demo_output_root,
|
contentBase: paths.demo_output_root,
|
||||||
port: 8090,
|
port: 8090,
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
gulp.task("webpack-prod-demo", () =>
|
gulp.task("webpack-prod-demo", () =>
|
||||||
prodBuild(
|
prodBuild(
|
||||||
@@ -112,15 +126,15 @@ gulp.task("webpack-prod-demo", () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-dev-server-cast", () => {
|
gulp.task("webpack-dev-server-cast", () =>
|
||||||
runDevServer({
|
runDevServer({
|
||||||
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
||||||
contentBase: paths.cast_output_root,
|
contentBase: paths.cast_output_root,
|
||||||
port: 8080,
|
port: 8080,
|
||||||
// Accessible from the network, because that's how Cast hits it.
|
// Accessible from the network, because that's how Cast hits it.
|
||||||
listenHost: "0.0.0.0",
|
listenHost: "0.0.0.0",
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
gulp.task("webpack-prod-cast", () =>
|
gulp.task("webpack-prod-cast", () =>
|
||||||
prodBuild(
|
prodBuild(
|
||||||
@@ -137,7 +151,7 @@ gulp.task("webpack-watch-hassio", () => {
|
|||||||
isProdBuild: false,
|
isProdBuild: false,
|
||||||
latestBuild: true,
|
latestBuild: true,
|
||||||
})
|
})
|
||||||
).watch({ ignored: /build-translations/ }, doneHandler());
|
).watch({ ignored: /build/, poll: isWsl }, doneHandler());
|
||||||
|
|
||||||
gulp.watch(
|
gulp.watch(
|
||||||
path.join(paths.translations_src, "en.json"),
|
path.join(paths.translations_src, "en.json"),
|
||||||
@@ -153,14 +167,15 @@ gulp.task("webpack-prod-hassio", () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("webpack-dev-server-gallery", () => {
|
gulp.task("webpack-dev-server-gallery", () =>
|
||||||
runDevServer({
|
runDevServer({
|
||||||
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
||||||
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
||||||
contentBase: paths.gallery_output_root,
|
contentBase: paths.gallery_output_root,
|
||||||
port: 8100,
|
port: 8100,
|
||||||
});
|
listenHost: "0.0.0.0",
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task("webpack-prod-gallery", () =>
|
gulp.task("webpack-prod-gallery", () =>
|
||||||
prodBuild(
|
prodBuild(
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
|||||||
const paths = require("./paths.js");
|
const paths = require("./paths.js");
|
||||||
const bundle = require("./bundle.js");
|
const bundle = require("./bundle.js");
|
||||||
const log = require("fancy-log");
|
const log = require("fancy-log");
|
||||||
|
const WebpackBar = require("webpackbar");
|
||||||
|
|
||||||
class LogStartCompilePlugin {
|
class LogStartCompilePlugin {
|
||||||
ignoredFirst = false;
|
ignoredFirst = false;
|
||||||
@@ -49,12 +50,16 @@ const createWebpackConfig = ({
|
|||||||
test: /\.m?js$|\.ts$/,
|
test: /\.m?js$|\.ts$/,
|
||||||
use: {
|
use: {
|
||||||
loader: "babel-loader",
|
loader: "babel-loader",
|
||||||
options: bundle.babelOptions({ latestBuild }),
|
options: {
|
||||||
|
...bundle.babelOptions({ latestBuild }),
|
||||||
|
cacheDirectory: !isProdBuild,
|
||||||
|
cacheCompression: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
use: "raw-loader",
|
type: "asset/source",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -66,8 +71,11 @@ const createWebpackConfig = ({
|
|||||||
terserOptions: bundle.terserOptions(latestBuild),
|
terserOptions: bundle.terserOptions(latestBuild),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||||
|
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
new WebpackBar({ fancy: !isProdBuild }),
|
||||||
new WebpackManifestPlugin({
|
new WebpackManifestPlugin({
|
||||||
// Only include the JS of entrypoints
|
// Only include the JS of entrypoints
|
||||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||||
@@ -112,16 +120,6 @@ const createWebpackConfig = ({
|
|||||||
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
||||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||||
),
|
),
|
||||||
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
|
|
||||||
new webpack.NormalModuleReplacementPlugin(
|
|
||||||
new RegExp(
|
|
||||||
path.resolve(
|
|
||||||
paths.polymer_dir,
|
|
||||||
"src/resources/lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
|
||||||
),
|
|
||||||
!isProdBuild && new LogStartCompilePlugin(),
|
!isProdBuild && new LogStartCompilePlugin(),
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -129,25 +127,33 @@ const createWebpackConfig = ({
|
|||||||
alias: {
|
alias: {
|
||||||
"lit/decorators$": "lit/decorators.js",
|
"lit/decorators$": "lit/decorators.js",
|
||||||
"lit/directive$": "lit/directive.js",
|
"lit/directive$": "lit/directive.js",
|
||||||
|
"lit/directives/until$": "lit/directives/until.js",
|
||||||
|
"lit/directives/class-map$": "lit/directives/class-map.js",
|
||||||
|
"lit/directives/style-map$": "lit/directives/style-map.js",
|
||||||
|
"lit/directives/if-defined$": "lit/directives/if-defined.js",
|
||||||
|
"lit/directives/guard$": "lit/directives/guard.js",
|
||||||
|
"lit/directives/cache$": "lit/directives/cache.js",
|
||||||
|
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: ({ chunk }) => {
|
filename: ({ chunk }) => {
|
||||||
if (!isProdBuild || dontHash.has(chunk.name)) {
|
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) {
|
||||||
return `${chunk.name}.js`;
|
return `${chunk.name}.js`;
|
||||||
}
|
}
|
||||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||||
},
|
},
|
||||||
chunkFilename:
|
chunkFilename:
|
||||||
isProdBuild && !isStatsBuild
|
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js",
|
||||||
? "chunk.[chunkhash].js"
|
|
||||||
: "[name].chunk.js",
|
|
||||||
path: outputPath,
|
path: outputPath,
|
||||||
publicPath,
|
publicPath,
|
||||||
// To silence warning in worker plugin
|
// To silence warning in worker plugin
|
||||||
globalObject: "self",
|
globalObject: "self",
|
||||||
},
|
},
|
||||||
|
experiments: {
|
||||||
|
topLevelAwait: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -139,7 +139,7 @@
|
|||||||
Your authentication credentials or Home Assistant url are never sent
|
Your authentication credentials or Home Assistant url are never sent
|
||||||
to the Cloud. You can validate this behavior in
|
to the Cloud. You can validate this behavior in
|
||||||
<a
|
<a
|
||||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
href="https://github.com/home-assistant/frontend/tree/dev/cast"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>the source code</a
|
>the source code</a
|
||||||
>.
|
>.
|
||||||
|
@@ -191,7 +191,7 @@ class HcCast extends LitElement {
|
|||||||
}
|
}
|
||||||
this.connection.close();
|
this.connection.close();
|
||||||
location.reload();
|
location.reload();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
alert("Unable to log out!");
|
alert("Unable to log out!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -212,7 +212,7 @@ export class HcConnect extends LitElement {
|
|||||||
let url: URL;
|
let url: URL;
|
||||||
try {
|
try {
|
||||||
url = new URL(value);
|
url = new URL(value);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this.error = "Invalid URL";
|
this.error = "Invalid URL";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ export class HcConnect extends LitElement {
|
|||||||
try {
|
try {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
auth = await getAuth(options);
|
auth = await getAuth(options);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
|
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
|
||||||
this.cannotConnect = true;
|
this.cannotConnect = true;
|
||||||
return;
|
return;
|
||||||
@@ -259,7 +259,7 @@ export class HcConnect extends LitElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
conn = await createConnection({ auth });
|
conn = await createConnection({ auth });
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
// In case of saved tokens, silently solve problems.
|
// In case of saved tokens, silently solve problems.
|
||||||
if (init === "saved-tokens") {
|
if (init === "saved-tokens") {
|
||||||
if (err === ERR_CANNOT_CONNECT) {
|
if (err === ERR_CANNOT_CONNECT) {
|
||||||
@@ -285,7 +285,7 @@ export class HcConnect extends LitElement {
|
|||||||
try {
|
try {
|
||||||
saveTokens(null);
|
saveTokens(null);
|
||||||
location.reload();
|
location.reload();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
alert("Unable to log out!");
|
alert("Unable to log out!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,8 @@ import {
|
|||||||
import { castContext } from "../cast_context";
|
import { castContext } from "../cast_context";
|
||||||
|
|
||||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||||
const touchSupported = castContext.getDeviceCapabilities()
|
const touchSupported =
|
||||||
.touch_input_supported;
|
castContext.getDeviceCapabilities().touch_input_supported;
|
||||||
return {
|
return {
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
|
@@ -148,14 +148,14 @@ export class HcMain extends HassElement {
|
|||||||
expires_in: 0,
|
expires_in: 0,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this._getErrorMessage(err);
|
this._error = this._getErrorMessage(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let connection;
|
let connection;
|
||||||
try {
|
try {
|
||||||
connection = await createConnection({ auth });
|
connection = await createConnection({ auth });
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this._getErrorMessage(err);
|
this._error = this._getErrorMessage(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ export class HcMain extends HassElement {
|
|||||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
console.log("Error fetching Lovelace configuration", err, msg);
|
console.log("Error fetching Lovelace configuration", err, msg);
|
||||||
// Generate a Lovelace config.
|
// Generate a Lovelace config.
|
||||||
|
@@ -29,6 +29,11 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Energy distribution today",
|
||||||
|
type: "energy-distribution",
|
||||||
|
link_dashboard: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "thermostat",
|
type: "thermostat",
|
||||||
entity: "climate.upstairs",
|
entity: "climate.upstairs",
|
||||||
@@ -113,8 +118,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||||
},
|
},
|
||||||
state_filter: {
|
state_filter: {
|
||||||
on:
|
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
|
||||||
off: "brightness(80%) saturate(0.8)",
|
off: "brightness(80%) saturate(0.8)",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
@@ -196,8 +200,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||||
},
|
},
|
||||||
state_filter: {
|
state_filter: {
|
||||||
on:
|
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
|
||||||
off: "brightness(80%) saturate(0.8)",
|
off: "brightness(80%) saturate(0.8)",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
@@ -277,8 +280,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||||
},
|
},
|
||||||
state_filter: {
|
state_filter: {
|
||||||
on:
|
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
|
||||||
off: "brightness(80%) saturate(0.8)",
|
off: "brightness(80%) saturate(0.8)",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
@@ -315,8 +317,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||||
},
|
},
|
||||||
state_filter: {
|
state_filter: {
|
||||||
on:
|
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
|
||||||
off: "brightness(80%) saturate(0.8)",
|
off: "brightness(80%) saturate(0.8)",
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
import { Lovelace } from "../../../src/panels/lovelace/types";
|
import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||||
|
import { energyEntities } from "../stubs/entities";
|
||||||
import { DemoConfig } from "./types";
|
import { DemoConfig } from "./types";
|
||||||
|
|
||||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||||
@@ -12,9 +13,8 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
|||||||
// eslint-disable-next-line import/no-mutable-exports
|
// eslint-disable-next-line import/no-mutable-exports
|
||||||
export let selectedDemoConfigIndex = 0;
|
export let selectedDemoConfigIndex = 0;
|
||||||
// eslint-disable-next-line import/no-mutable-exports
|
// eslint-disable-next-line import/no-mutable-exports
|
||||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||||
selectedDemoConfigIndex
|
demoConfigs[selectedDemoConfigIndex]();
|
||||||
]();
|
|
||||||
|
|
||||||
export const setDemoConfig = async (
|
export const setDemoConfig = async (
|
||||||
hass: MockHomeAssistant,
|
hass: MockHomeAssistant,
|
||||||
@@ -28,6 +28,7 @@ export const setDemoConfig = async (
|
|||||||
selectedDemoConfig = confProm;
|
selectedDemoConfig = confProm;
|
||||||
|
|
||||||
hass.addEntities(config.entities(hass.localize), true);
|
hass.addEntities(config.entities(hass.localize), true);
|
||||||
|
hass.addEntities(energyEntities());
|
||||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||||
hass.mockTheme(config.theme());
|
hass.mockTheme(config.theme());
|
||||||
};
|
};
|
||||||
|
@@ -980,8 +980,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
|||||||
icon: "mdi:account-off",
|
icon: "mdi:account-off",
|
||||||
custom_ui_state_card: "state-card-custom-ui",
|
custom_ui_state_card: "state-card-custom-ui",
|
||||||
templates: {
|
templates: {
|
||||||
icon:
|
icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||||
"if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
|
||||||
icon_color:
|
icon_color:
|
||||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||||
},
|
},
|
||||||
@@ -1005,8 +1004,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
|||||||
icon: "mdi:account-multiple-minus",
|
icon: "mdi:account-multiple-minus",
|
||||||
custom_ui_state_card: "state-card-custom-ui",
|
custom_ui_state_card: "state-card-custom-ui",
|
||||||
templates: {
|
templates: {
|
||||||
icon:
|
icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||||
"if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
|
||||||
icon_color:
|
icon_color:
|
||||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||||
},
|
},
|
||||||
|
@@ -19,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
@state() private _switching?: boolean;
|
@state() private _switching = false;
|
||||||
|
|
||||||
private _hidden = localStorage.hide_demo_card;
|
private _hidden = localStorage.hide_demo_card;
|
||||||
|
|
||||||
@@ -27,12 +27,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
|||||||
return this._hidden ? 0 : 2;
|
return this._hidden ? 0 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(
|
public setConfig(_config: LovelaceCardConfig) {}
|
||||||
// @ts-ignore
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
config: LovelaceCardConfig
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
) {}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (this._hidden) {
|
if (this._hidden) {
|
||||||
@@ -49,7 +44,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
|||||||
(conf) => html`
|
(conf) => html`
|
||||||
${conf.name}
|
${conf.name}
|
||||||
<small>
|
<small>
|
||||||
<a target="_blank" href="${conf.authorUrl}">
|
<a target="_blank" href=${conf.authorUrl}>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.page-demo.cards.demo.demo_by",
|
"ui.panel.page-demo.cards.demo.demo_by",
|
||||||
"name",
|
"name",
|
||||||
@@ -99,7 +94,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
|||||||
this._switching = true;
|
this._switching = true;
|
||||||
try {
|
try {
|
||||||
await setDemoConfig(this.hass, this.lovelace!, index);
|
await setDemoConfig(this.hass, this.lovelace!, index);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
alert("Failed to switch config :-(");
|
alert("Failed to switch config :-(");
|
||||||
} finally {
|
} finally {
|
||||||
this._switching = false;
|
this._switching = false;
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import "@polymer/polymer/lib/elements/dom-if";
|
|
||||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
|
||||||
import "../../src/resources/ha-style";
|
import "../../src/resources/ha-style";
|
||||||
import "../../src/resources/roboto";
|
import "../../src/resources/roboto";
|
||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
import "../../src/resources/safari-14-attachshadow-patch";
|
||||||
|
@@ -20,6 +20,9 @@ import { mockShoppingList } from "./stubs/shopping_list";
|
|||||||
import { mockSystemLog } from "./stubs/system_log";
|
import { mockSystemLog } from "./stubs/system_log";
|
||||||
import { mockTemplate } from "./stubs/template";
|
import { mockTemplate } from "./stubs/template";
|
||||||
import { mockTranslations } from "./stubs/translations";
|
import { mockTranslations } from "./stubs/translations";
|
||||||
|
import { mockEnergy } from "./stubs/energy";
|
||||||
|
import { mockConfig } from "./stubs/config";
|
||||||
|
import { energyEntities } from "./stubs/entities";
|
||||||
|
|
||||||
class HaDemo extends HomeAssistantAppEl {
|
class HaDemo extends HomeAssistantAppEl {
|
||||||
protected async _initializeHass() {
|
protected async _initializeHass() {
|
||||||
@@ -47,8 +50,12 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
mockEvents(hass);
|
mockEvents(hass);
|
||||||
mockMediaPlayer(hass);
|
mockMediaPlayer(hass);
|
||||||
mockFrontend(hass);
|
mockFrontend(hass);
|
||||||
|
mockEnergy(hass);
|
||||||
|
mockConfig(hass);
|
||||||
mockPersistentNotification(hass);
|
mockPersistentNotification(hass);
|
||||||
|
|
||||||
|
hass.addEntities(energyEntities());
|
||||||
|
|
||||||
// Once config is loaded AND localize, set entities and apply theme.
|
// Once config is loaded AND localize, set entities and apply theme.
|
||||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||||
([conf, localize]) => {
|
([conf, localize]) => {
|
||||||
|
7
demo/src/stubs/area_registry.ts
Normal file
7
demo/src/stubs/area_registry.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { AreaRegistryEntry } from "../../../src/data/area_registry";
|
||||||
|
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockAreaRegistry = (
|
||||||
|
hass: MockHomeAssistant,
|
||||||
|
data: AreaRegistryEntry[] = []
|
||||||
|
) => hass.mockWS("config/area_registry/list", () => data);
|
41
demo/src/stubs/config.ts
Normal file
41
demo/src/stubs/config.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockConfig = (hass: MockHomeAssistant) => {
|
||||||
|
hass.mockAPI("config/config_entries/entry", () => [
|
||||||
|
{
|
||||||
|
entry_id: "co2signal",
|
||||||
|
domain: "co2signal",
|
||||||
|
title: "CO2 Signal",
|
||||||
|
source: "user",
|
||||||
|
state: "loaded",
|
||||||
|
supports_options: false,
|
||||||
|
supports_unload: true,
|
||||||
|
pref_disable_new_entities: false,
|
||||||
|
pref_disable_polling: false,
|
||||||
|
disabled_by: null,
|
||||||
|
reason: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
hass.mockWS("config/entity_registry/list", () => [
|
||||||
|
{
|
||||||
|
config_entry_id: "co2signal",
|
||||||
|
device_id: "co2signal",
|
||||||
|
area_id: null,
|
||||||
|
disabled_by: null,
|
||||||
|
entity_id: "sensor.co2_intensity",
|
||||||
|
name: null,
|
||||||
|
icon: null,
|
||||||
|
platform: "co2signal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config_entry_id: "co2signal",
|
||||||
|
device_id: "co2signal",
|
||||||
|
area_id: null,
|
||||||
|
disabled_by: null,
|
||||||
|
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||||
|
name: null,
|
||||||
|
icon: null,
|
||||||
|
platform: "co2signal",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
7
demo/src/stubs/device_registry.ts
Normal file
7
demo/src/stubs/device_registry.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||||
|
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockDeviceRegistry = (
|
||||||
|
hass: MockHomeAssistant,
|
||||||
|
data: DeviceRegistryEntry[] = []
|
||||||
|
) => hass.mockWS("config/device_registry/list", () => data);
|
134
demo/src/stubs/energy.ts
Normal file
134
demo/src/stubs/energy.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||||
|
import { EnergySolarForecasts } from "../../../src/data/energy";
|
||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||||
|
hass.mockWS("energy/get_prefs", () => ({
|
||||||
|
energy_sources: [
|
||||||
|
{
|
||||||
|
type: "grid",
|
||||||
|
flow_from: [
|
||||||
|
{
|
||||||
|
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||||
|
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||||
|
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||||
|
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||||
|
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
flow_to: [
|
||||||
|
{
|
||||||
|
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||||
|
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
||||||
|
entity_energy_to: "sensor.energy_production_tarif_1",
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||||
|
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
||||||
|
entity_energy_to: "sensor.energy_production_tarif_2",
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cost_adjustment_day: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "solar",
|
||||||
|
stat_energy_from: "sensor.solar_production",
|
||||||
|
config_entry_solar_forecast: ["solar_forecast"],
|
||||||
|
},
|
||||||
|
/* {
|
||||||
|
type: "battery",
|
||||||
|
stat_energy_from: "sensor.battery_output",
|
||||||
|
stat_energy_to: "sensor.battery_input",
|
||||||
|
}, */
|
||||||
|
{
|
||||||
|
type: "gas",
|
||||||
|
stat_energy_from: "sensor.energy_gas",
|
||||||
|
stat_cost: "sensor.energy_gas_cost",
|
||||||
|
entity_energy_from: "sensor.energy_gas",
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
device_consumption: [
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.energy_car",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.energy_ac",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.energy_washing_machine",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.energy_dryer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.energy_heat_pump",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.energy_boiler",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
||||||
|
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||||
|
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||||
|
hass.mockWS(
|
||||||
|
"energy/solar_forecast",
|
||||||
|
(): EnergySolarForecasts => ({
|
||||||
|
solar_forecast: {
|
||||||
|
wh_hours: {
|
||||||
|
[`${todayString}T06:00:00`]: 0,
|
||||||
|
[`${todayString}T06:23:00`]: 6,
|
||||||
|
[`${todayString}T06:45:00`]: 39,
|
||||||
|
[`${todayString}T07:00:00`]: 28,
|
||||||
|
[`${todayString}T08:00:00`]: 208,
|
||||||
|
[`${todayString}T09:00:00`]: 352,
|
||||||
|
[`${todayString}T10:00:00`]: 544,
|
||||||
|
[`${todayString}T11:00:00`]: 748,
|
||||||
|
[`${todayString}T12:00:00`]: 1259,
|
||||||
|
[`${todayString}T13:00:00`]: 1361,
|
||||||
|
[`${todayString}T14:00:00`]: 1373,
|
||||||
|
[`${todayString}T15:00:00`]: 1370,
|
||||||
|
[`${todayString}T16:00:00`]: 1186,
|
||||||
|
[`${todayString}T17:00:00`]: 937,
|
||||||
|
[`${todayString}T18:00:00`]: 652,
|
||||||
|
[`${todayString}T19:00:00`]: 370,
|
||||||
|
[`${todayString}T20:00:00`]: 155,
|
||||||
|
[`${todayString}T21:48:00`]: 24,
|
||||||
|
[`${todayString}T22:36:00`]: 0,
|
||||||
|
[`${tomorrowString}T06:01:00`]: 0,
|
||||||
|
[`${tomorrowString}T06:23:00`]: 9,
|
||||||
|
[`${tomorrowString}T06:45:00`]: 47,
|
||||||
|
[`${tomorrowString}T07:00:00`]: 48,
|
||||||
|
[`${tomorrowString}T08:00:00`]: 473,
|
||||||
|
[`${tomorrowString}T09:00:00`]: 827,
|
||||||
|
[`${tomorrowString}T10:00:00`]: 1153,
|
||||||
|
[`${tomorrowString}T11:00:00`]: 1413,
|
||||||
|
[`${tomorrowString}T12:00:00`]: 1590,
|
||||||
|
[`${tomorrowString}T13:00:00`]: 1652,
|
||||||
|
[`${tomorrowString}T14:00:00`]: 1612,
|
||||||
|
[`${tomorrowString}T15:00:00`]: 1438,
|
||||||
|
[`${tomorrowString}T16:00:00`]: 1149,
|
||||||
|
[`${tomorrowString}T17:00:00`]: 830,
|
||||||
|
[`${tomorrowString}T18:00:00`]: 542,
|
||||||
|
[`${tomorrowString}T19:00:00`]: 311,
|
||||||
|
[`${tomorrowString}T20:00:00`]: 140,
|
||||||
|
[`${tomorrowString}T21:47:00`]: 22,
|
||||||
|
[`${tomorrowString}T22:34:00`]: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
178
demo/src/stubs/entities.ts
Normal file
178
demo/src/stubs/entities.ts
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import { convertEntities } from "../../../src/fake_data/entity";
|
||||||
|
|
||||||
|
export const energyEntities = () =>
|
||||||
|
convertEntities({
|
||||||
|
"sensor.grid_fossil_fuel_percentage": {
|
||||||
|
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||||
|
state: "88.6",
|
||||||
|
attributes: {
|
||||||
|
unit_of_measurement: "%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.solar_production": {
|
||||||
|
entity_id: "sensor.solar_production",
|
||||||
|
state: "88.6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Solar",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.battery_input": {
|
||||||
|
entity_id: "sensor.battery_input",
|
||||||
|
state: "4",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Battery Input",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.battery_output": {
|
||||||
|
entity_id: "sensor.battery_output",
|
||||||
|
state: "3",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Battery Output",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_consumption_tarif_1": {
|
||||||
|
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||||
|
state: "88.6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Grid consumption low tariff",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_consumption_tarif_2": {
|
||||||
|
entity_id: "sensor.energy_consumption_tarif_2",
|
||||||
|
state: "88.6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Grid consumption high tariff",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_production_tarif_1": {
|
||||||
|
entity_id: "sensor.energy_production_tarif_1",
|
||||||
|
state: "88.6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Returned to grid low tariff",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_production_tarif_2": {
|
||||||
|
entity_id: "sensor.energy_production_tarif_2",
|
||||||
|
state: "88.6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Returned to grid high tariff",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_consumption_tarif_1_cost": {
|
||||||
|
entity_id: "sensor.energy_consumption_tarif_1_cost",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
unit_of_measurement: "EUR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_consumption_tarif_2_cost": {
|
||||||
|
entity_id: "sensor.energy_consumption_tarif_2_cost",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
unit_of_measurement: "EUR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_production_tarif_1_compensation": {
|
||||||
|
entity_id: "sensor.energy_production_tarif_1_compensation",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
unit_of_measurement: "EUR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_production_tarif_2_compensation": {
|
||||||
|
entity_id: "sensor.energy_production_tarif_2_compensation",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
unit_of_measurement: "EUR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_gas_cost": {
|
||||||
|
entity_id: "sensor.energy_gas_cost",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
unit_of_measurement: "EUR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_gas": {
|
||||||
|
entity_id: "sensor.energy_gas",
|
||||||
|
state: "4",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Gas",
|
||||||
|
unit_of_measurement: "m³",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_car": {
|
||||||
|
entity_id: "sensor.energy_car",
|
||||||
|
state: "4",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Electric car",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_ac": {
|
||||||
|
entity_id: "sensor.energy_ac",
|
||||||
|
state: "3",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Air conditioning",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_washing_machine": {
|
||||||
|
entity_id: "sensor.energy_washing_machine",
|
||||||
|
state: "6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Washing machine",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_dryer": {
|
||||||
|
entity_id: "sensor.energy_dryer",
|
||||||
|
state: "5.5",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Dryer",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_heat_pump": {
|
||||||
|
entity_id: "sensor.energy_heat_pump",
|
||||||
|
state: "6",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Heat pump",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.energy_boiler": {
|
||||||
|
entity_id: "sensor.energy_boiler",
|
||||||
|
state: "7",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Boiler",
|
||||||
|
unit_of_measurement: "kWh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
7
demo/src/stubs/entity_registry.ts
Normal file
7
demo/src/stubs/entity_registry.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||||
|
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockEntityRegistry = (
|
||||||
|
hass: MockHomeAssistant,
|
||||||
|
data: EntityRegistryEntry[] = []
|
||||||
|
) => hass.mockWS("config/entity_registry/list", () => data);
|
59
demo/src/stubs/hassio_supervisor.ts
Normal file
59
demo/src/stubs/hassio_supervisor.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
|
||||||
|
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockHassioSupervisor = (hass: MockHomeAssistant) => {
|
||||||
|
hass.config.components.push("hassio");
|
||||||
|
hass.mockWS("supervisor/api", (msg) => {
|
||||||
|
if (msg.endpoint === "/supervisor/info") {
|
||||||
|
const data: HassioSupervisorInfo = {
|
||||||
|
version: "2021.10.dev0805",
|
||||||
|
version_latest: "2021.10.dev0806",
|
||||||
|
update_available: true,
|
||||||
|
channel: "dev",
|
||||||
|
arch: "aarch64",
|
||||||
|
supported: true,
|
||||||
|
healthy: true,
|
||||||
|
ip_address: "172.30.32.2",
|
||||||
|
wait_boot: 5,
|
||||||
|
timezone: "America/Los_Angeles",
|
||||||
|
logging: "info",
|
||||||
|
debug: false,
|
||||||
|
debug_block: false,
|
||||||
|
diagnostics: true,
|
||||||
|
addons: [
|
||||||
|
{
|
||||||
|
name: "Visual Studio Code",
|
||||||
|
slug: "a0d7b954_vscode",
|
||||||
|
description:
|
||||||
|
"Fully featured VSCode experience, to edit your HA config in the browser, including auto-completion!",
|
||||||
|
state: "started",
|
||||||
|
version: "3.6.2",
|
||||||
|
version_latest: "3.6.2",
|
||||||
|
update_available: false,
|
||||||
|
repository: "a0d7b954",
|
||||||
|
icon: true,
|
||||||
|
logo: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Z-Wave JS",
|
||||||
|
slug: "core_zwave_js",
|
||||||
|
description:
|
||||||
|
"Control a ZWave network with Home Assistant Z-Wave JS",
|
||||||
|
state: "started",
|
||||||
|
version: "0.1.45",
|
||||||
|
version_latest: "0.1.45",
|
||||||
|
update_available: false,
|
||||||
|
repository: "core",
|
||||||
|
icon: true,
|
||||||
|
logo: true,
|
||||||
|
},
|
||||||
|
] as any,
|
||||||
|
addons_repositories: [
|
||||||
|
"https://github.com/hassio-addons/repository",
|
||||||
|
] as any,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return Promise.reject(`${msg.method} ${msg.endpoint} is not implemented`);
|
||||||
|
});
|
||||||
|
};
|
@@ -1,4 +1,6 @@
|
|||||||
|
import { addHours, differenceInHours, endOfDay } from "date-fns";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { StatisticValue } from "../../../src/data/history";
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
interface HistoryQueryParams {
|
interface HistoryQueryParams {
|
||||||
@@ -64,17 +66,219 @@ const generateHistory = (state, deltas) => {
|
|||||||
|
|
||||||
const incrementalUnits = ["clients", "queries", "ads"];
|
const incrementalUnits = ["clients", "queries", "ads"];
|
||||||
|
|
||||||
|
const generateMeanStatistics = (
|
||||||
|
id: string,
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
initValue: number,
|
||||||
|
maxDiff: number
|
||||||
|
) => {
|
||||||
|
const statistics: StatisticValue[] = [];
|
||||||
|
let currentDate = new Date(start);
|
||||||
|
currentDate.setMinutes(0, 0, 0);
|
||||||
|
let lastVal = initValue;
|
||||||
|
const now = new Date();
|
||||||
|
while (end > currentDate && currentDate < now) {
|
||||||
|
const delta = Math.random() * maxDiff;
|
||||||
|
const mean = lastVal + delta;
|
||||||
|
statistics.push({
|
||||||
|
statistic_id: id,
|
||||||
|
start: currentDate.toISOString(),
|
||||||
|
mean,
|
||||||
|
min: mean - Math.random() * maxDiff,
|
||||||
|
max: mean + Math.random() * maxDiff,
|
||||||
|
last_reset: "1970-01-01T00:00:00+00:00",
|
||||||
|
state: mean,
|
||||||
|
sum: null,
|
||||||
|
});
|
||||||
|
lastVal = mean;
|
||||||
|
currentDate = addHours(currentDate, 1);
|
||||||
|
}
|
||||||
|
return statistics;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateSumStatistics = (
|
||||||
|
id: string,
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
initValue: number,
|
||||||
|
maxDiff: number
|
||||||
|
) => {
|
||||||
|
const statistics: StatisticValue[] = [];
|
||||||
|
let currentDate = new Date(start);
|
||||||
|
currentDate.setMinutes(0, 0, 0);
|
||||||
|
let sum = initValue;
|
||||||
|
const now = new Date();
|
||||||
|
while (end > currentDate && currentDate < now) {
|
||||||
|
const add = Math.random() * maxDiff;
|
||||||
|
sum += add;
|
||||||
|
statistics.push({
|
||||||
|
statistic_id: id,
|
||||||
|
start: currentDate.toISOString(),
|
||||||
|
mean: null,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
last_reset: "1970-01-01T00:00:00+00:00",
|
||||||
|
state: initValue + sum,
|
||||||
|
sum,
|
||||||
|
});
|
||||||
|
currentDate = addHours(currentDate, 1);
|
||||||
|
}
|
||||||
|
return statistics;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateCurvedStatistics = (
|
||||||
|
id: string,
|
||||||
|
start: Date,
|
||||||
|
end: Date,
|
||||||
|
initValue: number,
|
||||||
|
maxDiff: number,
|
||||||
|
metered: boolean
|
||||||
|
) => {
|
||||||
|
const statistics: StatisticValue[] = [];
|
||||||
|
let currentDate = new Date(start);
|
||||||
|
currentDate.setMinutes(0, 0, 0);
|
||||||
|
let sum = initValue;
|
||||||
|
const hours = differenceInHours(end, start) - 1;
|
||||||
|
let i = 0;
|
||||||
|
let half = false;
|
||||||
|
const now = new Date();
|
||||||
|
while (end > currentDate && currentDate < now) {
|
||||||
|
const add = Math.random() * maxDiff;
|
||||||
|
sum += i * add;
|
||||||
|
statistics.push({
|
||||||
|
statistic_id: id,
|
||||||
|
start: currentDate.toISOString(),
|
||||||
|
mean: null,
|
||||||
|
min: null,
|
||||||
|
max: null,
|
||||||
|
last_reset: "1970-01-01T00:00:00+00:00",
|
||||||
|
state: initValue + sum,
|
||||||
|
sum: metered ? sum : null,
|
||||||
|
});
|
||||||
|
currentDate = addHours(currentDate, 1);
|
||||||
|
if (!half && i > hours / 2) {
|
||||||
|
half = true;
|
||||||
|
}
|
||||||
|
i += half ? -1 : 1;
|
||||||
|
}
|
||||||
|
return statistics;
|
||||||
|
};
|
||||||
|
|
||||||
|
const statisticsFunctions: Record<
|
||||||
|
string,
|
||||||
|
(id: string, start: Date, end: Date) => StatisticValue[]
|
||||||
|
> = {
|
||||||
|
"sensor.energy_consumption_tarif_1": (id: string, start: Date, end: Date) => {
|
||||||
|
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
|
||||||
|
const morningLow = generateSumStatistics(id, start, morningEnd, 0, 0.7);
|
||||||
|
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||||
|
const morningFinalVal = morningLow.length
|
||||||
|
? morningLow[morningLow.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const empty = generateSumStatistics(
|
||||||
|
id,
|
||||||
|
morningEnd,
|
||||||
|
eveningStart,
|
||||||
|
morningFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const eveningLow = generateSumStatistics(
|
||||||
|
id,
|
||||||
|
eveningStart,
|
||||||
|
end,
|
||||||
|
morningFinalVal,
|
||||||
|
0.7
|
||||||
|
);
|
||||||
|
return [...morningLow, ...empty, ...eveningLow];
|
||||||
|
},
|
||||||
|
"sensor.energy_consumption_tarif_2": (id: string, start: Date, end: Date) => {
|
||||||
|
const morningEnd = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||||
|
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||||
|
const highTarif = generateSumStatistics(
|
||||||
|
id,
|
||||||
|
morningEnd,
|
||||||
|
eveningStart,
|
||||||
|
0,
|
||||||
|
0.3
|
||||||
|
);
|
||||||
|
const highTarifFinalVal = highTarif.length
|
||||||
|
? highTarif[highTarif.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const morning = generateSumStatistics(id, start, morningEnd, 0, 0);
|
||||||
|
const evening = generateSumStatistics(
|
||||||
|
id,
|
||||||
|
eveningStart,
|
||||||
|
end,
|
||||||
|
highTarifFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return [...morning, ...highTarif, ...evening];
|
||||||
|
},
|
||||||
|
"sensor.energy_production_tarif_1": (id, start, end) =>
|
||||||
|
generateSumStatistics(id, start, end, 0, 0),
|
||||||
|
"sensor.energy_production_tarif_1_compensation": (id, start, end) =>
|
||||||
|
generateSumStatistics(id, start, end, 0, 0),
|
||||||
|
"sensor.energy_production_tarif_2": (id, start, end) => {
|
||||||
|
const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||||
|
const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000);
|
||||||
|
const dayEnd = new Date(endOfDay(productionEnd));
|
||||||
|
const production = generateCurvedStatistics(
|
||||||
|
id,
|
||||||
|
productionStart,
|
||||||
|
productionEnd,
|
||||||
|
0,
|
||||||
|
0.15,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const productionFinalVal = production.length
|
||||||
|
? production[production.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||||
|
const evening = generateSumStatistics(
|
||||||
|
id,
|
||||||
|
productionEnd,
|
||||||
|
dayEnd,
|
||||||
|
productionFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 1);
|
||||||
|
return [...morning, ...production, ...evening, ...rest];
|
||||||
|
},
|
||||||
|
"sensor.solar_production": (id, start, end) => {
|
||||||
|
const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000);
|
||||||
|
const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000);
|
||||||
|
const dayEnd = new Date(endOfDay(productionEnd));
|
||||||
|
const production = generateCurvedStatistics(
|
||||||
|
id,
|
||||||
|
productionStart,
|
||||||
|
productionEnd,
|
||||||
|
0,
|
||||||
|
0.3,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const productionFinalVal = production.length
|
||||||
|
? production[production.length - 1].sum!
|
||||||
|
: 0;
|
||||||
|
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||||
|
const evening = generateSumStatistics(
|
||||||
|
id,
|
||||||
|
productionEnd,
|
||||||
|
dayEnd,
|
||||||
|
productionFinalVal,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 2);
|
||||||
|
return [...morning, ...production, ...evening, ...rest];
|
||||||
|
},
|
||||||
|
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
|
||||||
|
generateMeanStatistics(id, start, end, 35, 1.3),
|
||||||
|
};
|
||||||
|
|
||||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||||
mockHass.mockAPI(
|
mockHass.mockAPI(
|
||||||
new RegExp("history/period/.+"),
|
new RegExp("history/period/.+"),
|
||||||
(
|
(hass, _method, path, _parameters) => {
|
||||||
hass,
|
|
||||||
// @ts-ignore
|
|
||||||
method,
|
|
||||||
path,
|
|
||||||
// @ts-ignore
|
|
||||||
parameters
|
|
||||||
) => {
|
|
||||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||||
const entities = params.filter_entity_id.split(",");
|
const entities = params.filter_entity_id.split(",");
|
||||||
|
|
||||||
@@ -95,7 +299,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
|||||||
const numberState = Number(state.state);
|
const numberState = Number(state.state);
|
||||||
|
|
||||||
if (isNaN(numberState)) {
|
if (isNaN(numberState)) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line no-console
|
||||||
console.log(
|
console.log(
|
||||||
"Ignoring state with unparsable state but with a unit",
|
"Ignoring state with unparsable state but with a unit",
|
||||||
entityId,
|
entityId,
|
||||||
@@ -140,4 +344,40 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
mockHass.mockWS("history/list_statistic_ids", () => []);
|
||||||
|
mockHass.mockWS(
|
||||||
|
"history/statistics_during_period",
|
||||||
|
({ statistic_ids, start_time, end_time }, hass) => {
|
||||||
|
const start = new Date(start_time);
|
||||||
|
const end = end_time ? new Date(end_time) : new Date();
|
||||||
|
|
||||||
|
const statistics: Record<string, StatisticValue[]> = {};
|
||||||
|
|
||||||
|
statistic_ids.forEach((id: string) => {
|
||||||
|
if (id in statisticsFunctions) {
|
||||||
|
statistics[id] = statisticsFunctions[id](id, start, end);
|
||||||
|
} else {
|
||||||
|
const entityState = hass.states[id];
|
||||||
|
const state = entityState ? Number(entityState.state) : 1;
|
||||||
|
statistics[id] =
|
||||||
|
entityState && "last_reset" in entityState.attributes
|
||||||
|
? generateSumStatistics(
|
||||||
|
id,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
state,
|
||||||
|
state * (state > 80 ? 0.01 : 0.05)
|
||||||
|
)
|
||||||
|
: generateMeanStatistics(
|
||||||
|
id,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
state,
|
||||||
|
state * (state > 80 ? 0.05 : 0.1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@@ -10,10 +10,9 @@ export const mockLovelace = (
|
|||||||
localizePromise: Promise<LocalizeFunc>
|
localizePromise: Promise<LocalizeFunc>
|
||||||
) => {
|
) => {
|
||||||
hass.mockWS("lovelace/config", () =>
|
hass.mockWS("lovelace/config", () =>
|
||||||
Promise.all([
|
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||||
selectedDemoConfig,
|
([config, localize]) => config.lovelace(localize)
|
||||||
localizePromise,
|
)
|
||||||
]).then(([config, localize]) => config.lovelace(localize))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||||
@@ -24,9 +23,9 @@ customElements.whenDefined("hui-view").then(() => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const HUIView = customElements.get("hui-view");
|
const HUIView = customElements.get("hui-view");
|
||||||
// Patch HUI-VIEW to make the lovelace object available to the demo card
|
// Patch HUI-VIEW to make the lovelace object available to the demo card
|
||||||
const oldCreateCard = HUIView.prototype.createCardElement;
|
const oldCreateCard = HUIView!.prototype.createCardElement;
|
||||||
|
|
||||||
HUIView.prototype.createCardElement = function (config) {
|
HUIView!.prototype.createCardElement = function (config) {
|
||||||
const el = oldCreateCard.call(this, config);
|
const el = oldCreateCard.call(this, config);
|
||||||
if (el.tagName === "HA-DEMO-CARD") {
|
if (el.tagName === "HA-DEMO-CARD") {
|
||||||
(el as HADemoCard).lovelace = this.lovelace;
|
(el as HADemoCard).lovelace = this.lovelace;
|
||||||
|
@@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
|
|||||||
body: { message: "Template dev tool does not work in the demo." },
|
body: { message: "Template dev tool does not work in the demo." },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
hass.mockWS("render_template", (msg, onChange) => {
|
hass.mockWS("render_template", (msg, _hass, onChange) => {
|
||||||
onChange!({
|
onChange!({
|
||||||
result: msg.template,
|
result: msg.template,
|
||||||
listeners: { all: false, domains: [], entities: [], time: false },
|
listeners: { all: false, domains: [], entities: [], time: false },
|
||||||
|
35
gallery/script/netlify_build_gallery
Executable file
35
gallery/script/netlify_build_gallery
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TARGET_LABEL="Needs gallery preview"
|
||||||
|
|
||||||
|
if [[ "$NETLIFY" != "true" ]]; then
|
||||||
|
echo "This script can only be run on Netlify"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function createStatus() {
|
||||||
|
state="$1"
|
||||||
|
description="$2"
|
||||||
|
target_url="$3"
|
||||||
|
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
"https://api.github.com/repos/home-assistant/frontend/statuses/$COMMIT_REF" \
|
||||||
|
-d '{"state": "'"${state}"'", "context": "Netlify/Gallery Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "${PULL_REQUEST}" == "false" ]]; then
|
||||||
|
gulp build-gallery
|
||||||
|
else
|
||||||
|
if [[ "$(curl -sSLf -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
"https://api.github.com/repos/home-assistant/frontend/pulls/${REVIEW_ID}" | jq '.labels[].name' -r)" =~ "$TARGET_LABEL" ]]; then
|
||||||
|
createStatus "pending" "Building gallery preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
|
||||||
|
gulp build-gallery
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
createStatus "success" "Build complete" "$DEPLOY_URL"
|
||||||
|
else
|
||||||
|
createStatus "error" "Build failed" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
createStatus "success" "Build was not requested by PR label"
|
||||||
|
fi
|
||||||
|
fi
|
143
gallery/src/components/demo-black-white-row.ts
Normal file
143
gallery/src/components/demo-black-white-row.ts
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import { Button } from "@material/mwc-button";
|
||||||
|
import { html, LitElement, css, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||||
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("demo-black-white-row")
|
||||||
|
class DemoBlackWhiteRow extends LitElement {
|
||||||
|
@property() title!: string;
|
||||||
|
|
||||||
|
@property() value!: any;
|
||||||
|
|
||||||
|
@property() disabled = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="row">
|
||||||
|
<div class="content light">
|
||||||
|
<ha-card .header=${this.title}>
|
||||||
|
<div class="card-content">
|
||||||
|
<slot name="light"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@click=${this.handleSubmit}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
<div class="content dark">
|
||||||
|
<ha-card .header=${this.title}>
|
||||||
|
<div class="card-content">
|
||||||
|
<slot name="dark"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@click=${this.handleSubmit}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
applyThemesOnElement(
|
||||||
|
this.shadowRoot!.querySelector(".dark"),
|
||||||
|
{
|
||||||
|
default_theme: "default",
|
||||||
|
default_dark_theme: "default",
|
||||||
|
themes: {},
|
||||||
|
darkMode: false,
|
||||||
|
},
|
||||||
|
"default",
|
||||||
|
{ dark: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(ev) {
|
||||||
|
const content = (ev.target as Button).closest(".content")!;
|
||||||
|
fireEvent(this, "submitted" as any, {
|
||||||
|
slot: content.classList.contains("light") ? "light" : "dark",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 50px 0;
|
||||||
|
background-color: var(--primary-background-color);
|
||||||
|
}
|
||||||
|
.light {
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 50px;
|
||||||
|
padding-right: 50px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.light ha-card {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
.dark {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
padding-left: 50px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
width: 300px;
|
||||||
|
margin: 0 16px 0;
|
||||||
|
overflow: auto;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1500px) {
|
||||||
|
.light {
|
||||||
|
flex: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 1000px) {
|
||||||
|
.light,
|
||||||
|
.dark {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.row,
|
||||||
|
.dark {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin: 16px auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-black-white-row": DemoBlackWhiteRow;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/trace/hat-script-graph";
|
import "../../../src/components/trace/hat-script-graph";
|
||||||
import "../../../src/components/trace/hat-trace-timeline";
|
import "../../../src/components/trace/hat-trace-timeline";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { DemoTrace } from "../data/traces/types";
|
import { DemoTrace } from "../data/traces/types";
|
||||||
import { basicTrace } from "../data/traces/basic_trace";
|
import { basicTrace } from "../data/traces/basic_trace";
|
||||||
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
|
|
||||||
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||||
|
|
||||||
|
156
gallery/src/demos/demo-ha-alert.ts
Normal file
156
gallery/src/demos/demo-ha-alert.ts
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import "../../../src/components/ha-alert";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
|
||||||
|
const alerts: {
|
||||||
|
title?: string;
|
||||||
|
description: string | TemplateResult;
|
||||||
|
type: "info" | "warning" | "error" | "success";
|
||||||
|
dismissable?: boolean;
|
||||||
|
action?: string;
|
||||||
|
rtl?: boolean;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
title: "Test info alert",
|
||||||
|
description: "This is a test info alert with a title and description",
|
||||||
|
type: "info",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test warning alert",
|
||||||
|
description: "This is a test warning alert with a title and description",
|
||||||
|
type: "warning",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test error alert",
|
||||||
|
description: "This is a test error alert with a title and description",
|
||||||
|
type: "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test warning with long string",
|
||||||
|
description:
|
||||||
|
"sensor.lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum",
|
||||||
|
type: "warning",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test success alert",
|
||||||
|
description: "This is a test success alert with a title and description",
|
||||||
|
type: "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "This is a test info alert with description only",
|
||||||
|
type: "info",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
"This is a test warning alert with a rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really long description only",
|
||||||
|
type: "warning",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Error with description and list",
|
||||||
|
description: html`<p>
|
||||||
|
This is a test error alert with a title, description and a list
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>List item #1</li>
|
||||||
|
<li>List item #2</li>
|
||||||
|
<li>List item #3</li>
|
||||||
|
</ul>`,
|
||||||
|
type: "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test dismissable alert",
|
||||||
|
description: "This is a test success alert that can be dismissable",
|
||||||
|
type: "success",
|
||||||
|
dismissable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Dismissable information",
|
||||||
|
type: "info",
|
||||||
|
dismissable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Error with action",
|
||||||
|
description: "This is a test error alert with action",
|
||||||
|
type: "error",
|
||||||
|
action: "restart",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Unsaved data",
|
||||||
|
description: "You have unsaved data",
|
||||||
|
type: "warning",
|
||||||
|
action: "save",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Dismissable information (RTL)",
|
||||||
|
type: "info",
|
||||||
|
dismissable: true,
|
||||||
|
rtl: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Error with action",
|
||||||
|
description: "This is a test error alert with action (RTL)",
|
||||||
|
type: "error",
|
||||||
|
action: "restart",
|
||||||
|
rtl: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Test success alert (RTL)",
|
||||||
|
description: "This is a test success alert with a title and description",
|
||||||
|
type: "success",
|
||||||
|
rtl: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-ha-alert")
|
||||||
|
export class DemoHaAlert extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card header="ha-alert demo">
|
||||||
|
<div class="card-content">
|
||||||
|
${alerts.map(
|
||||||
|
(alert) => html`
|
||||||
|
<ha-alert
|
||||||
|
.title=${alert.title || ""}
|
||||||
|
.alertType=${alert.type}
|
||||||
|
.dismissable=${alert.dismissable || false}
|
||||||
|
.actionText=${alert.action || ""}
|
||||||
|
.rtl=${alert.rtl || false}
|
||||||
|
>
|
||||||
|
${alert.description}
|
||||||
|
</ha-alert>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
ha-alert {
|
||||||
|
display: block;
|
||||||
|
margin: 24px 0;
|
||||||
|
}
|
||||||
|
.condition {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-ha-alert": DemoHaAlert;
|
||||||
|
}
|
||||||
|
}
|
85
gallery/src/demos/demo-ha-bar.ts
Normal file
85
gallery/src/demos/demo-ha-bar.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import "../../../src/components/ha-bar";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
|
||||||
|
const bars: {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
value: number;
|
||||||
|
warning?: number;
|
||||||
|
error?: number;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
value: 33,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
min: -10,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 200,
|
||||||
|
max: 13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 4,
|
||||||
|
min: 13,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-ha-bar")
|
||||||
|
export class DemoHaBar extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${bars
|
||||||
|
.map((bar) => ({ min: 0, max: 100, warning: 70, error: 90, ...bar }))
|
||||||
|
.map(
|
||||||
|
(bar) => html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<pre>Config: ${JSON.stringify(bar)}</pre>
|
||||||
|
<ha-bar
|
||||||
|
class=${classMap({
|
||||||
|
warning: bar.value > bar.warning,
|
||||||
|
error: bar.value > bar.error,
|
||||||
|
})}
|
||||||
|
.min=${bar.min}
|
||||||
|
.max=${bar.max}
|
||||||
|
.value=${bar.value}
|
||||||
|
>
|
||||||
|
</ha-bar>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
.warning {
|
||||||
|
--ha-bar-primary-color: var(--warning-color);
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
--ha-bar-primary-color: var(--error-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-ha-bar": DemoHaBar;
|
||||||
|
}
|
||||||
|
}
|
61
gallery/src/demos/demo-ha-chip.ts
Normal file
61
gallery/src/demos/demo-ha-chip.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { mdiHomeAssistant } from "@mdi/js";
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/components/ha-chip";
|
||||||
|
import "../../../src/components/ha-svg-icon";
|
||||||
|
|
||||||
|
const chips: {
|
||||||
|
icon?: string;
|
||||||
|
content?: string;
|
||||||
|
}[] = [
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
icon: mdiHomeAssistant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "Content",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: mdiHomeAssistant,
|
||||||
|
content: "Content",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-ha-chip")
|
||||||
|
export class DemoHaChip extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card header="ha-chip demo">
|
||||||
|
<div class="card-content">
|
||||||
|
${chips.map(
|
||||||
|
(chip) => html`
|
||||||
|
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||||
|
${chip.icon
|
||||||
|
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||||
|
</ha-svg-icon>`
|
||||||
|
: ""}
|
||||||
|
${chip.content}
|
||||||
|
</ha-chip>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-ha-chip": DemoHaChip;
|
||||||
|
}
|
||||||
|
}
|
282
gallery/src/demos/demo-ha-form.ts
Normal file
282
gallery/src/demos/demo-ha-form.ts
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/* eslint-disable lit/no-template-arrow */
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import { LitElement, TemplateResult, html } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import { computeInitialHaFormData } from "../../../src/components/ha-form/compute-initial-ha-form-data";
|
||||||
|
import type { HaFormSchema } from "../../../src/components/ha-form/types";
|
||||||
|
import "../../../src/components/ha-form/ha-form";
|
||||||
|
import "../components/demo-black-white-row";
|
||||||
|
|
||||||
|
const SCHEMAS: {
|
||||||
|
title: string;
|
||||||
|
translations?: Record<string, string>;
|
||||||
|
error?: Record<string, string>;
|
||||||
|
schema: HaFormSchema[];
|
||||||
|
data?: Record<string, any>;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
title: "Authentication",
|
||||||
|
translations: {
|
||||||
|
username: "Username",
|
||||||
|
password: "Password",
|
||||||
|
invalid_login: "Invalid username or password",
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
base: "invalid_login",
|
||||||
|
},
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "username",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "password",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: "One of each",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "constant",
|
||||||
|
value: "Constant Value",
|
||||||
|
name: "constant",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "boolean",
|
||||||
|
name: "bool",
|
||||||
|
optional: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int",
|
||||||
|
optional: true,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "float",
|
||||||
|
name: "float",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "string",
|
||||||
|
optional: true,
|
||||||
|
default: "Default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "default"],
|
||||||
|
["other", "other"],
|
||||||
|
],
|
||||||
|
name: "select",
|
||||||
|
optional: true,
|
||||||
|
default: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "multi_select",
|
||||||
|
options: {
|
||||||
|
default: "Default",
|
||||||
|
other: "Other",
|
||||||
|
},
|
||||||
|
name: "multi",
|
||||||
|
optional: true,
|
||||||
|
default: ["default"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "positive_time_period_dict",
|
||||||
|
name: "time",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Numbers",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int with default",
|
||||||
|
optional: true,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int range required",
|
||||||
|
required: true,
|
||||||
|
default: 5,
|
||||||
|
valueMin: 0,
|
||||||
|
valueMax: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
name: "int range optional",
|
||||||
|
optional: true,
|
||||||
|
valueMin: 0,
|
||||||
|
valueMax: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "select",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "Default"],
|
||||||
|
["other", "Other"],
|
||||||
|
],
|
||||||
|
name: "select",
|
||||||
|
required: true,
|
||||||
|
default: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "Default"],
|
||||||
|
["other", "Other"],
|
||||||
|
],
|
||||||
|
name: "select optional",
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
["default", "Default"],
|
||||||
|
["other", "Other"],
|
||||||
|
["uno", "mas"],
|
||||||
|
["one", "more"],
|
||||||
|
["and", "another_one"],
|
||||||
|
["option", "1000"],
|
||||||
|
],
|
||||||
|
name: "select many otions",
|
||||||
|
optional: true,
|
||||||
|
default: "default",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Multi select",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "multi_select",
|
||||||
|
options: {
|
||||||
|
default: "Default",
|
||||||
|
other: "Other",
|
||||||
|
},
|
||||||
|
name: "multi",
|
||||||
|
required: true,
|
||||||
|
default: ["default"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "multi_select",
|
||||||
|
options: {
|
||||||
|
default: "Default",
|
||||||
|
other: "Other",
|
||||||
|
uno: "mas",
|
||||||
|
one: "more",
|
||||||
|
and: "another_one",
|
||||||
|
option: "1000",
|
||||||
|
},
|
||||||
|
name: "multi many otions",
|
||||||
|
optional: true,
|
||||||
|
default: ["default"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Field specific error",
|
||||||
|
data: {
|
||||||
|
new_password: "hello",
|
||||||
|
new_password_2: "bye",
|
||||||
|
},
|
||||||
|
translations: {
|
||||||
|
new_password: "New Password",
|
||||||
|
new_password_2: "Re-type Password",
|
||||||
|
not_match: "The passwords do not match",
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
new_password_2: "not_match",
|
||||||
|
},
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "new_password",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "new_password_2",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-ha-form")
|
||||||
|
class DemoHaForm extends LitElement {
|
||||||
|
private data = SCHEMAS.map(
|
||||||
|
({ schema, data }) => data || computeInitialHaFormData(schema)
|
||||||
|
);
|
||||||
|
|
||||||
|
private disabled = SCHEMAS.map(() => false);
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${SCHEMAS.map((info, idx) => {
|
||||||
|
const translations = info.translations || {};
|
||||||
|
return html`
|
||||||
|
<demo-black-white-row
|
||||||
|
.title=${info.title}
|
||||||
|
.value=${this.data[idx]}
|
||||||
|
.disabled=${this.disabled[idx]}
|
||||||
|
@submitted=${() => {
|
||||||
|
this.disabled[idx] = true;
|
||||||
|
this.requestUpdate();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.disabled[idx] = false;
|
||||||
|
this.requestUpdate();
|
||||||
|
}, 2000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${["light", "dark"].map(
|
||||||
|
(slot) => html`
|
||||||
|
<ha-form
|
||||||
|
slot=${slot}
|
||||||
|
.data=${this.data[idx]}
|
||||||
|
.schema=${info.schema}
|
||||||
|
.error=${info.error}
|
||||||
|
.disabled=${this.disabled[idx]}
|
||||||
|
.computeError=${(error) => translations[error] || error}
|
||||||
|
.computeLabel=${(schema) =>
|
||||||
|
translations[schema.name] || schema.name}
|
||||||
|
@value-changed=${(e) => {
|
||||||
|
this.data[idx] = e.detail.value;
|
||||||
|
this.requestUpdate();
|
||||||
|
}}
|
||||||
|
></ha-form>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</demo-black-white-row>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-ha-form": DemoHaForm;
|
||||||
|
}
|
||||||
|
}
|
122
gallery/src/demos/demo-ha-label-badge.ts
Normal file
122
gallery/src/demos/demo-ha-label-badge.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import { html, css, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import "../../../src/components/ha-label-badge";
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
|
||||||
|
const colors = ["#03a9f4", "#ffa600", "#43a047"];
|
||||||
|
|
||||||
|
const badges: {
|
||||||
|
label?: string;
|
||||||
|
description?: string;
|
||||||
|
image?: string;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
label: "label",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "label",
|
||||||
|
description: "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "label",
|
||||||
|
description: "Description",
|
||||||
|
image: "/images/living_room.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Description",
|
||||||
|
image: "/images/living_room.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "label",
|
||||||
|
image: "/images/living_room.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: "/images/living_room.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "big label",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "big label",
|
||||||
|
description: "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "big label",
|
||||||
|
description: "Description",
|
||||||
|
image: "/images/living_room.png",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-ha-label-badge")
|
||||||
|
export class DemoHaLabelBadge extends LitElement {
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
${badges.map(
|
||||||
|
(badge) => html`
|
||||||
|
<ha-label-badge
|
||||||
|
style="--ha-label-badge-color: ${colors[
|
||||||
|
Math.floor(Math.random() * colors.length)
|
||||||
|
]}"
|
||||||
|
.label=${badge.label}
|
||||||
|
.description=${badge.description}
|
||||||
|
.image=${badge.image}
|
||||||
|
>
|
||||||
|
</ha-label-badge>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
${badges.map(
|
||||||
|
(badge) => html`
|
||||||
|
<div class="badge">
|
||||||
|
<ha-label-badge
|
||||||
|
style="--ha-label-badge-color: ${colors[
|
||||||
|
Math.floor(Math.random() * colors.length)
|
||||||
|
]}"
|
||||||
|
.label=${badge.label}
|
||||||
|
.description=${badge.description}
|
||||||
|
.image=${badge.image}
|
||||||
|
>
|
||||||
|
</ha-label-badge>
|
||||||
|
<pre>${JSON.stringify(badge, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
margin-left: 16px;
|
||||||
|
background-color: var(--markdown-code-background-color);
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-ha-label-badge": DemoHaLabelBadge;
|
||||||
|
}
|
||||||
|
}
|
131
gallery/src/demos/demo-ha-selector.ts
Normal file
131
gallery/src/demos/demo-ha-selector.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/* eslint-disable lit/no-template-arrow */
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import { LitElement, TemplateResult, css, html } from "lit";
|
||||||
|
import { customElement, state } from "lit/decorators";
|
||||||
|
import "../../../src/components/ha-selector/ha-selector";
|
||||||
|
import "../../../src/components/ha-settings-row";
|
||||||
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
|
import type { HomeAssistant } from "../../../src/types";
|
||||||
|
import "../components/demo-black-white-row";
|
||||||
|
import { BlueprintInput } from "../../../src/data/blueprint";
|
||||||
|
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
||||||
|
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
||||||
|
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
||||||
|
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
||||||
|
|
||||||
|
const SCHEMAS: {
|
||||||
|
name: string;
|
||||||
|
input: Record<string, BlueprintInput | null>;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
name: "One of each",
|
||||||
|
input: {
|
||||||
|
entity: { name: "Entity", selector: { entity: {} } },
|
||||||
|
device: { name: "Device", selector: { device: {} } },
|
||||||
|
addon: { name: "Addon", selector: { addon: {} } },
|
||||||
|
area: { name: "Area", selector: { area: {} } },
|
||||||
|
target: { name: "Target", selector: { target: {} } },
|
||||||
|
number_box: {
|
||||||
|
name: "Number Box",
|
||||||
|
selector: {
|
||||||
|
number: {
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
mode: "box",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
number_slider: {
|
||||||
|
name: "Number Slider",
|
||||||
|
selector: {
|
||||||
|
number: {
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
mode: "slider",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
boolean: { name: "Boolean", selector: { boolean: {} } },
|
||||||
|
time: { name: "Time", selector: { time: {} } },
|
||||||
|
action: { name: "Action", selector: { action: {} } },
|
||||||
|
text: { name: "Text", selector: { text: { multiline: false } } },
|
||||||
|
text_multiline: {
|
||||||
|
name: "Text multiline",
|
||||||
|
selector: { text: { multiline: true } },
|
||||||
|
},
|
||||||
|
object: { name: "Object", selector: { object: {} } },
|
||||||
|
select: {
|
||||||
|
name: "Select",
|
||||||
|
selector: { select: { options: ["Option 1", "Option 2"] } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-ha-selector")
|
||||||
|
class DemoHaSelector extends LitElement {
|
||||||
|
@state() private hass!: HomeAssistant;
|
||||||
|
|
||||||
|
private data = SCHEMAS.map(() => ({}));
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const hass = provideHass(this);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("config", "en");
|
||||||
|
mockEntityRegistry(hass);
|
||||||
|
mockDeviceRegistry(hass);
|
||||||
|
mockAreaRegistry(hass);
|
||||||
|
mockHassioSupervisor(hass);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
${SCHEMAS.map((info, idx) => {
|
||||||
|
const data = this.data[idx];
|
||||||
|
const valueChanged = (ev) => {
|
||||||
|
this.data[idx] = {
|
||||||
|
...data,
|
||||||
|
[ev.target.key]: ev.detail.value,
|
||||||
|
};
|
||||||
|
this.requestUpdate();
|
||||||
|
};
|
||||||
|
return html`
|
||||||
|
<demo-black-white-row .title=${info.name} .value=${this.data[idx]}>
|
||||||
|
${["light", "dark"].map((slot) =>
|
||||||
|
Object.entries(info.input).map(
|
||||||
|
([key, value]) =>
|
||||||
|
html`
|
||||||
|
<ha-settings-row narrow slot=${slot}>
|
||||||
|
<span slot="heading">${value?.name || key}</span>
|
||||||
|
<span slot="description">${value?.description}</span>
|
||||||
|
<ha-selector
|
||||||
|
.hass=${this.hass}
|
||||||
|
.selector=${value!.selector}
|
||||||
|
.key=${key}
|
||||||
|
.value=${data[key] ?? value!.default}
|
||||||
|
@value-changed=${valueChanged}
|
||||||
|
></ha-selector>
|
||||||
|
</ha-settings-row>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</demo-black-white-row>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
paper-input,
|
||||||
|
ha-selector {
|
||||||
|
width: 60;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-ha-selector": DemoHaSelector;
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,8 @@ import { html, css, LitElement, TemplateResult } from "lit";
|
|||||||
import "../../../src/components/ha-formfield";
|
import "../../../src/components/ha-formfield";
|
||||||
import "../../../src/components/ha-switch";
|
import "../../../src/components/ha-switch";
|
||||||
|
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { IntegrationManifest } from "../../../src/data/integration";
|
import { IntegrationManifest } from "../../../src/data/integration";
|
||||||
|
|
||||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||||
@@ -15,8 +17,6 @@ import type {
|
|||||||
} from "../../../src/panels/config/integrations/ha-config-integrations";
|
} from "../../../src/panels/config/integrations/ha-config-integrations";
|
||||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
|
|
||||||
const createConfigEntry = (
|
const createConfigEntry = (
|
||||||
title: string,
|
title: string,
|
||||||
@@ -187,6 +187,7 @@ const createEntityRegistryEntries = (
|
|||||||
device_id: "mock-device-id",
|
device_id: "mock-device-id",
|
||||||
area_id: null,
|
area_id: null,
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
|
entity_category: null,
|
||||||
entity_id: "binary_sensor.updater",
|
entity_id: "binary_sensor.updater",
|
||||||
name: null,
|
name: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
@@ -211,6 +212,7 @@ const createDeviceRegistryEntries = (
|
|||||||
area_id: null,
|
area_id: null,
|
||||||
name_by_user: null,
|
name_by_user: null,
|
||||||
disabled_by: null,
|
disabled_by: null,
|
||||||
|
configuration_url: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
||||||
@@ -9,7 +9,6 @@ import { actionHandler } from "../../../src/panels/lovelace/common/directives/ac
|
|||||||
export class DemoUtilLongPress extends LitElement {
|
export class DemoUtilLongPress extends LitElement {
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
|
||||||
${[1, 2, 3].map(
|
${[1, 2, 3].map(
|
||||||
() => html`
|
() => html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@@ -41,26 +40,22 @@ export class DemoUtilLongPress extends LitElement {
|
|||||||
area.scrollTop = area.scrollHeight;
|
area.scrollTop = area.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderStyle() {
|
static styles = css`
|
||||||
return html`
|
ha-card {
|
||||||
<style>
|
width: 200px;
|
||||||
ha-card {
|
margin: calc(42vh - 140px) auto;
|
||||||
width: 200px;
|
padding: 8px;
|
||||||
margin: calc(42vh - 140px) auto;
|
text-align: center;
|
||||||
padding: 8px;
|
}
|
||||||
text-align: center;
|
ha-card:first-of-type {
|
||||||
}
|
margin-top: 16px;
|
||||||
ha-card:first-of-type {
|
}
|
||||||
margin-top: 16px;
|
ha-card:last-of-type {
|
||||||
}
|
margin-bottom: 16px;
|
||||||
ha-card:last-of-type {
|
}
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
</style>
|
`;
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -65,10 +65,11 @@ class HaGallery extends PolymerElement {
|
|||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
icon="hass:arrow-left"
|
|
||||||
on-click="_backTapped"
|
on-click="_backTapped"
|
||||||
class$="[[_computeHeaderButtonClass(_demo)]]"
|
class$="[[_computeHeaderButtonClass(_demo)]]"
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-icon icon="hass:arrow-left"></ha-icon>
|
||||||
|
</ha-icon-button>
|
||||||
<div main-title>
|
<div main-title>
|
||||||
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
[[_withDefault(_demo, "Home Assistant Gallery")]]
|
||||||
</div>
|
</div>
|
||||||
@@ -172,6 +173,14 @@ class HaGallery extends PolymerElement {
|
|||||||
this.$.notifications.showDialog({ message: ev.detail.message })
|
this.$.notifications.showDialog({ message: ev.detail.message })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.addEventListener("alert-dismissed-clicked", () =>
|
||||||
|
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addEventListener("alert-action-clicked", () =>
|
||||||
|
this.$.notifications.showDialog({ message: "Alert action clicked" })
|
||||||
|
);
|
||||||
|
|
||||||
this.addEventListener("hass-more-info", (ev) => {
|
this.addEventListener("hass-more-info", (ev) => {
|
||||||
if (ev.detail.entityId) {
|
if (ev.detail.entityId) {
|
||||||
this.$.notifications.showDialog({
|
this.$.notifications.showDialog({
|
||||||
|
@@ -4,6 +4,7 @@ import { property } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
|
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
@@ -32,7 +33,7 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
return filterAndSort(addons, filter);
|
return filterAndSort(addons, filter);
|
||||||
}
|
}
|
||||||
return addons.sort((a, b) =>
|
return addons.sort((a, b) =>
|
||||||
a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1
|
caseInsensitiveStringCompare(a.name, b.name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
@@ -18,7 +17,7 @@ import { navigate } from "../../../src/common/navigate";
|
|||||||
import "../../../src/common/search/search-input";
|
import "../../../src/common/search/search-input";
|
||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-icon-button";
|
||||||
import {
|
import {
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
@@ -92,9 +91,11 @@ class HassioAddonStore extends LitElement {
|
|||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
>
|
>
|
||||||
<mwc-icon-button slot="trigger" alt="menu">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
.label=${this.supervisor.localize("common.menu")}
|
||||||
</mwc-icon-button>
|
.path=${mdiDotsVertical}
|
||||||
|
slot="trigger"
|
||||||
|
></ha-icon-button>
|
||||||
<mwc-list-item>
|
<mwc-list-item>
|
||||||
${this.supervisor.localize("store.repositories")}
|
${this.supervisor.localize("store.repositories")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
@@ -113,6 +114,7 @@ class HassioAddonStore extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<search-input
|
<search-input
|
||||||
|
.hass=${this.hass}
|
||||||
no-label-float
|
no-label-float
|
||||||
no-underline
|
no-underline
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
|
@@ -13,6 +13,7 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "web-animations-js/web-animations-next-lite.min";
|
import "web-animations-js/web-animations-next-lite.min";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -53,7 +54,9 @@ class HassioAddonAudio extends LitElement {
|
|||||||
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
||||||
>
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${this.supervisor.localize(
|
.label=${this.supervisor.localize(
|
||||||
@@ -117,10 +120,6 @@ class HassioAddonAudio extends LitElement {
|
|||||||
paper-dropdown-menu {
|
paper-dropdown-menu {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
color: var(--error-color);
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
paper-item {
|
paper-item {
|
||||||
width: 450px;
|
width: 450px;
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ import "@material/mwc-button";
|
|||||||
import { ActionDetail } from "@material/mwc-list";
|
import { ActionDetail } from "@material/mwc-list";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
|
||||||
import { DEFAULT_SCHEMA, Type } from "js-yaml";
|
import { DEFAULT_SCHEMA, Type } from "js-yaml";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -16,11 +15,13 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import "../../../../src/components/ha-yaml-editor";
|
import "../../../../src/components/ha-yaml-editor";
|
||||||
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
|
||||||
@@ -100,9 +101,11 @@ class HassioAddonConfig extends LitElement {
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="card-menu">
|
<div class="card-menu">
|
||||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||||
<mwc-icon-button slot="trigger">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
.label=${this.hass.localize("common.menu")}
|
||||||
</mwc-icon-button>
|
.path=${mdiDotsVertical}
|
||||||
|
slot="trigger"
|
||||||
|
></ha-icon-button>
|
||||||
<mwc-list-item .disabled=${!this._canShowSchema}>
|
<mwc-list-item .disabled=${!this._canShowSchema}>
|
||||||
${this._yamlMode
|
${this._yamlMode
|
||||||
? this.supervisor.localize(
|
? this.supervisor.localize(
|
||||||
@@ -134,19 +137,21 @@ class HassioAddonConfig extends LitElement {
|
|||||||
></ha-form>`
|
></ha-form>`
|
||||||
: html` <ha-yaml-editor
|
: html` <ha-yaml-editor
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
.schema=${ADDON_YAML_SCHEMA}
|
.yamlSchema=${ADDON_YAML_SCHEMA}
|
||||||
></ha-yaml-editor>`}
|
></ha-yaml-editor>`}
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
${!this._yamlMode ||
|
${!this._yamlMode ||
|
||||||
(this._canShowSchema && this.addon.schema) ||
|
(this._canShowSchema && this.addon.schema) ||
|
||||||
this._valid
|
this._valid
|
||||||
? ""
|
? ""
|
||||||
: html`
|
: html`
|
||||||
<div class="errors">
|
<ha-alert alert-type="error">
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.configuration.options.invalid_yaml"
|
"addon.configuration.options.invalid_yaml"
|
||||||
)}
|
)}
|
||||||
</div>
|
</ha-alert>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
${hasHiddenOptions
|
${hasHiddenOptions
|
||||||
@@ -257,7 +262,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
path: "options",
|
path: "options",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.common.update_available",
|
"addon.common.update_available",
|
||||||
"error",
|
"error",
|
||||||
@@ -269,6 +274,9 @@ class HassioAddonConfig extends LitElement {
|
|||||||
|
|
||||||
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
|
const options: Record<string, unknown> = this._yamlMode
|
||||||
|
? this._editor?.value
|
||||||
|
: this._options;
|
||||||
const eventdata = {
|
const eventdata = {
|
||||||
success: true,
|
success: true,
|
||||||
response: undefined,
|
response: undefined,
|
||||||
@@ -282,20 +290,20 @@ class HassioAddonConfig extends LitElement {
|
|||||||
const validation = await validateHassioAddonOption(
|
const validation = await validateHassioAddonOption(
|
||||||
this.hass,
|
this.hass,
|
||||||
this.addon.slug,
|
this.addon.slug,
|
||||||
this._editor?.value
|
options
|
||||||
);
|
);
|
||||||
if (!validation.valid) {
|
if (!validation.valid) {
|
||||||
throw Error(validation.message);
|
throw Error(validation.message);
|
||||||
}
|
}
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, {
|
await setHassioAddonOption(this.hass, this.addon.slug, {
|
||||||
options: this._yamlMode ? this._editor?.value : this._options,
|
options,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._configHasChanged = false;
|
this._configHasChanged = false;
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -322,17 +330,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
color: var(--error-color);
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
iron-autogrow-textarea {
|
|
||||||
width: 100%;
|
|
||||||
font-family: var(--code-font-family, monospace);
|
|
||||||
}
|
|
||||||
.syntaxerror {
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
.card-menu {
|
.card-menu {
|
||||||
float: right;
|
float: right;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -62,7 +63,9 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -86,9 +89,9 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
<td>
|
<td>
|
||||||
<paper-input
|
<paper-input
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
placeholder="${this.supervisor.localize(
|
placeholder=${this.supervisor.localize(
|
||||||
"addon.configuration.network.disabled"
|
"addon.configuration.network.disabled"
|
||||||
)}"
|
)}
|
||||||
.value=${item.host ? String(item.host) : ""}
|
.value=${item.host ? String(item.host) : ""}
|
||||||
.container=${item.container}
|
.container=${item.container}
|
||||||
no-label-float
|
no-label-float
|
||||||
@@ -168,7 +171,7 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_reset",
|
"addon.failed_to_reset",
|
||||||
"error",
|
"error",
|
||||||
@@ -204,7 +207,7 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -225,10 +228,6 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
ha-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
color: var(--error-color);
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.card-actions {
|
.card-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonDocumentation,
|
fetchHassioAddonDocumentation,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
@@ -12,7 +14,6 @@ import { haStyle } from "../../../../src/resources/styles";
|
|||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
|
|
||||||
@customElement("hassio-addon-documentation-tab")
|
@customElement("hassio-addon-documentation-tab")
|
||||||
class HassioAddonDocumentationDashboard extends LitElement {
|
class HassioAddonDocumentationDashboard extends LitElement {
|
||||||
@@ -38,7 +39,9 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ha-card>
|
<ha-card>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._content
|
${this._content
|
||||||
? html`<ha-markdown .content=${this._content}></ha-markdown>`
|
? html`<ha-markdown .content=${this._content}></ha-markdown>`
|
||||||
@@ -76,7 +79,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
this.hass,
|
this.hass,
|
||||||
this.addon!.slug
|
this.addon!.slug
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.documentation.get_logs",
|
"addon.documentation.get_logs",
|
||||||
"error",
|
"error",
|
||||||
|
@@ -222,7 +222,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
try {
|
try {
|
||||||
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
||||||
this.addon = addoninfo;
|
this.addon = addoninfo;
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
||||||
this.addon = undefined;
|
this.addon = undefined;
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
|
|||||||
import { navigate } from "../../../../src/common/navigate";
|
import { navigate } from "../../../../src/common/navigate";
|
||||||
import "../../../../src/components/buttons/ha-call-api-button";
|
import "../../../../src/components/buttons/ha-call-api-button";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-label-badge";
|
import "../../../../src/components/ha-label-badge";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
@@ -122,18 +123,18 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<hassio-card-content
|
<hassio-card-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.title="${this.supervisor.localize(
|
.title=${this.supervisor.localize(
|
||||||
"addon.dashboard.new_update_available",
|
"addon.dashboard.new_update_available",
|
||||||
"name",
|
"name",
|
||||||
this.addon.name,
|
this.addon.name,
|
||||||
"version",
|
"version",
|
||||||
this.addon.version_latest
|
this.addon.version_latest
|
||||||
)}"
|
)}
|
||||||
.description="${this.supervisor.localize(
|
.description=${this.supervisor.localize(
|
||||||
"common.running_version",
|
"common.running_version",
|
||||||
"version",
|
"version",
|
||||||
this.addon.version
|
this.addon.version
|
||||||
)}"
|
)}
|
||||||
icon=${mdiArrowUpBoldCircle}
|
icon=${mdiArrowUpBoldCircle}
|
||||||
iconClass="update"
|
iconClass="update"
|
||||||
></hassio-card-content>
|
></hassio-card-content>
|
||||||
@@ -143,14 +144,14 @@ class HassioAddonInfo extends LitElement {
|
|||||||
this.addon.arch
|
this.addon.arch
|
||||||
)
|
)
|
||||||
? html`
|
? html`
|
||||||
<p class="warning">
|
<ha-alert alert-type="warning">
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.dashboard.not_available_arch"
|
"addon.dashboard.not_available_arch"
|
||||||
)}
|
)}
|
||||||
</p>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<p class="warning">
|
<ha-alert alert-type="warning">
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.dashboard.not_available_arch",
|
"addon.dashboard.not_available_arch",
|
||||||
"core_version_installed",
|
"core_version_installed",
|
||||||
@@ -158,7 +159,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
"core_version_needed",
|
"core_version_needed",
|
||||||
addonStoreInfo.homeassistant
|
addonStoreInfo.homeassistant
|
||||||
)}
|
)}
|
||||||
</p>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -253,7 +254,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.dashboard.visit_addon_page",
|
"addon.dashboard.visit_addon_page",
|
||||||
"name",
|
"name",
|
||||||
html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer"
|
html`<a href=${this.addon.url!} target="_blank" rel="noreferrer"
|
||||||
>${this.addon.name}</a
|
>${this.addon.name}</a
|
||||||
>`
|
>`
|
||||||
)}
|
)}
|
||||||
@@ -296,10 +297,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
})}
|
})}
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="rating"
|
id="rating"
|
||||||
.value=${this.addon.rating}
|
|
||||||
label="rating"
|
label="rating"
|
||||||
description=""
|
description=""
|
||||||
></ha-label-badge>
|
>
|
||||||
|
${this.addon.rating}
|
||||||
|
</ha-label-badge>
|
||||||
${this.addon.host_network
|
${this.addon.host_network
|
||||||
? html`
|
? html`
|
||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@@ -363,9 +365,9 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-label-badge
|
<ha-label-badge
|
||||||
@click=${this._showMoreInfo}
|
@click=${this._showMoreInfo}
|
||||||
id="docker_api"
|
id="docker_api"
|
||||||
.label=".${this.supervisor.localize(
|
.label=${this.supervisor.localize(
|
||||||
"addon.dashboard.capability.label.docker"
|
"addon.dashboard.capability.label.docker"
|
||||||
)}"
|
)}
|
||||||
description=""
|
description=""
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
||||||
@@ -436,10 +438,10 @@ class HassioAddonInfo extends LitElement {
|
|||||||
${this.addon.version
|
${this.addon.version
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div
|
||||||
class="${classMap({
|
class=${classMap({
|
||||||
"addon-options": true,
|
"addon-options": true,
|
||||||
started: this.addon.state === "started",
|
started: this.addon.state === "started",
|
||||||
})}"
|
})}
|
||||||
>
|
>
|
||||||
<ha-settings-row ?three-line=${this.narrow}>
|
<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
@@ -569,21 +571,23 @@ class HassioAddonInfo extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
${!this.addon.version && addonStoreInfo && !this.addon.available
|
${!this.addon.version && addonStoreInfo && !this.addon.available
|
||||||
? !addonArchIsSupported(
|
? !addonArchIsSupported(
|
||||||
this.supervisor.info.supported_arch,
|
this.supervisor.info.supported_arch,
|
||||||
this.addon.arch
|
this.addon.arch
|
||||||
)
|
)
|
||||||
? html`
|
? html`
|
||||||
<p class="warning">
|
<ha-alert alert-type="warning">
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.dashboard.not_available_arch"
|
"addon.dashboard.not_available_arch"
|
||||||
)}
|
)}
|
||||||
</p>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<p class="warning">
|
<ha-alert alert-type="warning">
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"addon.dashboard.not_available_version",
|
"addon.dashboard.not_available_version",
|
||||||
"core_version_installed",
|
"core_version_installed",
|
||||||
@@ -591,7 +595,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
"core_version_needed",
|
"core_version_needed",
|
||||||
addonStoreInfo!.homeassistant
|
addonStoreInfo!.homeassistant
|
||||||
)}
|
)}
|
||||||
</p>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -793,7 +797,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "option",
|
path: "option",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -815,7 +819,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "option",
|
path: "option",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -837,7 +841,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "option",
|
path: "option",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -859,7 +863,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "security",
|
path: "security",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -881,7 +885,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "option",
|
path: "option",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.failed_to_save",
|
"addon.failed_to_save",
|
||||||
"error",
|
"error",
|
||||||
@@ -892,15 +896,24 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
private async _openChangelog(): Promise<void> {
|
private async _openChangelog(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const content = await fetchHassioAddonChangelog(
|
let content = await fetchHassioAddonChangelog(this.hass, this.addon.slug);
|
||||||
this.hass,
|
if (
|
||||||
this.addon.slug
|
content.includes(`# ${this.addon.version}`) &&
|
||||||
);
|
content.includes(`# ${this.addon.version_latest}`)
|
||||||
|
) {
|
||||||
|
const newcontent = content.split(`# ${this.addon.version}`)[0];
|
||||||
|
if (newcontent.includes(`# ${this.addon.version_latest}`)) {
|
||||||
|
// Only change the content if the new version still exist
|
||||||
|
// if the changelog does not have the newests version on top
|
||||||
|
// this will not be true, and we don't modify the content
|
||||||
|
content = newcontent;
|
||||||
|
}
|
||||||
|
}
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.changelog"),
|
title: this.supervisor.localize("addon.dashboard.changelog"),
|
||||||
content,
|
content,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize(
|
title: this.supervisor.localize(
|
||||||
"addon.dashboard.action_error.get_changelog"
|
"addon.dashboard.action_error.get_changelog"
|
||||||
@@ -922,7 +935,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "install",
|
path: "install",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.action_error.install"),
|
title: this.supervisor.localize("addon.dashboard.action_error.install"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -943,7 +956,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "stop",
|
path: "stop",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.action_error.stop"),
|
title: this.supervisor.localize("addon.dashboard.action_error.stop"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -964,7 +977,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "stop",
|
path: "stop",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.action_error.restart"),
|
title: this.supervisor.localize("addon.dashboard.action_error.restart"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -978,7 +991,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
supervisor: this.supervisor,
|
supervisor: this.supervisor,
|
||||||
name: this.addon.name,
|
name: this.addon.name,
|
||||||
version: this.addon.version_latest,
|
version: this.addon.version_latest,
|
||||||
snapshotParams: {
|
backupParams: {
|
||||||
name: `addon_${this.addon.slug}_${this.addon.version}`,
|
name: `addon_${this.addon.slug}_${this.addon.version}`,
|
||||||
addons: [this.addon.slug],
|
addons: [this.addon.slug],
|
||||||
homeassistant: false,
|
homeassistant: false,
|
||||||
@@ -1023,7 +1036,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
button.progress = false;
|
button.progress = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to validate addon configuration",
|
title: "Failed to validate addon configuration",
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -1041,7 +1054,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "start",
|
path: "start",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.action_error.start"),
|
title: this.supervisor.localize("addon.dashboard.action_error.start"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -1079,7 +1092,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "uninstall",
|
path: "uninstall",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize(
|
title: this.supervisor.localize(
|
||||||
"addon.dashboard.action_error.uninstall"
|
"addon.dashboard.action_error.uninstall"
|
||||||
@@ -1140,6 +1153,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
img.logo {
|
img.logo {
|
||||||
|
max-width: 100%;
|
||||||
max-height: 60px;
|
max-height: 60px;
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -1149,10 +1163,10 @@ class HassioAddonInfo extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
ha-svg-icon.running {
|
ha-svg-icon.running {
|
||||||
color: var(--paper-green-400);
|
color: var(--success-color);
|
||||||
}
|
}
|
||||||
ha-svg-icon.stopped {
|
ha-svg-icon.stopped {
|
||||||
color: var(--google-red-300);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
ha-call-api-button {
|
ha-call-api-button {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonLogs,
|
fetchHassioAddonLogs,
|
||||||
@@ -34,7 +35,9 @@ class HassioAddonLogs extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
<ha-card>
|
<ha-card>
|
||||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._content
|
${this._content
|
||||||
? html`<hassio-ansi-to-html
|
? html`<hassio-ansi-to-html
|
||||||
@@ -60,10 +63,6 @@ class HassioAddonLogs extends LitElement {
|
|||||||
ha-card {
|
ha-card {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.errors {
|
|
||||||
color: var(--error-color);
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -72,7 +71,7 @@ class HassioAddonLogs extends LitElement {
|
|||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
try {
|
try {
|
||||||
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
|
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = this.supervisor.localize(
|
this._error = this.supervisor.localize(
|
||||||
"addon.logs.get_logs",
|
"addon.logs.get_logs",
|
||||||
"error",
|
"error",
|
||||||
|
@@ -14,7 +14,7 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import relativeTime from "../../../src/common/datetime/relative_time";
|
import { relativeTime } from "../../../src/common/datetime/relative_time";
|
||||||
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
|
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
|
||||||
import {
|
import {
|
||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
@@ -23,14 +23,16 @@ import {
|
|||||||
} from "../../../src/components/data-table/ha-data-table";
|
} from "../../../src/components/data-table/ha-data-table";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-fab";
|
import "../../../src/components/ha-fab";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import "../../../src/components/ha-icon-button";
|
||||||
|
import "../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
fetchHassioSnapshots,
|
fetchHassioBackups,
|
||||||
friendlyFolderName,
|
friendlyFolderName,
|
||||||
HassioSnapshot,
|
HassioBackup,
|
||||||
reloadHassioSnapshots,
|
reloadHassioBackups,
|
||||||
removeSnapshot,
|
removeBackup,
|
||||||
} from "../../../src/data/hassio/snapshot";
|
} from "../../../src/data/hassio/backup";
|
||||||
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
@@ -40,14 +42,14 @@ import "../../../src/layouts/hass-tabs-subpage-data-table";
|
|||||||
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
|
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
|
import { showBackupUploadDialog } from "../dialogs/backup/show-dialog-backup-upload";
|
||||||
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
|
import { showHassioBackupDialog } from "../dialogs/backup/show-dialog-hassio-backup";
|
||||||
import { showSnapshotUploadDialog } from "../dialogs/snapshot/show-dialog-snapshot-upload";
|
import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hassio-create-backup";
|
||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-snapshots")
|
@customElement("hassio-backups")
|
||||||
export class HassioSnapshots extends LitElement {
|
export class HassioBackups extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
@@ -58,9 +60,9 @@ export class HassioSnapshots extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public isWide!: boolean;
|
@property({ type: Boolean }) public isWide!: boolean;
|
||||||
|
|
||||||
@state() private _selectedSnapshots: string[] = [];
|
@state() private _selectedBackups: string[] = [];
|
||||||
|
|
||||||
@state() private _snapshots?: HassioSnapshot[] = [];
|
@state() private _backups?: HassioBackup[] = [];
|
||||||
|
|
||||||
@query("hass-tabs-subpage-data-table", true)
|
@query("hass-tabs-subpage-data-table", true)
|
||||||
private _dataTable!: HaTabsSubpageDataTable;
|
private _dataTable!: HaTabsSubpageDataTable;
|
||||||
@@ -75,26 +77,26 @@ export class HassioSnapshots extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refreshData() {
|
public async refreshData() {
|
||||||
await reloadHassioSnapshots(this.hass);
|
await reloadHassioBackups(this.hass);
|
||||||
await this.fetchSnapshots();
|
await this.fetchBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeSnapshotContent = (snapshot: HassioSnapshot): string => {
|
private _computeBackupContent = (backup: HassioBackup): string => {
|
||||||
if (snapshot.type === "full") {
|
if (backup.type === "full") {
|
||||||
return this.supervisor.localize("snapshot.full_snapshot");
|
return this.supervisor.localize("backup.full_backup");
|
||||||
}
|
}
|
||||||
const content: string[] = [];
|
const content: string[] = [];
|
||||||
if (snapshot.content.homeassistant) {
|
if (backup.content.homeassistant) {
|
||||||
content.push("Home Assistant");
|
content.push("Home Assistant");
|
||||||
}
|
}
|
||||||
if (snapshot.content.folders.length !== 0) {
|
if (backup.content.folders.length !== 0) {
|
||||||
for (const folder of snapshot.content.folders) {
|
for (const folder of backup.content.folders) {
|
||||||
content.push(friendlyFolderName[folder] || folder);
|
content.push(friendlyFolderName[folder] || folder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot.content.addons.length !== 0) {
|
if (backup.content.addons.length !== 0) {
|
||||||
for (const addon of snapshot.content.addons) {
|
for (const addon of backup.content.addons) {
|
||||||
content.push(
|
content.push(
|
||||||
this.supervisor.supervisor.addons.find(
|
this.supervisor.supervisor.addons.find(
|
||||||
(entry) => entry.slug === addon
|
(entry) => entry.slug === addon
|
||||||
@@ -117,23 +119,23 @@ export class HassioSnapshots extends LitElement {
|
|||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(narrow: boolean): DataTableColumnContainer => ({
|
(narrow: boolean): DataTableColumnContainer => ({
|
||||||
name: {
|
name: {
|
||||||
title: this.supervisor?.localize("snapshot.name") || "",
|
title: this.supervisor?.localize("backup.name") || "",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (entry: string, snapshot: any) =>
|
template: (entry: string, backup: any) =>
|
||||||
html`${entry || snapshot.slug}
|
html`${entry || backup.slug}
|
||||||
<div class="secondary">${snapshot.secondary}</div>`,
|
<div class="secondary">${backup.secondary}</div>`,
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
title: this.supervisor?.localize("snapshot.created") || "",
|
title: this.supervisor?.localize("backup.created") || "",
|
||||||
width: "15%",
|
width: "15%",
|
||||||
direction: "desc",
|
direction: "desc",
|
||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
template: (entry: string) =>
|
template: (entry: string) =>
|
||||||
relativeTime(new Date(entry), this.hass.localize),
|
relativeTime(new Date(entry), this.hass.locale),
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
title: "",
|
title: "",
|
||||||
@@ -143,10 +145,10 @@ export class HassioSnapshots extends LitElement {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
private _snapshotData = memoizeOne((snapshots: HassioSnapshot[]) =>
|
private _backupData = memoizeOne((backups: HassioBackup[]) =>
|
||||||
snapshots.map((snapshot) => ({
|
backups.map((backup) => ({
|
||||||
...snapshot,
|
...backup,
|
||||||
secondary: this._computeSnapshotContent(snapshot),
|
secondary: this._computeBackupContent(backup),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -160,11 +162,11 @@ export class HassioSnapshots extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.searchLabel=${this.supervisor.localize("search")}
|
.searchLabel=${this.supervisor.localize("search")}
|
||||||
.noDataText=${this.supervisor.localize("snapshot.no_snapshots")}
|
.noDataText=${this.supervisor.localize("backup.no_backups")}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.columns=${this._columns(this.narrow)}
|
.columns=${this._columns(this.narrow)}
|
||||||
.data=${this._snapshotData(this._snapshots || [])}
|
.data=${this._backupData(this._backups || [])}
|
||||||
id="slug"
|
id="slug"
|
||||||
@row-click=${this._handleRowClicked}
|
@row-click=${this._handleRowClicked}
|
||||||
@selection-changed=${this._handleSelectionChanged}
|
@selection-changed=${this._handleSelectionChanged}
|
||||||
@@ -179,20 +181,22 @@ export class HassioSnapshots extends LitElement {
|
|||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
>
|
>
|
||||||
<mwc-icon-button slot="trigger" alt="menu">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
.label=${this.hass.localize("common.menu")}
|
||||||
</mwc-icon-button>
|
.path=${mdiDotsVertical}
|
||||||
|
slot="trigger"
|
||||||
|
></ha-icon-button>
|
||||||
<mwc-list-item>
|
<mwc-list-item>
|
||||||
${this.supervisor?.localize("common.reload")}
|
${this.supervisor?.localize("common.reload")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
${atLeastVersion(this.hass.config.version, 0, 116)
|
${atLeastVersion(this.hass.config.version, 0, 116)
|
||||||
? html`<mwc-list-item>
|
? html`<mwc-list-item>
|
||||||
${this.supervisor?.localize("snapshot.upload_snapshot")}
|
${this.supervisor?.localize("backup.upload_backup")}
|
||||||
</mwc-list-item>`
|
</mwc-list-item>`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
|
|
||||||
${this._selectedSnapshots.length
|
${this._selectedBackups.length
|
||||||
? html`<div
|
? html`<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
"header-toolbar": this.narrow,
|
"header-toolbar": this.narrow,
|
||||||
@@ -201,8 +205,8 @@ export class HassioSnapshots extends LitElement {
|
|||||||
slot="header"
|
slot="header"
|
||||||
>
|
>
|
||||||
<p class="selected-txt">
|
<p class="selected-txt">
|
||||||
${this.supervisor.localize("snapshot.selected", {
|
${this.supervisor.localize("backup.selected", {
|
||||||
number: this._selectedSnapshots.length,
|
number: this._selectedBackups.length,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
<div class="header-btns">
|
<div class="header-btns">
|
||||||
@@ -212,19 +216,21 @@ export class HassioSnapshots extends LitElement {
|
|||||||
@click=${this._deleteSelected}
|
@click=${this._deleteSelected}
|
||||||
class="warning"
|
class="warning"
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("snapshot.delete_selected")}
|
${this.supervisor.localize("backup.delete_selected")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<mwc-icon-button
|
<ha-icon-button
|
||||||
|
.label=${this.supervisor.localize(
|
||||||
|
"snapshot.delete_selected"
|
||||||
|
)}
|
||||||
|
.path=${mdiDelete}
|
||||||
id="delete-btn"
|
id="delete-btn"
|
||||||
class="warning"
|
class="warning"
|
||||||
@click=${this._deleteSelected}
|
@click=${this._deleteSelected}
|
||||||
>
|
></ha-icon-button>
|
||||||
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
<paper-tooltip animation-delay="0" for="delete-btn">
|
<paper-tooltip animation-delay="0" for="delete-btn">
|
||||||
${this.supervisor.localize("snapshot.delete_selected")}
|
${this.supervisor.localize("backup.delete_selected")}
|
||||||
</paper-tooltip>
|
</paper-tooltip>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
@@ -233,8 +239,8 @@ export class HassioSnapshots extends LitElement {
|
|||||||
|
|
||||||
<ha-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
@click=${this._createSnapshot}
|
@click=${this._createBackup}
|
||||||
.label=${this.supervisor.localize("snapshot.create_snapshot")}
|
.label=${this.supervisor.localize("backup.create_backup")}
|
||||||
extended
|
extended
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
@@ -249,7 +255,7 @@ export class HassioSnapshots extends LitElement {
|
|||||||
this.refreshData();
|
this.refreshData();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this._showUploadSnapshotDialog();
|
this._showUploadBackupDialog();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,33 +263,33 @@ export class HassioSnapshots extends LitElement {
|
|||||||
private _handleSelectionChanged(
|
private _handleSelectionChanged(
|
||||||
ev: HASSDomEvent<SelectionChangedEvent>
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
): void {
|
): void {
|
||||||
this._selectedSnapshots = ev.detail.value;
|
this._selectedBackups = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showUploadSnapshotDialog() {
|
private _showUploadBackupDialog() {
|
||||||
showSnapshotUploadDialog(this, {
|
showBackupUploadDialog(this, {
|
||||||
showSnapshot: (slug: string) =>
|
showBackup: (slug: string) =>
|
||||||
showHassioSnapshotDialog(this, {
|
showHassioBackupDialog(this, {
|
||||||
slug,
|
slug,
|
||||||
supervisor: this.supervisor,
|
supervisor: this.supervisor,
|
||||||
onDelete: () => this.fetchSnapshots(),
|
onDelete: () => this.fetchBackups(),
|
||||||
}),
|
}),
|
||||||
reloadSnapshot: () => this.refreshData(),
|
reloadBackup: () => this.refreshData(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchSnapshots() {
|
private async fetchBackups() {
|
||||||
await reloadHassioSnapshots(this.hass);
|
await reloadHassioBackups(this.hass);
|
||||||
this._snapshots = await fetchHassioSnapshots(this.hass);
|
this._backups = await fetchHassioBackups(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _deleteSelected() {
|
private async _deleteSelected() {
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: this.supervisor.localize("snapshot.delete_snapshot_title"),
|
title: this.supervisor.localize("backup.delete_backup_title"),
|
||||||
text: this.supervisor.localize("snapshot.delete_snapshot_text", {
|
text: this.supervisor.localize("backup.delete_backup_text", {
|
||||||
number: this._selectedSnapshots.length,
|
number: this._selectedBackups.length,
|
||||||
}),
|
}),
|
||||||
confirmText: this.supervisor.localize("snapshot.delete_snapshot_confirm"),
|
confirmText: this.supervisor.localize("backup.delete_backup_confirm"),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
@@ -292,44 +298,44 @@ export class HassioSnapshots extends LitElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this._selectedSnapshots.map((slug) => removeSnapshot(this.hass, slug))
|
this._selectedBackups.map((slug) => removeBackup(this.hass, slug))
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("snapshot.failed_to_delete"),
|
title: this.supervisor.localize("backup.failed_to_delete"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await reloadHassioSnapshots(this.hass);
|
await reloadHassioBackups(this.hass);
|
||||||
this._snapshots = await fetchHassioSnapshots(this.hass);
|
this._backups = await fetchHassioBackups(this.hass);
|
||||||
this._dataTable.clearSelection();
|
this._dataTable.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
const slug = ev.detail.id;
|
const slug = ev.detail.id;
|
||||||
showHassioSnapshotDialog(this, {
|
showHassioBackupDialog(this, {
|
||||||
slug,
|
slug,
|
||||||
supervisor: this.supervisor,
|
supervisor: this.supervisor,
|
||||||
onDelete: () => this.fetchSnapshots(),
|
onDelete: () => this.fetchBackups(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createSnapshot() {
|
private _createBackup() {
|
||||||
if (this.supervisor!.info.state !== "running") {
|
if (this.supervisor!.info.state !== "running") {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor!.localize("snapshot.could_not_create"),
|
title: this.supervisor!.localize("backup.could_not_create"),
|
||||||
text: this.supervisor!.localize(
|
text: this.supervisor!.localize(
|
||||||
"snapshot.create_blocked_not_running",
|
"backup.create_blocked_not_running",
|
||||||
"state",
|
"state",
|
||||||
this.supervisor!.info.state
|
this.supervisor!.info.state
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showHassioCreateSnapshotDialog(this, {
|
showHassioCreateBackupDialog(this, {
|
||||||
supervisor: this.supervisor!,
|
supervisor: this.supervisor!,
|
||||||
onCreate: () => this.fetchSnapshots(),
|
onCreate: () => this.fetchBackups(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +374,7 @@ export class HassioSnapshots extends LitElement {
|
|||||||
margin-right: -12px;
|
margin-right: -12px;
|
||||||
}
|
}
|
||||||
.header-btns > mwc-button,
|
.header-btns > mwc-button,
|
||||||
.header-btns > mwc-icon-button {
|
.header-btns > ha-icon-button {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@@ -378,6 +384,6 @@ export class HassioSnapshots extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hassio-snapshots": HassioSnapshots;
|
"hassio-backups": HassioBackups;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -41,16 +41,16 @@ class HassioAnsiToHtml extends LitElement {
|
|||||||
text-decoration: underline line-through;
|
text-decoration: underline line-through;
|
||||||
}
|
}
|
||||||
.fg-red {
|
.fg-red {
|
||||||
color: rgb(222, 56, 43);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
.fg-green {
|
.fg-green {
|
||||||
color: rgb(57, 181, 74);
|
color: var(--success-color);
|
||||||
}
|
}
|
||||||
.fg-yellow {
|
.fg-yellow {
|
||||||
color: rgb(255, 199, 6);
|
color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.fg-blue {
|
.fg-blue {
|
||||||
color: rgb(0, 111, 184);
|
color: var(--info-color);
|
||||||
}
|
}
|
||||||
.fg-magenta {
|
.fg-magenta {
|
||||||
color: rgb(118, 38, 113);
|
color: rgb(118, 38, 113);
|
||||||
@@ -65,16 +65,16 @@ class HassioAnsiToHtml extends LitElement {
|
|||||||
background-color: rgb(0, 0, 0);
|
background-color: rgb(0, 0, 0);
|
||||||
}
|
}
|
||||||
.bg-red {
|
.bg-red {
|
||||||
background-color: rgb(222, 56, 43);
|
background-color: var(--error-color);
|
||||||
}
|
}
|
||||||
.bg-green {
|
.bg-green {
|
||||||
background-color: rgb(57, 181, 74);
|
background-color: var(--success-color);
|
||||||
}
|
}
|
||||||
.bg-yellow {
|
.bg-yellow {
|
||||||
background-color: rgb(255, 199, 6);
|
background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.bg-blue {
|
.bg-blue {
|
||||||
background-color: rgb(0, 111, 184);
|
background-color: var(--info-color);
|
||||||
}
|
}
|
||||||
.bg-magenta {
|
.bg-magenta {
|
||||||
background-color: rgb(118, 38, 113);
|
background-color: rgb(118, 38, 113);
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { mdiHelpCircle } from "@mdi/js";
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import "../../../src/components/ha-relative-time";
|
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
@@ -19,8 +18,6 @@ class HassioCardContent extends LitElement {
|
|||||||
|
|
||||||
@property() public topbarClass?: string;
|
@property() public topbarClass?: string;
|
||||||
|
|
||||||
@property() public datetime?: string;
|
|
||||||
|
|
||||||
@property() public iconTitle?: string;
|
@property() public iconTitle?: string;
|
||||||
|
|
||||||
@property() public iconClass?: string;
|
@property() public iconClass?: string;
|
||||||
@@ -37,7 +34,7 @@ class HassioCardContent extends LitElement {
|
|||||||
${this.iconImage
|
${this.iconImage
|
||||||
? html`
|
? html`
|
||||||
<div class="icon_image ${this.iconClass}">
|
<div class="icon_image ${this.iconClass}">
|
||||||
<img src="${this.iconImage}" .title=${this.iconTitle} />
|
<img src=${this.iconImage} .title=${this.iconTitle} />
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -56,15 +53,6 @@ class HassioCardContent extends LitElement {
|
|||||||
/* treat as available when undefined */
|
/* treat as available when undefined */
|
||||||
this.available === false ? " (Not available)" : ""
|
this.available === false ? " (Not available)" : ""
|
||||||
}
|
}
|
||||||
${this.datetime
|
|
||||||
? html`
|
|
||||||
<ha-relative-time
|
|
||||||
.hass=${this.hass}
|
|
||||||
class="addition"
|
|
||||||
.datetime=${this.datetime}
|
|
||||||
></ha-relative-time>
|
|
||||||
`
|
|
||||||
: undefined}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -80,14 +68,14 @@ class HassioCardContent extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
ha-svg-icon.update {
|
ha-svg-icon.update {
|
||||||
color: var(--paper-orange-400);
|
color: var(--warning-color);
|
||||||
}
|
}
|
||||||
ha-svg-icon.running,
|
ha-svg-icon.running,
|
||||||
ha-svg-icon.installed {
|
ha-svg-icon.installed {
|
||||||
color: var(--paper-green-400);
|
color: var(--success-color);
|
||||||
}
|
}
|
||||||
ha-svg-icon.hassupdate,
|
ha-svg-icon.hassupdate,
|
||||||
ha-svg-icon.snapshot {
|
ha-svg-icon.backup {
|
||||||
color: var(--paper-item-icon-color);
|
color: var(--paper-item-icon-color);
|
||||||
}
|
}
|
||||||
ha-svg-icon.not_available {
|
ha-svg-icon.not_available {
|
||||||
@@ -106,9 +94,6 @@ class HassioCardContent extends LitElement {
|
|||||||
height: 2.4em;
|
height: 2.4em;
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
}
|
}
|
||||||
ha-relative-time {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.icon_image img {
|
.icon_image img {
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
max-width: 40px;
|
max-width: 40px;
|
||||||
@@ -122,7 +107,7 @@ class HassioCardContent extends LitElement {
|
|||||||
}
|
}
|
||||||
.dot {
|
.dot {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: var(--paper-orange-400);
|
background-color: var(--warning-color);
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
|
@@ -1,31 +1,25 @@
|
|||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import { mdiFolderUpload } from "@mdi/js";
|
import { mdiFolderUpload } from "@mdi/js";
|
||||||
import "@polymer/iron-input/iron-input";
|
|
||||||
import "@polymer/paper-input/paper-input-container";
|
import "@polymer/paper-input/paper-input-container";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/ha-circular-progress";
|
import "../../../src/components/ha-circular-progress";
|
||||||
import "../../../src/components/ha-file-upload";
|
import "../../../src/components/ha-file-upload";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import { HassioBackup, uploadBackup } from "../../../src/data/hassio/backup";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
import {
|
|
||||||
HassioSnapshot,
|
|
||||||
uploadSnapshot,
|
|
||||||
} from "../../../src/data/hassio/snapshot";
|
|
||||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"snapshot-uploaded": { snapshot: HassioSnapshot };
|
"backup-uploaded": { backup: HassioBackup };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
||||||
|
|
||||||
@customElement("hassio-upload-snapshot")
|
@customElement("hassio-upload-backup")
|
||||||
export class HassioUploadSnapshot extends LitElement {
|
export class HassioUploadBackup extends LitElement {
|
||||||
public hass!: HomeAssistant;
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() public value: string | null = null;
|
@state() public value: string | null = null;
|
||||||
@@ -35,10 +29,11 @@ export class HassioUploadSnapshot extends LitElement {
|
|||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-file-upload
|
<ha-file-upload
|
||||||
|
.hass=${this.hass}
|
||||||
.uploading=${this._uploading}
|
.uploading=${this._uploading}
|
||||||
.icon=${mdiFolderUpload}
|
.icon=${mdiFolderUpload}
|
||||||
accept="application/x-tar"
|
accept="application/x-tar"
|
||||||
label="Upload snapshot"
|
label="Upload backup"
|
||||||
@file-picked=${this._uploadFile}
|
@file-picked=${this._uploadFile}
|
||||||
auto-open-file-dialog
|
auto-open-file-dialog
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
@@ -50,10 +45,10 @@ export class HassioUploadSnapshot extends LitElement {
|
|||||||
|
|
||||||
if (file.size > MAX_FILE_SIZE) {
|
if (file.size > MAX_FILE_SIZE) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Snapshot file is too big",
|
title: "Backup file is too big",
|
||||||
text: html`The maximum allowed filesize is 1GB.<br />
|
text: html`The maximum allowed filesize is 1GB.<br />
|
||||||
<a
|
<a
|
||||||
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-snapshot-on-a-new-install"
|
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-backup-on-a-new-install"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>Have a look here on how to restore it.</a
|
>Have a look here on how to restore it.</a
|
||||||
>`,
|
>`,
|
||||||
@@ -65,16 +60,16 @@ export class HassioUploadSnapshot extends LitElement {
|
|||||||
if (!["application/x-tar"].includes(file.type)) {
|
if (!["application/x-tar"].includes(file.type)) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Unsupported file format",
|
title: "Unsupported file format",
|
||||||
text: "Please choose a Home Assistant snapshot file (.tar)",
|
text: "Please choose a Home Assistant backup file (.tar)",
|
||||||
confirmText: "ok",
|
confirmText: "ok",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._uploading = true;
|
this._uploading = true;
|
||||||
try {
|
try {
|
||||||
const snapshot = await uploadSnapshot(this.hass, file);
|
const backup = await uploadBackup(this.hass, file);
|
||||||
fireEvent(this, "snapshot-uploaded", { snapshot: snapshot.data });
|
fireEvent(this, "backup-uploaded", { backup: backup.data });
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Upload failed",
|
title: "Upload failed",
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -88,6 +83,6 @@ export class HassioUploadSnapshot extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hassio-upload-snapshot": HassioUploadSnapshot;
|
"hassio-upload-backup": HassioUploadBackup;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,10 +11,10 @@ import "../../../src/components/ha-formfield";
|
|||||||
import "../../../src/components/ha-radio";
|
import "../../../src/components/ha-radio";
|
||||||
import type { HaRadio } from "../../../src/components/ha-radio";
|
import type { HaRadio } from "../../../src/components/ha-radio";
|
||||||
import {
|
import {
|
||||||
HassioFullSnapshotCreateParams,
|
HassioFullBackupCreateParams,
|
||||||
HassioPartialSnapshotCreateParams,
|
HassioPartialBackupCreateParams,
|
||||||
HassioSnapshotDetail,
|
HassioBackupDetail,
|
||||||
} from "../../../src/data/hassio/snapshot";
|
} from "../../../src/data/hassio/backup";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { PolymerChangedEvent } from "../../../src/polymer-types";
|
import { PolymerChangedEvent } from "../../../src/polymer-types";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
@@ -64,17 +64,17 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
|
|||||||
}))
|
}))
|
||||||
.sort((a, b) => (a.name > b.name ? 1 : -1));
|
.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||||
|
|
||||||
@customElement("supervisor-snapshot-content")
|
@customElement("supervisor-backup-content")
|
||||||
export class SupervisorSnapshotContent extends LitElement {
|
export class SupervisorBackupContent extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public localize?: LocalizeFunc;
|
@property() public localize?: LocalizeFunc;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor?: Supervisor;
|
@property({ attribute: false }) public supervisor?: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public snapshot?: HassioSnapshotDetail;
|
@property({ attribute: false }) public backup?: HassioBackupDetail;
|
||||||
|
|
||||||
@property() public snapshotType: HassioSnapshotDetail["type"] = "full";
|
@property() public backupType: HassioBackupDetail["type"] = "full";
|
||||||
|
|
||||||
@property({ attribute: false }) public folders?: CheckboxItem[];
|
@property({ attribute: false }) public folders?: CheckboxItem[];
|
||||||
|
|
||||||
@@ -82,37 +82,35 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public homeAssistant = false;
|
@property({ type: Boolean }) public homeAssistant = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public snapshotHasPassword = false;
|
@property({ type: Boolean }) public backupHasPassword = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public onboarding = false;
|
@property({ type: Boolean }) public onboarding = false;
|
||||||
|
|
||||||
@property() public snapshotName = "";
|
@property() public backupName = "";
|
||||||
|
|
||||||
@property() public snapshotPassword = "";
|
@property() public backupPassword = "";
|
||||||
|
|
||||||
@property() public confirmSnapshotPassword = "";
|
@property() public confirmBackupPassword = "";
|
||||||
|
|
||||||
public willUpdate(changedProps) {
|
public willUpdate(changedProps) {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated) {
|
||||||
this.folders = _computeFolders(
|
this.folders = _computeFolders(
|
||||||
this.snapshot
|
this.backup
|
||||||
? this.snapshot.folders
|
? this.backup.folders
|
||||||
: ["homeassistant", "ssl", "share", "media", "addons/local"]
|
: ["homeassistant", "ssl", "share", "media", "addons/local"]
|
||||||
);
|
);
|
||||||
this.addons = _computeAddons(
|
this.addons = _computeAddons(
|
||||||
this.snapshot
|
this.backup ? this.backup.addons : this.supervisor?.supervisor.addons
|
||||||
? this.snapshot.addons
|
|
||||||
: this.supervisor?.supervisor.addons
|
|
||||||
);
|
);
|
||||||
this.snapshotType = this.snapshot?.type || "full";
|
this.backupType = this.backup?.type || "full";
|
||||||
this.snapshotName = this.snapshot?.name || "";
|
this.backupName = this.backup?.name || "";
|
||||||
this.snapshotHasPassword = this.snapshot?.protected || false;
|
this.backupHasPassword = this.backup?.protected || false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _localize = (string: string) =>
|
private _localize = (string: string) =>
|
||||||
this.supervisor?.localize(`snapshot.${string}`) ||
|
this.supervisor?.localize(`backup.${string}`) ||
|
||||||
this.localize!(`ui.panel.page-onboarding.restore.${string}`);
|
this.localize!(`ui.panel.page-onboarding.restore.${string}`);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -120,72 +118,70 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const foldersSection =
|
const foldersSection =
|
||||||
this.snapshotType === "partial" ? this._getSection("folders") : undefined;
|
this.backupType === "partial" ? this._getSection("folders") : undefined;
|
||||||
const addonsSection =
|
const addonsSection =
|
||||||
this.snapshotType === "partial" ? this._getSection("addons") : undefined;
|
this.backupType === "partial" ? this._getSection("addons") : undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.snapshot
|
${this.backup
|
||||||
? html`<div class="details">
|
? html`<div class="details">
|
||||||
${this.snapshot.type === "full"
|
${this.backup.type === "full"
|
||||||
? this._localize("full_snapshot")
|
? this._localize("full_backup")
|
||||||
: this._localize("partial_snapshot")}
|
: this._localize("partial_backup")}
|
||||||
(${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br />
|
(${Math.ceil(this.backup.size * 10) / 10 + " MB"})<br />
|
||||||
${this.hass
|
${this.hass
|
||||||
? formatDateTime(new Date(this.snapshot.date), this.hass.locale)
|
? formatDateTime(new Date(this.backup.date), this.hass.locale)
|
||||||
: this.snapshot.date}
|
: this.backup.date}
|
||||||
</div>`
|
</div>`
|
||||||
: html`<paper-input
|
: html`<paper-input
|
||||||
name="snapshotName"
|
name="backupName"
|
||||||
.label=${this.supervisor?.localize("snapshot.name") || "Name"}
|
.label=${this._localize("name")}
|
||||||
.value=${this.snapshotName}
|
.value=${this.backupName}
|
||||||
@value-changed=${this._handleTextValueChanged}
|
@value-changed=${this._handleTextValueChanged}
|
||||||
>
|
>
|
||||||
</paper-input>`}
|
</paper-input>`}
|
||||||
${!this.snapshot || this.snapshot.type === "full"
|
${!this.backup || this.backup.type === "full"
|
||||||
? html`<div class="sub-header">
|
? html`<div class="sub-header">
|
||||||
${!this.snapshot
|
${!this.backup
|
||||||
? this._localize("type")
|
? this._localize("type")
|
||||||
: this._localize("select_type")}
|
: this._localize("select_type")}
|
||||||
</div>
|
</div>
|
||||||
<div class="snapshot-types">
|
<div class="backup-types">
|
||||||
<ha-formfield .label=${this._localize("full_snapshot")}>
|
<ha-formfield .label=${this._localize("full_backup")}>
|
||||||
<ha-radio
|
<ha-radio
|
||||||
@change=${this._handleRadioValueChanged}
|
@change=${this._handleRadioValueChanged}
|
||||||
value="full"
|
value="full"
|
||||||
name="snapshotType"
|
name="backupType"
|
||||||
.checked=${this.snapshotType === "full"}
|
.checked=${this.backupType === "full"}
|
||||||
>
|
>
|
||||||
</ha-radio>
|
</ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield .label=${this._localize("partial_snapshot")}>
|
<ha-formfield .label=${this._localize("partial_backup")}>
|
||||||
<ha-radio
|
<ha-radio
|
||||||
@change=${this._handleRadioValueChanged}
|
@change=${this._handleRadioValueChanged}
|
||||||
value="partial"
|
value="partial"
|
||||||
name="snapshotType"
|
name="backupType"
|
||||||
.checked=${this.snapshotType === "partial"}
|
.checked=${this.backupType === "partial"}
|
||||||
>
|
>
|
||||||
</ha-radio>
|
</ha-radio>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
${this.snapshotType === "partial"
|
${this.backupType === "partial"
|
||||||
? html`<div class="partial-picker">
|
? html`<div class="partial-picker">
|
||||||
${this.snapshot && this.snapshot.homeassistant
|
${this.backup && this.backup.homeassistant
|
||||||
? html`
|
? html`
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${html`<supervisor-formfield-label
|
.label=${html`<supervisor-formfield-label
|
||||||
label="Home Assistant"
|
label="Home Assistant"
|
||||||
.iconPath=${mdiHomeAssistant}
|
.iconPath=${mdiHomeAssistant}
|
||||||
.version=${this.snapshot.homeassistant}
|
.version=${this.backup.homeassistant}
|
||||||
>
|
>
|
||||||
</supervisor-formfield-label>`}
|
</supervisor-formfield-label>`}
|
||||||
>
|
>
|
||||||
<ha-checkbox
|
<ha-checkbox
|
||||||
.checked=${this.homeAssistant}
|
.checked=${this.homeAssistant}
|
||||||
@click=${() => {
|
@click=${this.toggleHomeAssistant}
|
||||||
this.homeAssistant = !this.homeAssistant;
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
</ha-checkbox>
|
</ha-checkbox>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
@@ -233,38 +229,38 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</div> `
|
</div> `
|
||||||
: ""}
|
: ""}
|
||||||
${this.snapshotType === "partial" &&
|
${this.backupType === "partial" &&
|
||||||
(!this.snapshot || this.snapshotHasPassword)
|
(!this.backup || this.backupHasPassword)
|
||||||
? html`<hr />`
|
? html`<hr />`
|
||||||
: ""}
|
: ""}
|
||||||
${!this.snapshot
|
${!this.backup
|
||||||
? html`<ha-formfield
|
? html`<ha-formfield
|
||||||
class="password"
|
class="password"
|
||||||
.label=${this._localize("password_protection")}
|
.label=${this._localize("password_protection")}
|
||||||
>
|
>
|
||||||
<ha-checkbox
|
<ha-checkbox
|
||||||
.checked=${this.snapshotHasPassword}
|
.checked=${this.backupHasPassword}
|
||||||
@change=${this._toggleHasPassword}
|
@change=${this._toggleHasPassword}
|
||||||
>
|
>
|
||||||
</ha-checkbox>
|
</ha-checkbox>
|
||||||
</ha-formfield>`
|
</ha-formfield>`
|
||||||
: ""}
|
: ""}
|
||||||
${this.snapshotHasPassword
|
${this.backupHasPassword
|
||||||
? html`
|
? html`
|
||||||
<paper-input
|
<paper-input
|
||||||
.label=${this._localize("password")}
|
.label=${this._localize("password")}
|
||||||
type="password"
|
type="password"
|
||||||
name="snapshotPassword"
|
name="backupPassword"
|
||||||
.value=${this.snapshotPassword}
|
.value=${this.backupPassword}
|
||||||
@value-changed=${this._handleTextValueChanged}
|
@value-changed=${this._handleTextValueChanged}
|
||||||
>
|
>
|
||||||
</paper-input>
|
</paper-input>
|
||||||
${!this.snapshot
|
${!this.backup
|
||||||
? html` <paper-input
|
? html` <paper-input
|
||||||
.label=${this.supervisor?.localize("confirm_password")}
|
.label=${this._localize("confirm_password")}
|
||||||
type="password"
|
type="password"
|
||||||
name="confirmSnapshotPassword"
|
name="confirmBackupPassword"
|
||||||
.value=${this.confirmSnapshotPassword}
|
.value=${this.confirmBackupPassword}
|
||||||
@value-changed=${this._handleTextValueChanged}
|
@value-changed=${this._handleTextValueChanged}
|
||||||
>
|
>
|
||||||
</paper-input>`
|
</paper-input>`
|
||||||
@@ -274,6 +270,10 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleHomeAssistant() {
|
||||||
|
this.homeAssistant = !this.homeAssistant;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
.partial-picker ha-formfield {
|
.partial-picker ha-formfield {
|
||||||
@@ -307,7 +307,7 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
margin: 0 -14px -16px;
|
margin: 0 -14px -16px;
|
||||||
}
|
}
|
||||||
.snapshot-types {
|
.backup-types {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-left: -13px;
|
margin-left: -13px;
|
||||||
}
|
}
|
||||||
@@ -317,23 +317,23 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public snapshotDetails():
|
public backupDetails():
|
||||||
| HassioPartialSnapshotCreateParams
|
| HassioPartialBackupCreateParams
|
||||||
| HassioFullSnapshotCreateParams {
|
| HassioFullBackupCreateParams {
|
||||||
const data: any = {};
|
const data: any = {};
|
||||||
|
|
||||||
if (!this.snapshot) {
|
if (!this.backup) {
|
||||||
data.name = this.snapshotName || formatDate(new Date(), this.hass.locale);
|
data.name = this.backupName || formatDate(new Date(), this.hass.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.snapshotHasPassword) {
|
if (this.backupHasPassword) {
|
||||||
data.password = this.snapshotPassword;
|
data.password = this.backupPassword;
|
||||||
if (!this.snapshot) {
|
if (!this.backup) {
|
||||||
data.confirm_password = this.confirmSnapshotPassword;
|
data.confirm_password = this.confirmBackupPassword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.snapshotType === "full") {
|
if (this.backupType === "full") {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +415,7 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _toggleHasPassword(): void {
|
private _toggleHasPassword(): void {
|
||||||
this.snapshotHasPassword = !this.snapshotHasPassword;
|
this.backupHasPassword = !this.backupHasPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleSection(ev): void {
|
private _toggleSection(ev): void {
|
||||||
@@ -445,6 +445,6 @@ export class SupervisorSnapshotContent extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"supervisor-snapshot-content": SupervisorSnapshotContent;
|
"supervisor-backup-content": SupervisorBackupContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -20,10 +20,10 @@ class SupervisorMetric extends LitElement {
|
|||||||
<div slot="description" .title=${this.tooltip ?? ""}>
|
<div slot="description" .title=${this.tooltip ?? ""}>
|
||||||
<span class="value"> ${roundedValue} % </span>
|
<span class="value"> ${roundedValue} % </span>
|
||||||
<ha-bar
|
<ha-bar
|
||||||
class="${classMap({
|
class=${classMap({
|
||||||
"target-warning": roundedValue > 50,
|
"target-warning": roundedValue > 50,
|
||||||
"target-critical": roundedValue > 85,
|
"target-critical": roundedValue > 85,
|
||||||
})}"
|
})}
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
></ha-bar>
|
></ha-bar>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import { compare } from "../../../src/common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
@@ -33,7 +33,7 @@ class HassioAddons extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: this.supervisor.supervisor.addons
|
: this.supervisor.supervisor.addons
|
||||||
.sort((a, b) => compare(a.name, b.name))
|
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
||||||
.map(
|
.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
||||||
|
@@ -86,7 +86,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
"hassio/supervisor/update",
|
"hassio/supervisor/update",
|
||||||
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
||||||
)}
|
)}
|
||||||
${this.supervisor.host.features.includes("hassos")
|
${this.supervisor.host.features.includes("haos")
|
||||||
? this._renderUpdateCard(
|
? this._renderUpdateCard(
|
||||||
"Operating System",
|
"Operating System",
|
||||||
"os",
|
"os",
|
||||||
@@ -136,7 +136,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
<a href=${releaseNotesUrl} target="_blank" rel="noreferrer">
|
||||||
<mwc-button>
|
<mwc-button>
|
||||||
${this.supervisor.localize("common.release_notes")}
|
${this.supervisor.localize("common.release_notes")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
@@ -162,7 +162,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
supervisor: this.supervisor,
|
supervisor: this.supervisor,
|
||||||
name: "Home Assistant Core",
|
name: "Home Assistant Core",
|
||||||
version: this.supervisor.core.version_latest,
|
version: this.supervisor.core.version_latest,
|
||||||
snapshotParams: {
|
backupParams: {
|
||||||
name: `core_${this.supervisor.core.version}`,
|
name: `core_${this.supervisor.core.version}`,
|
||||||
folders: ["homeassistant"],
|
folders: ["homeassistant"],
|
||||||
homeassistant: true,
|
homeassistant: true,
|
||||||
@@ -206,7 +206,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
fireEvent(this, "supervisor-collection-refresh", {
|
fireEvent(this, "supervisor-collection-refresh", {
|
||||||
collection: item.key,
|
collection: item.key,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
// Only show an error if the status code was not expected (user behind proxy)
|
// Only show an error if the status code was not expected (user behind proxy)
|
||||||
// or no status at all(connection terminated)
|
// or no status at all(connection terminated)
|
||||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
|
@@ -3,22 +3,24 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
|
import "../../../../src/components/ha-icon-button";
|
||||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import "../../components/hassio-upload-snapshot";
|
import "../../components/hassio-upload-backup";
|
||||||
import { HassioSnapshotUploadDialogParams } from "./show-dialog-snapshot-upload";
|
import { HassioBackupUploadDialogParams } from "./show-dialog-backup-upload";
|
||||||
|
|
||||||
@customElement("dialog-hassio-snapshot-upload")
|
@customElement("dialog-hassio-backup-upload")
|
||||||
export class DialogHassioSnapshotUpload
|
export class DialogHassioBackupUpload
|
||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioSnapshotUploadDialogParams> {
|
implements HassDialog<HassioBackupUploadDialogParams>
|
||||||
|
{
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _params?: HassioSnapshotUploadDialogParams;
|
@state() private _params?: HassioBackupUploadDialogParams;
|
||||||
|
|
||||||
public async showDialog(
|
public async showDialog(
|
||||||
params: HassioSnapshotUploadDialogParams
|
params: HassioBackupUploadDialogParams
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
@@ -26,8 +28,8 @@ export class DialogHassioSnapshotUpload
|
|||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
if (this._params && !this._params.onboarding) {
|
if (this._params && !this._params.onboarding) {
|
||||||
if (this._params.reloadSnapshot) {
|
if (this._params.reloadBackup) {
|
||||||
this._params.reloadSnapshot();
|
this._params.reloadBackup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
@@ -50,23 +52,26 @@ export class DialogHassioSnapshotUpload
|
|||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title"> Upload snapshot </span>
|
<span slot="title"> Upload backup </span>
|
||||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
.label=${this.hass.localize("common.close")}
|
||||||
</mwc-icon-button>
|
.path=${mdiClose}
|
||||||
|
slot="actionItems"
|
||||||
|
dialogAction="cancel"
|
||||||
|
></ha-icon-button>
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
</div>
|
</div>
|
||||||
<hassio-upload-snapshot
|
<hassio-upload-backup
|
||||||
@snapshot-uploaded=${this._snapshotUploaded}
|
@backup-uploaded=${this._backupUploaded}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></hassio-upload-snapshot>
|
></hassio-upload-backup>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _snapshotUploaded(ev) {
|
private _backupUploaded(ev) {
|
||||||
const snapshot = ev.detail.snapshot;
|
const backup = ev.detail.backup;
|
||||||
this._params?.showSnapshot(snapshot.slug);
|
this._params?.showBackup(backup.slug);
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +98,6 @@ export class DialogHassioSnapshotUpload
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"dialog-hassio-snapshot-upload": DialogHassioSnapshotUpload;
|
"dialog-hassio-backup-upload": DialogHassioBackupUpload;
|
||||||
}
|
}
|
||||||
}
|
}
|
172
hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts → hassio/src/dialogs/backup/dialog-hassio-backup.ts
Executable file → Normal file
172
hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts → hassio/src/dialogs/backup/dialog-hassio-backup.ts
Executable file → Normal file
@@ -6,15 +6,16 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { slugify } from "../../../../src/common/string/slugify";
|
import { slugify } from "../../../../src/common/string/slugify";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import { getSignedPath } from "../../../../src/data/auth";
|
import { getSignedPath } from "../../../../src/data/auth";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
fetchHassioSnapshotInfo,
|
fetchHassioBackupInfo,
|
||||||
HassioSnapshotDetail,
|
HassioBackupDetail,
|
||||||
} from "../../../../src/data/hassio/snapshot";
|
} from "../../../../src/data/hassio/backup";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@@ -23,43 +24,46 @@ import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
|||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { fileDownload } from "../../../../src/util/file_download";
|
import { fileDownload } from "../../../../src/util/file_download";
|
||||||
import "../../components/supervisor-snapshot-content";
|
import "../../components/supervisor-backup-content";
|
||||||
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
|
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||||
import { HassioSnapshotDialogParams } from "./show-dialog-hassio-snapshot";
|
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
||||||
|
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||||
|
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
||||||
|
|
||||||
@customElement("dialog-hassio-snapshot")
|
@customElement("dialog-hassio-backup")
|
||||||
class HassioSnapshotDialog
|
class HassioBackupDialog
|
||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioSnapshotDialogParams> {
|
implements HassDialog<HassioBackupDialogParams>
|
||||||
|
{
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@state() private _snapshot?: HassioSnapshotDetail;
|
@state() private _backup?: HassioBackupDetail;
|
||||||
|
|
||||||
@state() private _dialogParams?: HassioSnapshotDialogParams;
|
@state() private _dialogParams?: HassioBackupDialogParams;
|
||||||
|
|
||||||
@state() private _restoringSnapshot = false;
|
@state() private _restoringBackup = false;
|
||||||
|
|
||||||
@query("supervisor-snapshot-content")
|
@query("supervisor-backup-content")
|
||||||
private _snapshotContent!: SupervisorSnapshotContent;
|
private _backupContent!: SupervisorBackupContent;
|
||||||
|
|
||||||
public async showDialog(params: HassioSnapshotDialogParams) {
|
public async showDialog(params: HassioBackupDialogParams) {
|
||||||
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
|
this._backup = await fetchHassioBackupInfo(this.hass, params.slug);
|
||||||
this._dialogParams = params;
|
this._dialogParams = params;
|
||||||
this._restoringSnapshot = false;
|
this._restoringBackup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
this._snapshot = undefined;
|
this._backup = undefined;
|
||||||
this._dialogParams = undefined;
|
this._dialogParams = undefined;
|
||||||
this._restoringSnapshot = false;
|
this._restoringBackup = false;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._dialogParams || !this._snapshot) {
|
if (!this._dialogParams || !this._backup) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
@@ -71,26 +75,31 @@ class HassioSnapshotDialog
|
|||||||
>
|
>
|
||||||
<div slot="heading">
|
<div slot="heading">
|
||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title">${this._snapshot.name}</span>
|
<span slot="title">${this._backup.name}</span>
|
||||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
.label=${this.hass.localize("common.close")}
|
||||||
</mwc-icon-button>
|
.path=${mdiClose}
|
||||||
|
slot="actionItems"
|
||||||
|
dialogAction="cancel"
|
||||||
|
></ha-icon-button>
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
</div>
|
</div>
|
||||||
${this._restoringSnapshot
|
${this._restoringBackup
|
||||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||||
: html`<supervisor-snapshot-content
|
: html`<supervisor-backup-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.supervisor=${this._dialogParams.supervisor}
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
.snapshot=${this._snapshot}
|
.backup=${this._backup}
|
||||||
.onboarding=${this._dialogParams.onboarding || false}
|
.onboarding=${this._dialogParams.onboarding || false}
|
||||||
.localize=${this._dialogParams.localize}
|
.localize=${this._dialogParams.localize}
|
||||||
>
|
>
|
||||||
</supervisor-snapshot-content>`}
|
</supervisor-backup-content>`}
|
||||||
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
|
|
||||||
<mwc-button
|
<mwc-button
|
||||||
.disabled=${this._restoringSnapshot}
|
.disabled=${this._restoringBackup}
|
||||||
slot="secondaryAction"
|
slot="secondaryAction"
|
||||||
@click=${this._restoreClicked}
|
@click=${this._restoreClicked}
|
||||||
>
|
>
|
||||||
@@ -102,13 +111,15 @@ class HassioSnapshotDialog
|
|||||||
fixed
|
fixed
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@action=${this._handleMenuAction}
|
@action=${this._handleMenuAction}
|
||||||
@closed=${(ev: Event) => ev.stopPropagation()}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<mwc-icon-button slot="trigger" alt="menu">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
.label=${this.hass.localize("common.menu")}
|
||||||
</mwc-icon-button>
|
.path=${mdiDotsVertical}
|
||||||
<mwc-list-item>Download Snapshot</mwc-list-item>
|
slot="trigger"
|
||||||
<mwc-list-item class="error">Delete Snapshot</mwc-list-item>
|
></ha-icon-button>
|
||||||
|
<mwc-list-item>Download Backup</mwc-list-item>
|
||||||
|
<mwc-list-item class="error">Delete Backup</mwc-list-item>
|
||||||
</ha-button-menu>`
|
</ha-button-menu>`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
@@ -120,9 +131,6 @@ class HassioSnapshotDialog
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-svg-icon {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
ha-circular-progress {
|
ha-circular-progress {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -149,30 +157,30 @@ class HassioSnapshotDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _restoreClicked() {
|
private async _restoreClicked() {
|
||||||
const snapshotDetails = this._snapshotContent.snapshotDetails();
|
const backupDetails = this._backupContent.backupDetails();
|
||||||
this._restoringSnapshot = true;
|
this._restoringBackup = true;
|
||||||
if (this._snapshotContent.snapshotType === "full") {
|
if (this._backupContent.backupType === "full") {
|
||||||
await this._fullRestoreClicked(snapshotDetails);
|
await this._fullRestoreClicked(backupDetails);
|
||||||
} else {
|
} else {
|
||||||
await this._partialRestoreClicked(snapshotDetails);
|
await this._partialRestoreClicked(backupDetails);
|
||||||
}
|
}
|
||||||
this._restoringSnapshot = false;
|
this._restoringBackup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _partialRestoreClicked(snapshotDetails) {
|
private async _partialRestoreClicked(backupDetails) {
|
||||||
if (
|
if (
|
||||||
this._dialogParams?.supervisor !== undefined &&
|
this._dialogParams?.supervisor !== undefined &&
|
||||||
this._dialogParams?.supervisor.info.state !== "running"
|
this._dialogParams?.supervisor.info.state !== "running"
|
||||||
) {
|
) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore snapshot",
|
title: "Could not restore backup",
|
||||||
text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: "Are you sure you want partially to restore this snapshot?",
|
title: "Are you sure you want partially to restore this backup?",
|
||||||
confirmText: "restore",
|
confirmText: "restore",
|
||||||
dismissText: "cancel",
|
dismissText: "cancel",
|
||||||
}))
|
}))
|
||||||
@@ -185,8 +193,12 @@ class HassioSnapshotDialog
|
|||||||
.callApi(
|
.callApi(
|
||||||
"POST",
|
"POST",
|
||||||
|
|
||||||
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
|
`hassio/${
|
||||||
snapshotDetails
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
|
? "backups"
|
||||||
|
: "snapshots"
|
||||||
|
}/${this._backup!.slug}/restore/partial`,
|
||||||
|
backupDetails
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
@@ -198,29 +210,29 @@ class HassioSnapshotDialog
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(snapshotDetails),
|
body: JSON.stringify(backupDetails),
|
||||||
});
|
});
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fullRestoreClicked(snapshotDetails) {
|
private async _fullRestoreClicked(backupDetails) {
|
||||||
if (
|
if (
|
||||||
this._dialogParams?.supervisor !== undefined &&
|
this._dialogParams?.supervisor !== undefined &&
|
||||||
this._dialogParams?.supervisor.info.state !== "running"
|
this._dialogParams?.supervisor.info.state !== "running"
|
||||||
) {
|
) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore snapshot",
|
title: "Could not restore backup",
|
||||||
text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title:
|
title:
|
||||||
"Are you sure you want to wipe your system and restore this snapshot?",
|
"Are you sure you want to wipe your system and restore this backup?",
|
||||||
confirmText: "restore",
|
confirmText: "restore",
|
||||||
dismissText: "cancel",
|
dismissText: "cancel",
|
||||||
}))
|
}))
|
||||||
@@ -232,8 +244,12 @@ class HassioSnapshotDialog
|
|||||||
this.hass
|
this.hass
|
||||||
.callApi(
|
.callApi(
|
||||||
"POST",
|
"POST",
|
||||||
`hassio/snapshots/${this._snapshot!.slug}/restore/full`,
|
`hassio/${
|
||||||
snapshotDetails
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
|
? "backups"
|
||||||
|
: "snapshots"
|
||||||
|
}/${this._backup!.slug}/restore/full`,
|
||||||
|
backupDetails
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
@@ -245,9 +261,9 @@ class HassioSnapshotDialog
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(snapshotDetails),
|
body: JSON.stringify(backupDetails),
|
||||||
});
|
});
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
@@ -256,7 +272,7 @@ class HassioSnapshotDialog
|
|||||||
private async _deleteClicked() {
|
private async _deleteClicked() {
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: "Are you sure you want to delete this snapshot?",
|
title: "Are you sure you want to delete this backup?",
|
||||||
confirmText: "delete",
|
confirmText: "delete",
|
||||||
dismissText: "cancel",
|
dismissText: "cancel",
|
||||||
}))
|
}))
|
||||||
@@ -266,7 +282,14 @@ class HassioSnapshotDialog
|
|||||||
|
|
||||||
this.hass
|
this.hass
|
||||||
|
|
||||||
.callApi("POST", `hassio/snapshots/${this._snapshot!.slug}/remove`)
|
.callApi(
|
||||||
|
atLeastVersion(this.hass.config.version, 2021, 9) ? "DELETE" : "POST",
|
||||||
|
`hassio/${
|
||||||
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
|
? `backups/${this._backup!.slug}`
|
||||||
|
: `snapshots/${this._backup!.slug}/remove`
|
||||||
|
}`
|
||||||
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
if (this._dialogParams!.onDelete) {
|
if (this._dialogParams!.onDelete) {
|
||||||
@@ -285,9 +308,13 @@ class HassioSnapshotDialog
|
|||||||
try {
|
try {
|
||||||
signedPath = await getSignedPath(
|
signedPath = await getSignedPath(
|
||||||
this.hass,
|
this.hass,
|
||||||
`/api/hassio/snapshots/${this._snapshot!.slug}/download`
|
`/api/hassio/${
|
||||||
|
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||||
|
? "backups"
|
||||||
|
: "snapshots"
|
||||||
|
}/${this._backup!.slug}/download`
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
@@ -297,8 +324,7 @@ class HassioSnapshotDialog
|
|||||||
if (window.location.href.includes("ui.nabu.casa")) {
|
if (window.location.href.includes("ui.nabu.casa")) {
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: "Potential slow download",
|
title: "Potential slow download",
|
||||||
text:
|
text: "Downloading backups over the Nabu Casa URL will take some time, it is recomended to use your local URL instead, do you want to continue?",
|
||||||
"Downloading snapshots over the Nabu Casa URL will take some time, it is recomended to use your local URL instead, do you want to continue?",
|
|
||||||
confirmText: "continue",
|
confirmText: "continue",
|
||||||
dismissText: "cancel",
|
dismissText: "cancel",
|
||||||
});
|
});
|
||||||
@@ -310,19 +336,19 @@ class HassioSnapshotDialog
|
|||||||
fileDownload(
|
fileDownload(
|
||||||
this,
|
this,
|
||||||
signedPath.path,
|
signedPath.path,
|
||||||
`home_assistant_snapshot_${slugify(this._computeName)}.tar`
|
`home_assistant_backup_${slugify(this._computeName)}.tar`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _computeName() {
|
private get _computeName() {
|
||||||
return this._snapshot
|
return this._backup
|
||||||
? this._snapshot.name || this._snapshot.slug
|
? this._backup.name || this._backup.slug
|
||||||
: "Unnamed snapshot";
|
: "Unnamed backup";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"dialog-hassio-snapshot": HassioSnapshotDialog;
|
"dialog-hassio-backup": HassioBackupDialog;
|
||||||
}
|
}
|
||||||
}
|
}
|
87
hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts → hassio/src/dialogs/backup/dialog-hassio-create-backup.ts
Executable file → Normal file
87
hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts → hassio/src/dialogs/backup/dialog-hassio-create-backup.ts
Executable file → Normal file
@@ -2,41 +2,42 @@ import "@material/mwc-button";
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
createHassioFullSnapshot,
|
createHassioFullBackup,
|
||||||
createHassioPartialSnapshot,
|
createHassioPartialBackup,
|
||||||
} from "../../../../src/data/hassio/snapshot";
|
} from "../../../../src/data/hassio/backup";
|
||||||
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import "../../components/supervisor-snapshot-content";
|
import "../../components/supervisor-backup-content";
|
||||||
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
|
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||||
import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot";
|
import { HassioCreateBackupDialogParams } from "./show-dialog-hassio-create-backup";
|
||||||
|
|
||||||
@customElement("dialog-hassio-create-snapshot")
|
@customElement("dialog-hassio-create-backup")
|
||||||
class HassioCreateSnapshotDialog extends LitElement {
|
class HassioCreateBackupDialog extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _dialogParams?: HassioCreateSnapshotDialogParams;
|
@state() private _dialogParams?: HassioCreateBackupDialogParams;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@state() private _creatingSnapshot = false;
|
@state() private _creatingBackup = false;
|
||||||
|
|
||||||
@query("supervisor-snapshot-content")
|
@query("supervisor-backup-content")
|
||||||
private _snapshotContent!: SupervisorSnapshotContent;
|
private _backupContent!: SupervisorBackupContent;
|
||||||
|
|
||||||
public showDialog(params: HassioCreateSnapshotDialogParams) {
|
public showDialog(params: HassioCreateBackupDialogParams) {
|
||||||
this._dialogParams = params;
|
this._dialogParams = params;
|
||||||
this._creatingSnapshot = false;
|
this._creatingBackup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
this._dialogParams = undefined;
|
this._dialogParams = undefined;
|
||||||
this._creatingSnapshot = false;
|
this._creatingBackup = false;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
@@ -52,82 +53,84 @@ class HassioCreateSnapshotDialog extends LitElement {
|
|||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._dialogParams.supervisor.localize("snapshot.create_snapshot")
|
this._dialogParams.supervisor.localize("backup.create_backup")
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${this._creatingSnapshot
|
${this._creatingBackup
|
||||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||||
: html`<supervisor-snapshot-content
|
: html`<supervisor-backup-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.supervisor=${this._dialogParams.supervisor}
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
>
|
>
|
||||||
</supervisor-snapshot-content>`}
|
</supervisor-backup-content>`}
|
||||||
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
${this._dialogParams.supervisor.localize("common.close")}
|
${this._dialogParams.supervisor.localize("common.close")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
.disabled=${this._creatingSnapshot}
|
.disabled=${this._creatingBackup}
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@click=${this._createSnapshot}
|
@click=${this._createBackup}
|
||||||
>
|
>
|
||||||
${this._dialogParams.supervisor.localize("snapshot.create")}
|
${this._dialogParams.supervisor.localize("backup.create")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createSnapshot(): Promise<void> {
|
private async _createBackup(): Promise<void> {
|
||||||
if (this._dialogParams!.supervisor.info.state !== "running") {
|
if (this._dialogParams!.supervisor.info.state !== "running") {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this._dialogParams!.supervisor.localize(
|
title: this._dialogParams!.supervisor.localize(
|
||||||
"snapshot.could_not_create"
|
"backup.could_not_create"
|
||||||
),
|
),
|
||||||
text: this._dialogParams!.supervisor.localize(
|
text: this._dialogParams!.supervisor.localize(
|
||||||
"snapshot.create_blocked_not_running",
|
"backup.create_blocked_not_running",
|
||||||
"state",
|
"state",
|
||||||
this._dialogParams!.supervisor.info.state
|
this._dialogParams!.supervisor.info.state
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const snapshotDetails = this._snapshotContent.snapshotDetails();
|
const backupDetails = this._backupContent.backupDetails();
|
||||||
this._creatingSnapshot = true;
|
this._creatingBackup = true;
|
||||||
|
|
||||||
this._error = "";
|
this._error = "";
|
||||||
if (snapshotDetails.password && !snapshotDetails.password.length) {
|
if (backupDetails.password && !backupDetails.password.length) {
|
||||||
this._error = this._dialogParams!.supervisor.localize(
|
this._error = this._dialogParams!.supervisor.localize(
|
||||||
"snapshot.enter_password"
|
"backup.enter_password"
|
||||||
);
|
);
|
||||||
this._creatingSnapshot = false;
|
this._creatingBackup = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
snapshotDetails.password &&
|
backupDetails.password &&
|
||||||
snapshotDetails.password !== snapshotDetails.confirm_password
|
backupDetails.password !== backupDetails.confirm_password
|
||||||
) {
|
) {
|
||||||
this._error = this._dialogParams!.supervisor.localize(
|
this._error = this._dialogParams!.supervisor.localize(
|
||||||
"snapshot.passwords_not_matching"
|
"backup.passwords_not_matching"
|
||||||
);
|
);
|
||||||
this._creatingSnapshot = false;
|
this._creatingBackup = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete snapshotDetails.confirm_password;
|
delete backupDetails.confirm_password;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this._snapshotContent.snapshotType === "full") {
|
if (this._backupContent.backupType === "full") {
|
||||||
await createHassioFullSnapshot(this.hass, snapshotDetails);
|
await createHassioFullBackup(this.hass, backupDetails);
|
||||||
} else {
|
} else {
|
||||||
await createHassioPartialSnapshot(this.hass, snapshotDetails);
|
await createHassioPartialBackup(this.hass, backupDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dialogParams!.onCreate();
|
this._dialogParams!.onCreate();
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
}
|
}
|
||||||
this._creatingSnapshot = false;
|
this._creatingBackup = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
@@ -146,6 +149,6 @@ class HassioCreateSnapshotDialog extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"dialog-hassio-create-snapshot": HassioCreateSnapshotDialog;
|
"dialog-hassio-create-backup": HassioCreateBackupDialog;
|
||||||
}
|
}
|
||||||
}
|
}
|
19
hassio/src/dialogs/backup/show-dialog-backup-upload.ts
Normal file
19
hassio/src/dialogs/backup/show-dialog-backup-upload.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "./dialog-hassio-backup-upload";
|
||||||
|
|
||||||
|
export interface HassioBackupUploadDialogParams {
|
||||||
|
showBackup: (slug: string) => void;
|
||||||
|
reloadBackup?: () => Promise<void>;
|
||||||
|
onboarding?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showBackupUploadDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: HassioBackupUploadDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-hassio-backup-upload",
|
||||||
|
dialogImport: () => import("./dialog-hassio-backup-upload"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@@ -2,7 +2,7 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
|
|||||||
import { LocalizeFunc } from "../../../../src/common/translations/localize";
|
import { LocalizeFunc } from "../../../../src/common/translations/localize";
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
export interface HassioSnapshotDialogParams {
|
export interface HassioBackupDialogParams {
|
||||||
slug: string;
|
slug: string;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
onboarding?: boolean;
|
onboarding?: boolean;
|
||||||
@@ -10,13 +10,13 @@ export interface HassioSnapshotDialogParams {
|
|||||||
localize?: LocalizeFunc;
|
localize?: LocalizeFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showHassioSnapshotDialog = (
|
export const showHassioBackupDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
dialogParams: HassioSnapshotDialogParams
|
dialogParams: HassioBackupDialogParams
|
||||||
): void => {
|
): void => {
|
||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-hassio-snapshot",
|
dialogTag: "dialog-hassio-backup",
|
||||||
dialogImport: () => import("./dialog-hassio-snapshot"),
|
dialogImport: () => import("./dialog-hassio-backup"),
|
||||||
dialogParams,
|
dialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
@@ -1,18 +1,18 @@
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
export interface HassioCreateSnapshotDialogParams {
|
export interface HassioCreateBackupDialogParams {
|
||||||
supervisor: Supervisor;
|
supervisor: Supervisor;
|
||||||
onCreate: () => void;
|
onCreate: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showHassioCreateSnapshotDialog = (
|
export const showHassioCreateBackupDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
dialogParams: HassioCreateSnapshotDialogParams
|
dialogParams: HassioCreateBackupDialogParams
|
||||||
): void => {
|
): void => {
|
||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-hassio-create-snapshot",
|
dialogTag: "dialog-hassio-create-backup",
|
||||||
dialogImport: () => import("./dialog-hassio-create-snapshot"),
|
dialogImport: () => import("./dialog-hassio-create-backup"),
|
||||||
dialogParams,
|
dialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
180
hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
Normal file
180
hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-circular-progress";
|
||||||
|
import "../../../../src/components/ha-markdown";
|
||||||
|
import {
|
||||||
|
extractApiErrorMessage,
|
||||||
|
ignoreSupervisorError,
|
||||||
|
} from "../../../../src/data/hassio/common";
|
||||||
|
import {
|
||||||
|
DatadiskList,
|
||||||
|
listDatadisks,
|
||||||
|
moveDatadisk,
|
||||||
|
} from "../../../../src/data/hassio/host";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
|
||||||
|
|
||||||
|
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
|
||||||
|
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
|
||||||
|
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
|
||||||
|
const rebootTime = (supervisor.host.startup_time * 4) / 60;
|
||||||
|
return Math.ceil((moveTime + rebootTime) / 10) * 10;
|
||||||
|
});
|
||||||
|
|
||||||
|
@customElement("dialog-hassio-datadisk")
|
||||||
|
class HassioDatadiskDialog extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private dialogParams?: HassioDatatiskDialogParams;
|
||||||
|
|
||||||
|
@state() private selectedDevice?: string;
|
||||||
|
|
||||||
|
@state() private devices?: DatadiskList["devices"];
|
||||||
|
|
||||||
|
@state() private moving = false;
|
||||||
|
|
||||||
|
public showDialog(params: HassioDatatiskDialogParams) {
|
||||||
|
this.dialogParams = params;
|
||||||
|
listDatadisks(this.hass).then((data) => {
|
||||||
|
this.devices = data.devices;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this.dialogParams = undefined;
|
||||||
|
this.selectedDevice = undefined;
|
||||||
|
this.devices = undefined;
|
||||||
|
this.moving = false;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.dialogParams) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
escapeKeyAction
|
||||||
|
.heading=${this.moving
|
||||||
|
? this.dialogParams.supervisor.localize("dialog.datadisk_move.moving")
|
||||||
|
: this.dialogParams.supervisor.localize("dialog.datadisk_move.title")}
|
||||||
|
@closed=${this.closeDialog}
|
||||||
|
?hideActions=${this.moving}
|
||||||
|
>
|
||||||
|
${this.moving
|
||||||
|
? html` <ha-circular-progress alt="Moving" size="large" active>
|
||||||
|
</ha-circular-progress>
|
||||||
|
<p class="progress-text">
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.moving_desc"
|
||||||
|
)}
|
||||||
|
</p>`
|
||||||
|
: html` ${this.devices?.length
|
||||||
|
? html`
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.description",
|
||||||
|
{
|
||||||
|
current_path: this.dialogParams.supervisor.os.data_disk,
|
||||||
|
time: calculateMoveTime(this.dialogParams.supervisor),
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<paper-dropdown-menu
|
||||||
|
.label=${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.select_device"
|
||||||
|
)}
|
||||||
|
@value-changed=${this._select_device}
|
||||||
|
>
|
||||||
|
<paper-listbox slot="dropdown-content">
|
||||||
|
${this.devices.map(
|
||||||
|
(device) => html`<paper-item>${device}</paper-item>`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
`
|
||||||
|
: this.devices === undefined
|
||||||
|
? this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.loading_devices"
|
||||||
|
)
|
||||||
|
: this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.no_devices"
|
||||||
|
)}
|
||||||
|
|
||||||
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.cancel"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
|
||||||
|
<mwc-button
|
||||||
|
.disabled=${!this.selectedDevice}
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._moveDatadisk}
|
||||||
|
>
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.move"
|
||||||
|
)}
|
||||||
|
</mwc-button>`}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _select_device(event) {
|
||||||
|
this.selectedDevice = event.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _moveDatadisk() {
|
||||||
|
this.moving = true;
|
||||||
|
try {
|
||||||
|
await moveDatadisk(this.hass, this.selectedDevice!);
|
||||||
|
} catch (err: any) {
|
||||||
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.dialogParams!.supervisor.localize(
|
||||||
|
"system.host.failed_to_move"
|
||||||
|
),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
paper-dropdown-menu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
ha-circular-progress {
|
||||||
|
display: block;
|
||||||
|
margin: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-hassio-datadisk": HassioDatadiskDialog;
|
||||||
|
}
|
||||||
|
}
|
17
hassio/src/dialogs/datadisk/show-dialog-hassio-datadisk.ts
Normal file
17
hassio/src/dialogs/datadisk/show-dialog-hassio-datadisk.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
|
export interface HassioDatatiskDialogParams {
|
||||||
|
supervisor: Supervisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showHassioDatadiskDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: HassioDatatiskDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-hassio-datadisk",
|
||||||
|
dialogImport: () => import("./dialog-hassio-datadisk"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@@ -4,9 +4,10 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/common/search/search-input";
|
import "../../../../src/common/search/search-input";
|
||||||
import { compare } from "../../../../src/common/string/compare";
|
import { stringCompare } from "../../../../src/common/string/compare";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-expansion-panel";
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
|
import "../../../../src/components/ha-icon-button";
|
||||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||||
import { dump } from "../../../../src/resources/js-yaml-dump";
|
import { dump } from "../../../../src/resources/js-yaml-dump";
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
@@ -27,7 +28,7 @@ const _filterDevices = memoizeOne(
|
|||||||
.toLocaleLowerCase()
|
.toLocaleLowerCase()
|
||||||
.includes(filter))
|
.includes(filter))
|
||||||
)
|
)
|
||||||
.sort((a, b) => compare(a.name, b.name))
|
.sort((a, b) => stringCompare(a.name, b.name))
|
||||||
);
|
);
|
||||||
|
|
||||||
@customElement("dialog-hassio-hardware")
|
@customElement("dialog-hassio-hardware")
|
||||||
@@ -70,10 +71,13 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
<h2>
|
<h2>
|
||||||
${this._dialogParams.supervisor.localize("dialog.hardware.title")}
|
${this._dialogParams.supervisor.localize("dialog.hardware.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<mwc-icon-button dialogAction="close">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
.label=${this.hass.localize("common.close")}
|
||||||
</mwc-icon-button>
|
.path=${mdiClose}
|
||||||
|
dialogAction="close"
|
||||||
|
></ha-icon-button>
|
||||||
<search-input
|
<search-input
|
||||||
|
.hass=${this.hass}
|
||||||
autofocus
|
autofocus
|
||||||
no-label-float
|
no-label-float
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
@@ -141,7 +145,7 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
mwc-icon-button {
|
ha-icon-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
@@ -61,10 +61,6 @@ class HassioMarkdownDialog extends LitElement {
|
|||||||
app-toolbar [main-title] {
|
app-toolbar [main-title] {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
paper-checkbox {
|
|
||||||
display: block;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
ha-paper-dialog {
|
ha-paper-dialog {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-icon-button";
|
|
||||||
import "@material/mwc-list/mwc-list";
|
import "@material/mwc-list/mwc-list";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@material/mwc-tab";
|
import "@material/mwc-tab";
|
||||||
@@ -10,14 +9,15 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { cache } from "lit/directives/cache";
|
import { cache } from "lit/directives/cache";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-expansion-panel";
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
import "../../../../src/components/ha-header-bar";
|
import "../../../../src/components/ha-header-bar";
|
||||||
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/ha-radio";
|
import "../../../../src/components/ha-radio";
|
||||||
import "../../../../src/components/ha-related-items";
|
import "../../../../src/components/ha-related-items";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
AccessPoints,
|
AccessPoints,
|
||||||
@@ -41,7 +41,8 @@ const IP_VERSIONS = ["ipv4", "ipv6"];
|
|||||||
@customElement("dialog-hassio-network")
|
@customElement("dialog-hassio-network")
|
||||||
export class DialogHassioNetwork
|
export class DialogHassioNetwork
|
||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioNetworkDialogParams> {
|
implements HassDialog<HassioNetworkDialogParams>
|
||||||
|
{
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
@@ -102,12 +103,15 @@ export class DialogHassioNetwork
|
|||||||
<span slot="title">
|
<span slot="title">
|
||||||
${this.supervisor.localize("dialog.network.title")}
|
${this.supervisor.localize("dialog.network.title")}
|
||||||
</span>
|
</span>
|
||||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
<ha-icon-button
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
.label=${this.hass.localize("common.close")}
|
||||||
</mwc-icon-button>
|
.path=${mdiClose}
|
||||||
|
slot="actionItems"
|
||||||
|
dialogAction="cancel"
|
||||||
|
></ha-icon-button>
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
${this._interfaces.length > 1
|
${this._interfaces.length > 1
|
||||||
? html` <mwc-tab-bar
|
? html`<mwc-tab-bar
|
||||||
.activeIndex=${this._curTabIndex}
|
.activeIndex=${this._curTabIndex}
|
||||||
@MDCTabBar:activated=${this._handleTabActivated}
|
@MDCTabBar:activated=${this._handleTabActivated}
|
||||||
>${this._interfaces.map(
|
>${this._interfaces.map(
|
||||||
@@ -250,9 +254,9 @@ export class DialogHassioNetwork
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this._dirty
|
${this._dirty
|
||||||
? html`<div class="warning">
|
? html`<ha-alert alert-type="warning">
|
||||||
${this.supervisor.localize("dialog.network.warning")}
|
${this.supervisor.localize("dialog.network.warning")}
|
||||||
</div>`
|
</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
@@ -285,7 +289,7 @@ export class DialogHassioNetwork
|
|||||||
this.hass,
|
this.hass,
|
||||||
this._interface.interface
|
this._interface.interface
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to scan for accesspoints",
|
title: "Failed to scan for accesspoints",
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -446,7 +450,7 @@ export class DialogHassioNetwork
|
|||||||
this._interface!.interface,
|
this._interface!.interface,
|
||||||
interfaceOptions
|
interfaceOptions
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("dialog.network.failed_to_change"),
|
title: this.supervisor.localize("dialog.network.failed_to_change"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -492,7 +496,7 @@ export class DialogHassioNetwork
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleRadioValueChangedAp(ev: CustomEvent): void {
|
private _handleRadioValueChangedAp(ev: CustomEvent): void {
|
||||||
const value = ((ev.target as any).value as string) as
|
const value = (ev.target as any).value as string as
|
||||||
| "open"
|
| "open"
|
||||||
| "wep"
|
| "wep"
|
||||||
| "wpa-psk";
|
| "wpa-psk";
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDelete } from "@mdi/js";
|
import { mdiDelete } from "@mdi/js";
|
||||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
@@ -7,7 +6,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
addHassioDockerRegistry,
|
addHassioDockerRegistry,
|
||||||
@@ -110,16 +109,15 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
)}:
|
)}:
|
||||||
${entry.username}</span
|
${entry.username}</span
|
||||||
>
|
>
|
||||||
<mwc-icon-button
|
<ha-icon-button
|
||||||
.entry=${entry}
|
.entry=${entry}
|
||||||
.title=${this.supervisor.localize(
|
.label=${this.supervisor.localize(
|
||||||
"dialog.registries.remove"
|
"dialog.registries.remove"
|
||||||
)}
|
)}
|
||||||
|
.path=${mdiDelete}
|
||||||
slot="meta"
|
slot="meta"
|
||||||
@click=${this._removeRegistry}
|
@click=${this._removeRegistry}
|
||||||
>
|
></ha-icon-button>
|
||||||
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
@@ -161,9 +159,9 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
this.updateComplete.then(() =>
|
this.updateComplete.then(() =>
|
||||||
(this.shadowRoot?.querySelector(
|
(
|
||||||
"[dialogInitialFocus]"
|
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||||
) as HTMLElement)?.focus()
|
)?.focus()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +188,7 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
await addHassioDockerRegistry(this.hass, data);
|
await addHassioDockerRegistry(this.hass, data);
|
||||||
await this._loadRegistries();
|
await this._loadRegistries();
|
||||||
this._addingRegistry = false;
|
this._addingRegistry = false;
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -204,7 +202,7 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
try {
|
try {
|
||||||
await removeHassioDockerRegistry(this.hass, entry.registry);
|
await removeHassioDockerRegistry(this.hass, entry.registry);
|
||||||
await this._loadRegistries();
|
await this._loadRegistries();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("dialog.registries.failed_to_remove"),
|
title: this.supervisor.localize("dialog.registries.failed_to_remove"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
@@ -234,7 +232,7 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
mwc-button {
|
mwc-button {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
mwc-icon-button {
|
ha-icon-button {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
margin: -10px;
|
margin: -10px;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import { mdiDelete } from "@mdi/js";
|
import { mdiDelete } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
@@ -9,9 +8,11 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonsInfo,
|
fetchHassioAddonsInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
@@ -56,7 +57,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
|
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
|
||||||
repos
|
repos
|
||||||
.filter((repo) => repo.slug !== "core" && repo.slug !== "local")
|
.filter((repo) => repo.slug !== "core" && repo.slug !== "local")
|
||||||
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
||||||
);
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -75,7 +76,9 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
this._dialogParams!.supervisor.localize("dialog.repositories.title")
|
this._dialogParams!.supervisor.localize("dialog.repositories.title")
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${this._error ? html`<div class="error">${this._error}</div>` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
<div class="form">
|
<div class="form">
|
||||||
${repositories.length
|
${repositories.length
|
||||||
? repositories.map(
|
? repositories.map(
|
||||||
@@ -86,15 +89,14 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
<div secondary>${repo.maintainer}</div>
|
<div secondary>${repo.maintainer}</div>
|
||||||
<div secondary>${repo.url}</div>
|
<div secondary>${repo.url}</div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
<mwc-icon-button
|
<ha-icon-button
|
||||||
.slug=${repo.slug}
|
.slug=${repo.slug}
|
||||||
.title=${this._dialogParams!.supervisor.localize(
|
.label=${this._dialogParams!.supervisor.localize(
|
||||||
"dialog.repositories.remove"
|
"dialog.repositories.remove"
|
||||||
)}
|
)}
|
||||||
|
.path=${mdiDelete}
|
||||||
@click=${this._removeRepository}
|
@click=${this._removeRepository}
|
||||||
>
|
></ha-icon-button>
|
||||||
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
</paper-item>
|
</paper-item>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
@@ -161,9 +163,9 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
this.updateComplete.then(() =>
|
this.updateComplete.then(() =>
|
||||||
(this.shadowRoot?.querySelector(
|
(
|
||||||
"[dialogInitialFocus]"
|
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||||
) as HTMLElement)?.focus()
|
)?.focus()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +184,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
this._repositories = addonsinfo.repositories;
|
this._repositories = addonsinfo.repositories;
|
||||||
|
|
||||||
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,7 +206,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
await this._loadData();
|
await this._loadData();
|
||||||
|
|
||||||
input.value = "";
|
input.value = "";
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
}
|
}
|
||||||
this._processing = false;
|
this._processing = false;
|
||||||
@@ -226,7 +228,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
addons_repositories: newRepositories,
|
addons_repositories: newRepositories,
|
||||||
});
|
});
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +0,0 @@
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
|
||||||
import "./dialog-hassio-snapshot-upload";
|
|
||||||
|
|
||||||
export interface HassioSnapshotUploadDialogParams {
|
|
||||||
showSnapshot: (slug: string) => void;
|
|
||||||
reloadSnapshot?: () => Promise<void>;
|
|
||||||
onboarding?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const showSnapshotUploadDialog = (
|
|
||||||
element: HTMLElement,
|
|
||||||
dialogParams: HassioSnapshotUploadDialogParams
|
|
||||||
): void => {
|
|
||||||
fireEvent(element, "show-dialog", {
|
|
||||||
dialogTag: "dialog-hassio-snapshot-upload",
|
|
||||||
dialogImport: () => import("./dialog-hassio-snapshot-upload"),
|
|
||||||
dialogParams,
|
|
||||||
});
|
|
||||||
};
|
|
@@ -26,7 +26,7 @@ export const suggestAddonRestart = async (
|
|||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
try {
|
try {
|
||||||
await restartHassioAddon(hass, addon.slug);
|
await restartHassioAddon(hass, addon.slug);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
showAlertDialog(element, {
|
showAlertDialog(element, {
|
||||||
title: supervisor.localize(
|
title: supervisor.localize(
|
||||||
"common.failed_to_restart_name",
|
"common.failed_to_restart_name",
|
||||||
|
@@ -2,16 +2,16 @@ import "@material/mwc-button/mwc-button";
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
import "../../../../src/components/ha-circular-progress";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-settings-row";
|
import "../../../../src/components/ha-settings-row";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import {
|
import {
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
ignoreSupervisorError,
|
ignoreSupervisorError,
|
||||||
} from "../../../../src/data/hassio/common";
|
} from "../../../../src/data/hassio/common";
|
||||||
import { createHassioPartialSnapshot } from "../../../../src/data/hassio/snapshot";
|
import { createHassioPartialBackup } from "../../../../src/data/hassio/backup";
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { SupervisorDialogSupervisorUpdateParams } from "./show-dialog-update";
|
import { SupervisorDialogSupervisorUpdateParams } from "./show-dialog-update";
|
||||||
@@ -22,9 +22,9 @@ class DialogSupervisorUpdate extends LitElement {
|
|||||||
|
|
||||||
@state() private _opened = false;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@state() private _createSnapshot = true;
|
@state() private _createBackup = true;
|
||||||
|
|
||||||
@state() private _action: "snapshot" | "update" | null = null;
|
@state() private _action: "backup" | "update" | null = null;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class DialogSupervisorUpdate extends LitElement {
|
|||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._action = null;
|
this._action = null;
|
||||||
this._createSnapshot = true;
|
this._createBackup = true;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._dialogParams = undefined;
|
this._dialogParams = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
@@ -49,9 +49,9 @@ class DialogSupervisorUpdate extends LitElement {
|
|||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
this.updateComplete.then(() =>
|
this.updateComplete.then(() =>
|
||||||
(this.shadowRoot?.querySelector(
|
(
|
||||||
"[dialogInitialFocus]"
|
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||||
) as HTMLElement)?.focus()
|
)?.focus()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,20 +84,20 @@ class DialogSupervisorUpdate extends LitElement {
|
|||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
${this._dialogParams.supervisor.localize(
|
${this._dialogParams.supervisor.localize(
|
||||||
"dialog.update.snapshot"
|
"dialog.update.backup"
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this._dialogParams.supervisor.localize(
|
${this._dialogParams.supervisor.localize(
|
||||||
"dialog.update.create_snapshot",
|
"dialog.update.create_backup",
|
||||||
"name",
|
"name",
|
||||||
this._dialogParams.name
|
this._dialogParams.name
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._createSnapshot}
|
.checked=${this._createBackup}
|
||||||
haptic
|
haptic
|
||||||
@click=${this._toggleSnapshot}
|
@click=${this._toggleBackup}
|
||||||
>
|
>
|
||||||
</ha-switch>
|
</ha-switch>
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -123,29 +123,31 @@ class DialogSupervisorUpdate extends LitElement {
|
|||||||
this._dialogParams.version
|
this._dialogParams.version
|
||||||
)
|
)
|
||||||
: this._dialogParams.supervisor.localize(
|
: this._dialogParams.supervisor.localize(
|
||||||
"dialog.update.snapshotting",
|
"dialog.update.creating_backup",
|
||||||
"name",
|
"name",
|
||||||
this._dialogParams.name
|
this._dialogParams.name
|
||||||
)}
|
)}
|
||||||
</p>`}
|
</p>`}
|
||||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: ""}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleSnapshot() {
|
private _toggleBackup() {
|
||||||
this._createSnapshot = !this._createSnapshot;
|
this._createBackup = !this._createBackup;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _update() {
|
private async _update() {
|
||||||
if (this._createSnapshot) {
|
if (this._createBackup) {
|
||||||
this._action = "snapshot";
|
this._action = "backup";
|
||||||
try {
|
try {
|
||||||
await createHassioPartialSnapshot(
|
await createHassioPartialBackup(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._dialogParams!.snapshotParams
|
this._dialogParams!.backupParams
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
this._action = null;
|
this._action = null;
|
||||||
return;
|
return;
|
||||||
@@ -155,11 +157,11 @@ class DialogSupervisorUpdate extends LitElement {
|
|||||||
this._action = "update";
|
this._action = "update";
|
||||||
try {
|
try {
|
||||||
await this._dialogParams!.updateHandler!();
|
await this._dialogParams!.updateHandler!();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
|
this._action = null;
|
||||||
}
|
}
|
||||||
this._action = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ export interface SupervisorDialogSupervisorUpdateParams {
|
|||||||
supervisor: Supervisor;
|
supervisor: Supervisor;
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
snapshotParams: any;
|
backupParams: any;
|
||||||
updateHandler: () => Promise<void>;
|
updateHandler: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -121,7 +121,7 @@ export class HassioMain extends SupervisorBaseElement {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
themeName =
|
themeName =
|
||||||
((this.hass.selectedTheme as unknown) as string) ||
|
(this.hass.selectedTheme as unknown as string) ||
|
||||||
this.hass.themes.default_theme;
|
this.hass.themes.default_theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user