Compare commits
375 Commits
20200330.0
...
20200519.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fafad302ba | ||
![]() |
0c94ad46b2 | ||
![]() |
d9bb40f934 | ||
![]() |
bbc16b6bc8 | ||
![]() |
16154e9d8b | ||
![]() |
61bd536d7b | ||
![]() |
02c798a8bc | ||
![]() |
c37a691b9b | ||
![]() |
23c68d17e8 | ||
![]() |
0a7f610ad3 | ||
![]() |
c9e8bd2e5d | ||
![]() |
a66d2ca1b9 | ||
![]() |
d38a0f0366 | ||
![]() |
29759de021 | ||
![]() |
264759ddf0 | ||
![]() |
91b0bd5b5e | ||
![]() |
a0e2cc7a3a | ||
![]() |
9e1eb41cbe | ||
![]() |
c37eb023b0 | ||
![]() |
840948ba4a | ||
![]() |
8fbdd88b24 | ||
![]() |
512d35d2e0 | ||
![]() |
0321e55a42 | ||
![]() |
a1ee9ad48b | ||
![]() |
1ad1fd28f1 | ||
![]() |
007f8b50b9 | ||
![]() |
6fe8a87cca | ||
![]() |
5f46679d94 | ||
![]() |
18a3f212f3 | ||
![]() |
67a3f5d87b | ||
![]() |
c88439ba2f | ||
![]() |
67e17d4016 | ||
![]() |
b61cf60faf | ||
![]() |
5503853445 | ||
![]() |
259726f5be | ||
![]() |
bec42d941b | ||
![]() |
dcbbaf08f9 | ||
![]() |
349355584a | ||
![]() |
7ce0b34774 | ||
![]() |
dd894758a4 | ||
![]() |
2aa1eb97fd | ||
![]() |
4c43ae7b2f | ||
![]() |
34e516e0be | ||
![]() |
7bd3427e76 | ||
![]() |
6853db693a | ||
![]() |
252ce1e467 | ||
![]() |
221c12bd61 | ||
![]() |
12edd68874 | ||
![]() |
f469753fb1 | ||
![]() |
c894ecd0e6 | ||
![]() |
2153bc536c | ||
![]() |
86bbac430c | ||
![]() |
16ad8a3c01 | ||
![]() |
b2af91c83e | ||
![]() |
4c0810f530 | ||
![]() |
f70130e21f | ||
![]() |
6bb7b01d00 | ||
![]() |
51e7aaa805 | ||
![]() |
54704e53b3 | ||
![]() |
cc46797576 | ||
![]() |
7f1fb6f75f | ||
![]() |
2c2a1d204b | ||
![]() |
581fafdcc9 | ||
![]() |
10358abbec | ||
![]() |
de1ffe67b1 | ||
![]() |
eb2b24d57c | ||
![]() |
825db8a56a | ||
![]() |
577a21fc5c | ||
![]() |
1b2841eef9 | ||
![]() |
845511e322 | ||
![]() |
96ab057853 | ||
![]() |
f85cf0a238 | ||
![]() |
84a2676a9c | ||
![]() |
466a1af902 | ||
![]() |
ebbe7e805f | ||
![]() |
c861ee025e | ||
![]() |
6d0823328d | ||
![]() |
60be14dc77 | ||
![]() |
2d627819d9 | ||
![]() |
cf575f83f5 | ||
![]() |
79935b2d4c | ||
![]() |
d1cceb2013 | ||
![]() |
f5da130d51 | ||
![]() |
8768304ec5 | ||
![]() |
f10a5dcdbe | ||
![]() |
a27428ebcd | ||
![]() |
d19acf17c2 | ||
![]() |
1a0bf861ee | ||
![]() |
db906ad4d0 | ||
![]() |
75ed0f2f99 | ||
![]() |
29ed1144d5 | ||
![]() |
fa445d4066 | ||
![]() |
d10be4ef2d | ||
![]() |
7f66d5b8e9 | ||
![]() |
0c8cd680c2 | ||
![]() |
a7ba1977b4 | ||
![]() |
a960b39235 | ||
![]() |
3febf059ec | ||
![]() |
20203f7bdb | ||
![]() |
a399d76d06 | ||
![]() |
1ca097c5a0 | ||
![]() |
e83ede245d | ||
![]() |
05ac275780 | ||
![]() |
966c0bc06c | ||
![]() |
abf136dd63 | ||
![]() |
b6309cfd16 | ||
![]() |
b69d5e0fa3 | ||
![]() |
5084cde6b9 | ||
![]() |
ca1cc7ed0d | ||
![]() |
808a31db2b | ||
![]() |
44ad75aead | ||
![]() |
0961c9d05e | ||
![]() |
56754b4d43 | ||
![]() |
661779ad4e | ||
![]() |
cf37ebb652 | ||
![]() |
82741e490b | ||
![]() |
5fed28808e | ||
![]() |
321c0cfc84 | ||
![]() |
eba7cedaa6 | ||
![]() |
71492d0467 | ||
![]() |
9630a58ea7 | ||
![]() |
89f6f16ba2 | ||
![]() |
2d646da97f | ||
![]() |
39b5460598 | ||
![]() |
636429ccfa | ||
![]() |
e5abb95f5c | ||
![]() |
c631554eb0 | ||
![]() |
db07eeb916 | ||
![]() |
3216a46e76 | ||
![]() |
ae6243b7bf | ||
![]() |
df002d7a67 | ||
![]() |
e8a0632108 | ||
![]() |
0a92c28bac | ||
![]() |
d419547463 | ||
![]() |
da392912b3 | ||
![]() |
9ebee02727 | ||
![]() |
0bdcfcc42f | ||
![]() |
43623a30bc | ||
![]() |
99e73054a9 | ||
![]() |
108233f3b8 | ||
![]() |
8f2a7c95b3 | ||
![]() |
7fdd525dac | ||
![]() |
7ed24137eb | ||
![]() |
07cd30eaca | ||
![]() |
df8cf66e02 | ||
![]() |
f067bcd877 | ||
![]() |
e11ef55dd8 | ||
![]() |
fa8bd30e83 | ||
![]() |
04a2ff7506 | ||
![]() |
bc68e20041 | ||
![]() |
786da25b9f | ||
![]() |
c3832d56c3 | ||
![]() |
79d1a2f458 | ||
![]() |
f16a674a39 | ||
![]() |
6e38a80efc | ||
![]() |
6a3a1297ad | ||
![]() |
5ca63a8052 | ||
![]() |
8b04df093c | ||
![]() |
d2a5494335 | ||
![]() |
de545e90e2 | ||
![]() |
57ab7e829b | ||
![]() |
6847830575 | ||
![]() |
2084ecc4c6 | ||
![]() |
b0c27e587e | ||
![]() |
1687d90d02 | ||
![]() |
dfd9bf3c64 | ||
![]() |
5a25b9c2f1 | ||
![]() |
bf68101754 | ||
![]() |
487bd8d3fc | ||
![]() |
3ba9c931b9 | ||
![]() |
8484f7595a | ||
![]() |
ee889d59d4 | ||
![]() |
ae10330844 | ||
![]() |
f3e88f6f2e | ||
![]() |
2abfd0392d | ||
![]() |
462c1f94d6 | ||
![]() |
f4710891d0 | ||
![]() |
1fdb6b8034 | ||
![]() |
8029c3d672 | ||
![]() |
61fdee14c6 | ||
![]() |
117e4e468f | ||
![]() |
869ec113f4 | ||
![]() |
00e7b93011 | ||
![]() |
03e581870a | ||
![]() |
b04fe141ac | ||
![]() |
b0168fbb85 | ||
![]() |
75ba343b5e | ||
![]() |
88217473f7 | ||
![]() |
01e5dfc9b3 | ||
![]() |
58869677bd | ||
![]() |
032b01aec1 | ||
![]() |
a47c3fa854 | ||
![]() |
7c6ba1a782 | ||
![]() |
0d08f5413b | ||
![]() |
949cc17a9e | ||
![]() |
607c1a1ef0 | ||
![]() |
db2dab8227 | ||
![]() |
eb4ba4fc78 | ||
![]() |
bf888b1547 | ||
![]() |
c468aab9b2 | ||
![]() |
a0dae802f2 | ||
![]() |
14330fbd93 | ||
![]() |
bf55be7f7f | ||
![]() |
e10987a705 | ||
![]() |
355f40d740 | ||
![]() |
2503fabe1d | ||
![]() |
32c7c0b4f0 | ||
![]() |
301a964a65 | ||
![]() |
49f2fd2af7 | ||
![]() |
4f518cac2c | ||
![]() |
8420f73919 | ||
![]() |
1ef2d3c7f2 | ||
![]() |
fc20fd32f1 | ||
![]() |
13a8bf6993 | ||
![]() |
b2f424a6f8 | ||
![]() |
a9d927551c | ||
![]() |
fdf7b516a0 | ||
![]() |
9a00078169 | ||
![]() |
1dfb632fc4 | ||
![]() |
1c1f9a6a89 | ||
![]() |
2b76b3887e | ||
![]() |
a49c84032b | ||
![]() |
c72bb5b22b | ||
![]() |
24a844dddc | ||
![]() |
ae903973a7 | ||
![]() |
82442bb5ec | ||
![]() |
8786302190 | ||
![]() |
d27a17cf8e | ||
![]() |
b4b90ca59d | ||
![]() |
ecd967a68d | ||
![]() |
8812340768 | ||
![]() |
659da7bf80 | ||
![]() |
91eabf3c38 | ||
![]() |
e355c81e41 | ||
![]() |
d45a674652 | ||
![]() |
f91b46e88c | ||
![]() |
f57754212c | ||
![]() |
97c454aa0d | ||
![]() |
b516f10a35 | ||
![]() |
0ccc788148 | ||
![]() |
5c941e0afb | ||
![]() |
492e4d2df4 | ||
![]() |
66f33ad497 | ||
![]() |
ff81536463 | ||
![]() |
ba0cba1a2b | ||
![]() |
fc7771ec13 | ||
![]() |
4c3069e5b7 | ||
![]() |
ed54a185e4 | ||
![]() |
4f81085d0f | ||
![]() |
8383caf6a6 | ||
![]() |
1b9f224569 | ||
![]() |
05495af74f | ||
![]() |
d62da5e924 | ||
![]() |
57d72bd80f | ||
![]() |
681c05c323 | ||
![]() |
3d20d8b208 | ||
![]() |
4f9d77b906 | ||
![]() |
82f80db558 | ||
![]() |
b17490f0de | ||
![]() |
d33c0b4bd1 | ||
![]() |
7f528c90c3 | ||
![]() |
c4c4782429 | ||
![]() |
5414092309 | ||
![]() |
b632ea6f86 | ||
![]() |
bd98ce8c25 | ||
![]() |
0c1714ef78 | ||
![]() |
ee278f111f | ||
![]() |
47e9eb5a0d | ||
![]() |
baf5dcbd03 | ||
![]() |
2e2915ec09 | ||
![]() |
5f3a6740c1 | ||
![]() |
b17074d68b | ||
![]() |
ac4b2ea70f | ||
![]() |
4e2a9e3d7b | ||
![]() |
75525b9866 | ||
![]() |
1e39f22009 | ||
![]() |
7ba55ab666 | ||
![]() |
0c06517dcc | ||
![]() |
cd1bfe1b20 | ||
![]() |
35c2d8966b | ||
![]() |
cb255ef137 | ||
![]() |
f9d78a95ad | ||
![]() |
14eb496b1d | ||
![]() |
b390e7bed1 | ||
![]() |
903f92f94a | ||
![]() |
ebb20abee0 | ||
![]() |
2253275640 | ||
![]() |
7ddfe3c80b | ||
![]() |
4388d82076 | ||
![]() |
4f70ec7dc2 | ||
![]() |
a71f42366a | ||
![]() |
e9bb9fdafe | ||
![]() |
dd49ea6f20 | ||
![]() |
92de202e0b | ||
![]() |
1d02b69f52 | ||
![]() |
1dcd913c04 | ||
![]() |
471a5f8407 | ||
![]() |
7b97ac2f6f | ||
![]() |
e673d90b3f | ||
![]() |
764234f744 | ||
![]() |
6ca944b7ef | ||
![]() |
c574556deb | ||
![]() |
c04e1adea2 | ||
![]() |
5eb11349fc | ||
![]() |
b3beb7ef85 | ||
![]() |
ec95000fbc | ||
![]() |
53f0c01073 | ||
![]() |
42d5349db7 | ||
![]() |
f36a1bbf4a | ||
![]() |
e9945abf2f | ||
![]() |
71ba192c38 | ||
![]() |
e8a7671c25 | ||
![]() |
17138f78b8 | ||
![]() |
46c6f86d6a | ||
![]() |
ae1c519d3a | ||
![]() |
fd9299cb3d | ||
![]() |
a9be870fd3 | ||
![]() |
34f61fd6dc | ||
![]() |
e568e2ae21 | ||
![]() |
0656343a91 | ||
![]() |
fa2773af9b | ||
![]() |
a438439ce0 | ||
![]() |
da73c9d83d | ||
![]() |
a8f9f7ac7a | ||
![]() |
006d989943 | ||
![]() |
82d6909957 | ||
![]() |
12a7fc9337 | ||
![]() |
0241334656 | ||
![]() |
b217291b04 | ||
![]() |
aa211df0ad | ||
![]() |
8eebf51447 | ||
![]() |
f5d834d130 | ||
![]() |
0f2eae4091 | ||
![]() |
81d2334f48 | ||
![]() |
45384205eb | ||
![]() |
4150ac045d | ||
![]() |
793a704871 | ||
![]() |
9e2606f1c8 | ||
![]() |
4c4549eb37 | ||
![]() |
96f7a4231b | ||
![]() |
c4cf248a1e | ||
![]() |
50717b4d19 | ||
![]() |
da80a3896d | ||
![]() |
cc374e0478 | ||
![]() |
26d27f8be9 | ||
![]() |
4767fcbdfc | ||
![]() |
e1b48dd2a2 | ||
![]() |
087492fc0b | ||
![]() |
525703d376 | ||
![]() |
236bc6aefa | ||
![]() |
ec4c29c52c | ||
![]() |
d5ed1c4c41 | ||
![]() |
dfe808cfb4 | ||
![]() |
da229d9b65 | ||
![]() |
e54d904e4c | ||
![]() |
2e17c96866 | ||
![]() |
ddeb16463d | ||
![]() |
465efa460e | ||
![]() |
c351402556 | ||
![]() |
5f765e8b96 | ||
![]() |
6f556f69d6 | ||
![]() |
2439827eff | ||
![]() |
5f467f82e0 | ||
![]() |
6692fa439a | ||
![]() |
0535247bb3 | ||
![]() |
a0a4fcaf5f | ||
![]() |
454d81facc | ||
![]() |
0bfa8260fa | ||
![]() |
f2124f1c95 | ||
![]() |
451bc2370a | ||
![]() |
0b17642c31 | ||
![]() |
214dc25576 | ||
![]() |
158eddfd44 | ||
![]() |
5daa6dbd25 | ||
![]() |
645ef3e61f |
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"extends": ["airbnb-base", "prettier"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "2020",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
"modules": true
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"pragma": "h",
|
||||
"version": "15.0"
|
||||
},
|
||||
"import/resolver": {
|
||||
"webpack": {
|
||||
"config": "webpack.config.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"__DEV__": false,
|
||||
"__DEMO__": false,
|
||||
"__BUILD__": false,
|
||||
"__VERSION__": false,
|
||||
"__STATIC_PATH__": false,
|
||||
"Polymer": true,
|
||||
"webkitSpeechRecognition": false,
|
||||
"ResizeObserver": false
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-var": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 0,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/extensions": [2, "ignorePackages"],
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"react/jsx-no-bind": [2, { "ignoreRefs": true }],
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
"react/self-closing-comp": 2,
|
||||
"react/prefer-es6-class": 2,
|
||||
"react/no-string-refs": 2,
|
||||
"react/require-render-return": 2,
|
||||
"react/no-find-dom-node": 2,
|
||||
"react/no-is-mounted": 2,
|
||||
"react/jsx-no-comment-textnodes": 2,
|
||||
"react/jsx-no-undef": 2,
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"no-restricted-syntax": [0, "ForOfStatement"],
|
||||
"prettier/prettier": "error"
|
||||
},
|
||||
"plugins": ["react", "prettier"]
|
||||
}
|
@@ -1,12 +1,88 @@
|
||||
{
|
||||
"extends": "./.eslintrc-hound.json",
|
||||
"plugins": ["react"],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"airbnb-typescript/base",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/recommended",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"ecmaFeatures": {
|
||||
"modules": true
|
||||
},
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"webpack": {
|
||||
"config": "./webpack.config.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"__DEV__": false,
|
||||
"__DEMO__": false,
|
||||
"__BUILD__": false,
|
||||
"__VERSION__": false,
|
||||
"__STATIC_PATH__": false,
|
||||
"Polymer": true,
|
||||
"webkitSpeechRecognition": false,
|
||||
"ResizeObserver": false
|
||||
},
|
||||
"env": {
|
||||
"browser": true
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"import/no-unresolved": 2,
|
||||
"linebreak-style": 0,
|
||||
"implicit-arrow-linebreak": 0
|
||||
}
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-var": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 0,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/order": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-cycle": 0,
|
||||
"import/extensions": [
|
||||
2,
|
||||
"ignorePackages",
|
||||
{ "ts": "never", "js": "never" }
|
||||
],
|
||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"wc/no-self-class": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-ignore": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0
|
||||
},
|
||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
||||
"processor": "disable/disable"
|
||||
}
|
||||
|
12
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
@@ -62,6 +62,18 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
|
||||
- Browser and browser version:
|
||||
- Operating system:
|
||||
|
||||
## State of relevant entities
|
||||
|
||||
<!--
|
||||
If your issue is about how an entity is shown in the UI, please add the state
|
||||
and attributes for all situations with a screenshot of the UI.
|
||||
You can find this information at `/developer-tools/state`
|
||||
-->
|
||||
|
||||
```yaml
|
||||
|
||||
```
|
||||
|
||||
## Problem-relevant configuration
|
||||
|
||||
<!--
|
||||
|
9
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
@@ -3,23 +3,24 @@ name: Request a feature for the UI, Frontend or Lovelace
|
||||
about: Request an new feature for the Home Assistant frontend.
|
||||
labels: feature request
|
||||
---
|
||||
|
||||
<!--
|
||||
DO NOT DELETE ANY TEXT from this template!
|
||||
Otherwise, your request may be closed without comment.
|
||||
-->
|
||||
|
||||
## The request
|
||||
<!--
|
||||
|
||||
<!--
|
||||
Describe to our maintainers, the feature you would like to be added.
|
||||
Please be clear and concise and, if possible, provide a screenshot or mockup.
|
||||
-->
|
||||
|
||||
|
||||
## The alternatives
|
||||
|
||||
<!--
|
||||
Are you currently using, or have you considered alternatives?
|
||||
If so, could you please describe those?
|
||||
-->
|
||||
|
||||
|
||||
## Additional information
|
||||
|
||||
|
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,7 +2,9 @@
|
||||
You are amazing! Thanks for contributing to our project!
|
||||
Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
|
||||
-->
|
||||
|
||||
## Breaking change
|
||||
|
||||
<!--
|
||||
If your PR contains a breaking change for existing users, it is important
|
||||
to tell them what breaks, how to make it work again and why we did this.
|
||||
@@ -11,20 +13,20 @@
|
||||
Note: Remove this section if this PR is NOT a breaking change.
|
||||
-->
|
||||
|
||||
|
||||
## Proposed change
|
||||
<!--
|
||||
|
||||
<!--
|
||||
Describe the big picture of your changes here to communicate to the
|
||||
maintainers why we should accept this pull request. If it fixes a bug
|
||||
or resolves a feature request, be sure to link to that issue in the
|
||||
additional information section.
|
||||
-->
|
||||
|
||||
|
||||
## Type of change
|
||||
|
||||
<!--
|
||||
What type of change does your PR introduce to the Home Assistant frontend?
|
||||
NOTE: Please, check only 1! box!
|
||||
NOTE: Please, check only 1! box!
|
||||
If your PR requires multiple boxes to be checked, you'll most likely need to
|
||||
split it into multiple PRs. This makes things easier and faster to code review.
|
||||
-->
|
||||
@@ -36,6 +38,7 @@
|
||||
- [ ] Code quality improvements to existing code or addition of tests
|
||||
|
||||
## Example configuration
|
||||
|
||||
<!--
|
||||
Supplying a configuration snippet, makes it easier for a maintainer to test
|
||||
your PR.
|
||||
@@ -46,16 +49,18 @@
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
<!--
|
||||
Details are important, and help maintainers processing your PR.
|
||||
Please be sure to fill out additional details, if applicable.
|
||||
-->
|
||||
|
||||
- This PR fixes or closes issue: fixes #
|
||||
- This PR is related to issue:
|
||||
- Link to documentation pull request:
|
||||
- This PR is related to issue:
|
||||
- Link to documentation pull request:
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--
|
||||
Put an `x` in the boxes that apply. You can also fill these out after
|
||||
creating the PR. If you're unsure about any of them, don't hesitate to ask.
|
||||
@@ -74,4 +79,5 @@ If user exposed functionality or configuration variables are added/changed:
|
||||
<!--
|
||||
Thank you for contributing <3
|
||||
-->
|
||||
|
||||
[docs-repository]: https://github.com/home-assistant/home-assistant.io
|
||||
|
2
.github/lock.yml
vendored
@@ -24,4 +24,4 @@ only: pulls
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
issues:
|
||||
daysUntilLock: 30
|
||||
daysUntilLock: 30
|
||||
|
10
.github/workflows/ci.yaml
vendored
@@ -35,13 +35,11 @@ jobs:
|
||||
env:
|
||||
CI: true
|
||||
- name: Build icons
|
||||
run: ./node_modules/.bin/gulp gen-icons-hassio gen-icons-mdi gen-icons-app
|
||||
run: ./node_modules/.bin/gulp gen-icons-json
|
||||
- name: Build translations
|
||||
run: ./node_modules/.bin/gulp build-translations
|
||||
- name: Run eslint
|
||||
run: ./node_modules/.bin/eslint src hassio/src gallery/src
|
||||
- name: Run tslint
|
||||
run: ./node_modules/.bin/tslint 'src/**/*.ts' 'hassio/src/**/*.ts' 'gallery/src/**/*.ts' 'cast/src/**/*.ts' 'test-mocha/**/*.ts'
|
||||
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
|
||||
- name: Run tsc
|
||||
run: ./node_modules/.bin/tsc
|
||||
test:
|
||||
@@ -96,7 +94,7 @@ jobs:
|
||||
- name: Build Application
|
||||
run: ./node_modules/.bin/gulp build-app
|
||||
env:
|
||||
TRAVIS: "true"
|
||||
IS_TEST: "true"
|
||||
supervisor:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
@@ -124,4 +122,4 @@ jobs:
|
||||
- name: Build Application
|
||||
run: ./node_modules/.bin/gulp build-hassio
|
||||
env:
|
||||
TRAVIS: "true"
|
||||
IS_TEST: "true"
|
||||
|
4
.gitignore
vendored
@@ -5,7 +5,6 @@ npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
.reify-cache
|
||||
demo/hademo-icons.html
|
||||
|
||||
# Python stuff
|
||||
*.py[cod]
|
||||
@@ -31,3 +30,6 @@ src/cast/dev_const.ts
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
||||
#asdf
|
||||
.tool-versions
|
||||
|
10
.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
build
|
||||
build-translations/*
|
||||
translations/*
|
||||
node_modules/*
|
||||
hass_frontend/*
|
||||
pip-selfcheck.json
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
1
.vscode/extensions.json
vendored
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"esbenp.prettier-vscode",
|
||||
"bierner.lit-html",
|
||||
"runem.lit-plugin"
|
||||
|
@@ -6,12 +6,12 @@ This is the repository for the official [Home Assistant](https://home-assistant.
|
||||
|
||||
- [View demo of Home Assistant](https://demo.home-assistant.io/)
|
||||
- [More information about Home Assistant](https://home-assistant.io)
|
||||
- [Frontend development instructions](https://developers.home-assistant.io/docs/en/frontend_index.html)
|
||||
- [Frontend development instructions](https://developers.home-assistant.io/docs/frontend/development/)
|
||||
|
||||
## Development
|
||||
|
||||
- Initial setup: `script/setup`
|
||||
- Development: [Instructions](https://developers.home-assistant.io/docs/en/frontend_development.html)
|
||||
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
|
||||
- Production build: `script/build_frontend`
|
||||
- Gallery: `cd gallery && script/develop_gallery`
|
||||
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
|
||||
|
@@ -8,7 +8,7 @@ schedules:
|
||||
branches:
|
||||
include:
|
||||
- dev
|
||||
always: false
|
||||
always: true
|
||||
variables:
|
||||
- group: netlify
|
||||
|
||||
@@ -24,4 +24,7 @@ jobs:
|
||||
|
||||
# Demo
|
||||
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO}
|
||||
|
||||
# Gallery
|
||||
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY}
|
||||
displayName: 'Trigger netlify build preview'
|
||||
|
@@ -47,18 +47,13 @@ stages:
|
||||
|
||||
script/release
|
||||
displayName: "Build and release package"
|
||||
- stage: "Wheels"
|
||||
jobs:
|
||||
- template: templates/azp-job-wheels.yaml@azure
|
||||
parameters:
|
||||
builderVersion: '$(versionWheels)'
|
||||
builderApk: 'build-base'
|
||||
wheelsLocal: true
|
||||
wheelsRequirement: 'requirement.txt'
|
||||
preBuild:
|
||||
- task: NodeTool@0
|
||||
displayName: "Use Node $(versionNode)"
|
||||
inputs:
|
||||
versionSpec: "$(versionNode)"
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
yarn install
|
||||
script/build_frontend
|
||||
sleep 240
|
||||
echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt
|
||||
|
@@ -1,50 +1,39 @@
|
||||
const options = ({ latestBuild }) => ({
|
||||
presets: [
|
||||
!latestBuild && [require("@babel/preset-env").default, { modules: false }],
|
||||
require("@babel/preset-typescript").default,
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
[
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
{ loose: true, useBuiltIns: true },
|
||||
],
|
||||
// Only support the syntax, Webpack will handle it.
|
||||
"@babel/syntax-dynamic-import",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
[
|
||||
require("@babel/plugin-proposal-decorators").default,
|
||||
{ decoratorsBeforeExport: true },
|
||||
],
|
||||
[
|
||||
require("@babel/plugin-proposal-class-properties").default,
|
||||
{ loose: true },
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
module.exports.babelLoaderConfig = ({ latestBuild }) => {
|
||||
if (latestBuild === undefined) {
|
||||
throw Error("latestBuild not defined for babel loader config");
|
||||
}
|
||||
return {
|
||||
test: /\.m?js$|\.tsx?$/,
|
||||
exclude: [require.resolve("@mdi/js/mdi.js"), require.resolve("hls.js")],
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: [
|
||||
!latestBuild && [
|
||||
require("@babel/preset-env").default,
|
||||
{ modules: false },
|
||||
],
|
||||
[
|
||||
require("@babel/preset-typescript").default,
|
||||
{
|
||||
jsxPragma: "h",
|
||||
},
|
||||
],
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
[
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
{ loose: true, useBuiltIns: true },
|
||||
],
|
||||
// Only support the syntax, Webpack will handle it.
|
||||
"@babel/syntax-dynamic-import",
|
||||
[
|
||||
"@babel/transform-react-jsx",
|
||||
{
|
||||
pragma: "h",
|
||||
},
|
||||
],
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
[
|
||||
require("@babel/plugin-proposal-decorators").default,
|
||||
{ decoratorsBeforeExport: true },
|
||||
],
|
||||
[
|
||||
require("@babel/plugin-proposal-class-properties").default,
|
||||
{ loose: true },
|
||||
],
|
||||
],
|
||||
},
|
||||
options: options({ latestBuild }),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@@ -1,3 +1,7 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const paths = require("./paths.js");
|
||||
|
||||
module.exports = {
|
||||
isProdBuild() {
|
||||
return process.env.NODE_ENV === "production";
|
||||
@@ -5,10 +9,19 @@ module.exports = {
|
||||
isStatsBuild() {
|
||||
return process.env.STATS === "1";
|
||||
},
|
||||
isTravis() {
|
||||
return process.env.TRAVIS === "true";
|
||||
isTest() {
|
||||
return process.env.IS_TEST === "true";
|
||||
},
|
||||
isNetlify() {
|
||||
return process.env.NETLIFY === "true";
|
||||
},
|
||||
version() {
|
||||
const version = fs
|
||||
.readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8")
|
||||
.match(/\d{8}\.\d+/);
|
||||
if (!version) {
|
||||
throw Error("Version not found");
|
||||
}
|
||||
return version[0];
|
||||
},
|
||||
};
|
||||
|
@@ -5,7 +5,7 @@ const envVars = require("../env");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./gather-static.js");
|
||||
require("./compress.js");
|
||||
require("./webpack.js");
|
||||
@@ -20,8 +20,8 @@ gulp.task(
|
||||
},
|
||||
"clean",
|
||||
gulp.parallel(
|
||||
"gen-service-worker-dev",
|
||||
gulp.parallel("gen-icons-app", "gen-icons-mdi"),
|
||||
"gen-service-worker-app-dev",
|
||||
"gen-icons-json",
|
||||
"gen-pages-dev",
|
||||
"gen-index-app-dev",
|
||||
"build-translations"
|
||||
@@ -38,15 +38,15 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean",
|
||||
gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static",
|
||||
"webpack-prod-app",
|
||||
...// Don't compress running tests
|
||||
(envVars.isTravis() ? [] : ["compress-app"]),
|
||||
(envVars.isTest() ? [] : ["compress-app"]),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-app-prod",
|
||||
"gen-service-worker-prod"
|
||||
"gen-service-worker-app-prod"
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@@ -2,7 +2,6 @@ const gulp = require("gulp");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gather-static.js");
|
||||
require("./webpack.js");
|
||||
require("./service-worker.js");
|
||||
@@ -15,12 +14,8 @@ gulp.task(
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean-cast",
|
||||
gulp.parallel(
|
||||
"gen-icons-app",
|
||||
"gen-icons-mdi",
|
||||
"gen-index-cast-dev",
|
||||
"build-translations"
|
||||
),
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-cast",
|
||||
"webpack-dev-server-cast"
|
||||
)
|
||||
@@ -33,7 +28,8 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean-cast",
|
||||
gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-cast",
|
||||
"webpack-prod-cast",
|
||||
"gen-index-cast-prod"
|
||||
|
@@ -9,25 +9,30 @@ const paths = require("../paths");
|
||||
gulp.task("compress-app", function compressApp() {
|
||||
const jsLatest = gulp
|
||||
.src(path.resolve(paths.output, "**/*.js"))
|
||||
.pipe(zopfli())
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(gulp.dest(paths.output));
|
||||
|
||||
const jsEs5 = gulp
|
||||
.src(path.resolve(paths.output_es5, "**/*.js"))
|
||||
.pipe(zopfli())
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(gulp.dest(paths.output_es5));
|
||||
|
||||
const polyfills = gulp
|
||||
.src(path.resolve(paths.static, "polyfills/*.js"))
|
||||
.pipe(zopfli())
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(gulp.dest(path.resolve(paths.static, "polyfills")));
|
||||
|
||||
const translations = gulp
|
||||
.src(path.resolve(paths.static, "translations/*.json"))
|
||||
.pipe(zopfli())
|
||||
.src(path.resolve(paths.static, "translations/**/*.json"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(gulp.dest(path.resolve(paths.static, "translations")));
|
||||
|
||||
return merge(jsLatest, jsEs5, polyfills, translations);
|
||||
const icons = gulp
|
||||
.src(path.resolve(paths.static, "mdi/*.json"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(gulp.dest(path.resolve(paths.static, "mdi")));
|
||||
|
||||
return merge(jsLatest, jsEs5, polyfills, translations, icons);
|
||||
});
|
||||
|
||||
gulp.task("compress-hassio", function compressApp() {
|
||||
|
@@ -3,7 +3,7 @@ const gulp = require("gulp");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./gather-static.js");
|
||||
require("./webpack.js");
|
||||
require("./service-worker.js");
|
||||
@@ -16,13 +16,8 @@ gulp.task(
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean-demo",
|
||||
gulp.parallel(
|
||||
"gen-icons-app",
|
||||
"gen-icons-mdi",
|
||||
"gen-icons-demo",
|
||||
"gen-index-demo-dev",
|
||||
"build-translations"
|
||||
),
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"),
|
||||
"copy-static-demo",
|
||||
"webpack-dev-server-demo"
|
||||
)
|
||||
@@ -35,12 +30,9 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean-demo",
|
||||
gulp.parallel(
|
||||
"gen-icons-app",
|
||||
"gen-icons-mdi",
|
||||
"gen-icons-demo",
|
||||
"build-translations"
|
||||
),
|
||||
// Cast needs to be backwards compatible and older HA has no translations
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-demo",
|
||||
"webpack-prod-demo",
|
||||
"gen-index-demo-prod"
|
||||
|
@@ -1,9 +1,14 @@
|
||||
const del = require("del");
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const mapStream = require("map-stream");
|
||||
|
||||
const inDir = "translations";
|
||||
const downloadDir = inDir + "/downloads";
|
||||
const inDirFrontend = "translations/frontend";
|
||||
const inDirBackend = "translations/backend";
|
||||
const downloadDir = "translations/downloads";
|
||||
const srcMeta = "src/translations/translationMetadata.json";
|
||||
|
||||
const encoding = "utf8";
|
||||
|
||||
const tasks = [];
|
||||
|
||||
@@ -12,7 +17,7 @@ function hasHtml(data) {
|
||||
}
|
||||
|
||||
function recursiveCheckHasHtml(file, data, errors, recKey) {
|
||||
Object.keys(data).forEach(function(key) {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (typeof data[key] === "object") {
|
||||
const nextRecKey = recKey ? `${recKey}.${key}` : key;
|
||||
recursiveCheckHasHtml(file, data[key], errors, nextRecKey);
|
||||
@@ -25,7 +30,7 @@ function recursiveCheckHasHtml(file, data, errors, recKey) {
|
||||
function checkHtml() {
|
||||
const errors = [];
|
||||
|
||||
return mapStream(function(file, cb) {
|
||||
return mapStream(function (file, cb) {
|
||||
const content = file.contents;
|
||||
let error;
|
||||
if (content) {
|
||||
@@ -42,20 +47,36 @@ function checkHtml() {
|
||||
}
|
||||
|
||||
let taskName = "clean-downloaded-translations";
|
||||
gulp.task(taskName, function() {
|
||||
gulp.task(taskName, function () {
|
||||
return del([`${downloadDir}/**`]);
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = "check-translations-html";
|
||||
gulp.task(taskName, function() {
|
||||
gulp.task(taskName, function () {
|
||||
return gulp.src(`${downloadDir}/*.json`).pipe(checkHtml());
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = "check-all-files-exist";
|
||||
gulp.task(taskName, function () {
|
||||
const file = fs.readFileSync(srcMeta, { encoding });
|
||||
const meta = JSON.parse(file);
|
||||
Object.keys(meta).forEach((lang) => {
|
||||
if (!fs.existsSync(`${inDirFrontend}/${lang}.json`)) {
|
||||
fs.writeFileSync(`${inDirFrontend}/${lang}.json`, JSON.stringify({}));
|
||||
}
|
||||
if (!fs.existsSync(`${inDirBackend}/${lang}.json`)) {
|
||||
fs.writeFileSync(`${inDirBackend}/${lang}.json`, JSON.stringify({}));
|
||||
}
|
||||
});
|
||||
return Promise.resolve();
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = "move-downloaded-translations";
|
||||
gulp.task(taskName, function() {
|
||||
return gulp.src(`${downloadDir}/*.json`).pipe(gulp.dest(inDir));
|
||||
gulp.task(taskName, function () {
|
||||
return gulp.src(`${downloadDir}/*.json`).pipe(gulp.dest(inDirFrontend));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
@@ -65,6 +86,7 @@ gulp.task(
|
||||
gulp.series(
|
||||
"check-translations-html",
|
||||
"move-downloaded-translations",
|
||||
"check-all-files-exist",
|
||||
"clean-downloaded-translations"
|
||||
)
|
||||
);
|
||||
|
@@ -47,11 +47,9 @@ gulp.task("gen-pages-dev", (done) => {
|
||||
for (const page of PAGES) {
|
||||
const content = renderTemplate(page, {
|
||||
latestPageJS: `/frontend_latest/${page}.js`,
|
||||
latestHassIconsJS: "/frontend_latest/hass-icons.js",
|
||||
|
||||
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||
es5PageJS: `/frontend_es5/${page}.js`,
|
||||
es5HassIconsJS: "/frontend_es5/hass-icons.js",
|
||||
});
|
||||
|
||||
fs.outputFileSync(path.resolve(config.root, `${page}.html`), content);
|
||||
@@ -66,11 +64,9 @@ gulp.task("gen-pages-prod", (done) => {
|
||||
for (const page of PAGES) {
|
||||
const content = renderTemplate(page, {
|
||||
latestPageJS: latestManifest[`${page}.js`],
|
||||
latestHassIconsJS: latestManifest["hass-icons.js"],
|
||||
|
||||
es5Compatibility: es5Manifest["compatibility.js"],
|
||||
es5PageJS: es5Manifest[`${page}.js`],
|
||||
es5HassIconsJS: es5Manifest["hass-icons.js"],
|
||||
});
|
||||
|
||||
fs.outputFileSync(
|
||||
@@ -88,13 +84,11 @@ gulp.task("gen-index-app-dev", (done) => {
|
||||
latestAppJS: "/frontend_latest/app.js",
|
||||
latestCoreJS: "/frontend_latest/core.js",
|
||||
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
|
||||
latestHassIconsJS: "/frontend_latest/hass-icons.js",
|
||||
|
||||
es5Compatibility: "/frontend_es5/compatibility.js",
|
||||
es5AppJS: "/frontend_es5/app.js",
|
||||
es5CoreJS: "/frontend_es5/core.js",
|
||||
es5CustomPanelJS: "/frontend_es5/custom-panel.js",
|
||||
es5HassIconsJS: "/frontend_es5/hass-icons.js",
|
||||
}).replace(/#THEMEC/g, "{{ theme_color }}");
|
||||
|
||||
fs.outputFileSync(path.resolve(config.root, "index.html"), content);
|
||||
@@ -108,13 +102,11 @@ gulp.task("gen-index-app-prod", (done) => {
|
||||
latestAppJS: latestManifest["app.js"],
|
||||
latestCoreJS: latestManifest["core.js"],
|
||||
latestCustomPanelJS: latestManifest["custom-panel.js"],
|
||||
latestHassIconsJS: latestManifest["hass-icons.js"],
|
||||
|
||||
es5Compatibility: es5Manifest["compatibility.js"],
|
||||
es5AppJS: es5Manifest["app.js"],
|
||||
es5CoreJS: es5Manifest["core.js"],
|
||||
es5CustomPanelJS: es5Manifest["custom-panel.js"],
|
||||
es5HassIconsJS: es5Manifest["hass-icons.js"],
|
||||
});
|
||||
const minified = minifyHtml(content).replace(/#THEMEC/g, "{{ theme_color }}");
|
||||
|
||||
@@ -222,7 +214,7 @@ gulp.task("gen-index-gallery-dev", (done) => {
|
||||
// In dev mode we don't mangle names, so we hardcode urls. That way we can
|
||||
// run webpack as last in watch mode, which blocks output.
|
||||
const content = renderGalleryTemplate("index", {
|
||||
latestGalleryJS: "./entrypoint.js",
|
||||
latestGalleryJS: "./frontend_latest/entrypoint.js",
|
||||
});
|
||||
|
||||
fs.outputFileSync(path.resolve(config.gallery_root, "index.html"), content);
|
||||
|
@@ -3,7 +3,7 @@ const gulp = require("gulp");
|
||||
|
||||
require("./clean.js");
|
||||
require("./translations.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./gather-static.js");
|
||||
require("./webpack.js");
|
||||
require("./service-worker.js");
|
||||
@@ -16,7 +16,8 @@ gulp.task(
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean-gallery",
|
||||
gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-gallery",
|
||||
"gen-index-gallery-dev",
|
||||
"webpack-dev-server-gallery"
|
||||
@@ -30,7 +31,8 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean-gallery",
|
||||
gulp.parallel("gen-icons-app", "gen-icons-mdi", "build-translations"),
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-gallery",
|
||||
"webpack-prod-gallery",
|
||||
"gen-index-gallery-prod"
|
||||
|
@@ -26,6 +26,13 @@ function copyTranslations(staticDir) {
|
||||
);
|
||||
}
|
||||
|
||||
function copyMdiIcons(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
// MDI icons output
|
||||
fs.copySync(polyPath("build/mdi"), staticPath("mdi"));
|
||||
}
|
||||
|
||||
function copyPolyfills(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
@@ -73,19 +80,15 @@ gulp.task("copy-translations", (done) => {
|
||||
|
||||
gulp.task("copy-static", (done) => {
|
||||
const staticDir = paths.static;
|
||||
const staticPath = genStaticPath(paths.static);
|
||||
// Basic static files
|
||||
fs.copySync(polyPath("public"), paths.root);
|
||||
|
||||
copyPolyfills(staticDir);
|
||||
copyFonts(staticDir);
|
||||
copyTranslations(staticDir);
|
||||
copyMdiIcons(staticDir);
|
||||
|
||||
// Panel assets
|
||||
copyFileDir(
|
||||
npmPath("react-big-calendar/lib/css/react-big-calendar.css"),
|
||||
staticPath("panels/calendar/")
|
||||
);
|
||||
copyMapPanel(staticDir);
|
||||
done();
|
||||
});
|
||||
@@ -103,6 +106,7 @@ gulp.task("copy-static-demo", (done) => {
|
||||
copyMapPanel(paths.demo_static);
|
||||
copyFonts(paths.demo_static);
|
||||
copyTranslations(paths.demo_static);
|
||||
copyMdiIcons(paths.demo_static);
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -115,6 +119,7 @@ gulp.task("copy-static-cast", (done) => {
|
||||
copyMapPanel(paths.cast_static);
|
||||
copyFonts(paths.cast_static);
|
||||
copyTranslations(paths.cast_static);
|
||||
copyMdiIcons(paths.cast_static);
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -127,5 +132,6 @@ gulp.task("copy-static-gallery", (done) => {
|
||||
copyMapPanel(paths.gallery_static);
|
||||
copyFonts(paths.gallery_static);
|
||||
copyTranslations(paths.gallery_static);
|
||||
copyMdiIcons(paths.gallery_static);
|
||||
done();
|
||||
});
|
||||
|
112
build-scripts/gulp/gen-icons-json.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const hash = require("object-hash");
|
||||
|
||||
const ICON_PACKAGE_PATH = path.resolve(
|
||||
__dirname,
|
||||
"../../node_modules/@mdi/svg/"
|
||||
);
|
||||
const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json");
|
||||
const PACKAGE_PATH = path.resolve(ICON_PACKAGE_PATH, "package.json");
|
||||
const ICON_PATH = path.resolve(ICON_PACKAGE_PATH, "svg");
|
||||
const OUTPUT_DIR = path.resolve(__dirname, "../../build/mdi");
|
||||
|
||||
const encoding = "utf8";
|
||||
|
||||
const getMeta = () => {
|
||||
const file = fs.readFileSync(META_PATH, { encoding });
|
||||
const meta = JSON.parse(file);
|
||||
return meta.map((icon) => {
|
||||
const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, {
|
||||
encoding,
|
||||
});
|
||||
return { path: svg.match(/ d="([^"]+)"/)[1], name: icon.name };
|
||||
});
|
||||
};
|
||||
|
||||
const splitBySize = (meta) => {
|
||||
const chunks = [];
|
||||
const CHUNK_SIZE = 50000;
|
||||
|
||||
let curSize = 0;
|
||||
let startKey;
|
||||
let icons = [];
|
||||
|
||||
Object.values(meta).forEach((icon) => {
|
||||
if (startKey === undefined) {
|
||||
startKey = icon.name;
|
||||
}
|
||||
curSize += icon.path.length;
|
||||
icons.push(icon);
|
||||
if (curSize > CHUNK_SIZE) {
|
||||
chunks.push({
|
||||
startKey,
|
||||
endKey: icon.name,
|
||||
icons,
|
||||
});
|
||||
curSize = 0;
|
||||
startKey = undefined;
|
||||
icons = [];
|
||||
}
|
||||
});
|
||||
|
||||
chunks.push({
|
||||
startKey,
|
||||
icons,
|
||||
});
|
||||
|
||||
return chunks;
|
||||
};
|
||||
|
||||
const findDifferentiator = (curString, prevString) => {
|
||||
for (let i = 0; i < curString.length; i++) {
|
||||
if (curString[i] !== prevString[i]) {
|
||||
return curString.substring(0, i + 1);
|
||||
}
|
||||
}
|
||||
throw new Error("Cannot find differentiator", curString, prevString);
|
||||
};
|
||||
|
||||
gulp.task("gen-icons-json", (done) => {
|
||||
const meta = getMeta();
|
||||
const split = splitBySize(meta);
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
const parts = [];
|
||||
|
||||
let lastEnd;
|
||||
split.forEach((chunk) => {
|
||||
let startKey;
|
||||
if (lastEnd === undefined) {
|
||||
chunk.startKey = undefined;
|
||||
startKey = undefined;
|
||||
} else {
|
||||
startKey = findDifferentiator(chunk.startKey, lastEnd);
|
||||
}
|
||||
lastEnd = chunk.endKey;
|
||||
|
||||
const output = {};
|
||||
chunk.icons.forEach((icon) => {
|
||||
output[icon.name] = icon.path;
|
||||
});
|
||||
const filename = hash(output);
|
||||
parts.push({ start: startKey, file: filename });
|
||||
fs.writeFileSync(
|
||||
path.resolve(OUTPUT_DIR, `${filename}.json`),
|
||||
JSON.stringify(output)
|
||||
);
|
||||
});
|
||||
|
||||
const file = fs.readFileSync(PACKAGE_PATH, { encoding });
|
||||
const package = JSON.parse(file);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(OUTPUT_DIR, "iconMetadata.json"),
|
||||
JSON.stringify({ version: package.version, parts })
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
@@ -1,127 +0,0 @@
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const paths = require("../paths");
|
||||
const { mapFiles } = require("../util");
|
||||
|
||||
const ICON_PACKAGE_PATH = path.resolve(
|
||||
__dirname,
|
||||
"../../node_modules/@mdi/svg/"
|
||||
);
|
||||
const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json");
|
||||
const ICON_PATH = path.resolve(ICON_PACKAGE_PATH, "svg");
|
||||
const OUTPUT_DIR = path.resolve(__dirname, "../../build");
|
||||
const MDI_OUTPUT_PATH = path.resolve(OUTPUT_DIR, "mdi.html");
|
||||
const HASS_OUTPUT_PATH = path.resolve(OUTPUT_DIR, "hass-icons.html");
|
||||
|
||||
const BUILT_IN_PANEL_ICONS = [
|
||||
"calendar", // Calendar
|
||||
"settings", // Config
|
||||
"home-assistant", // Hass.io
|
||||
"poll-box", // History panel
|
||||
"format-list-bulleted-type", // Logbook
|
||||
"mailbox", // Mailbox
|
||||
"tooltip-account", // Map
|
||||
"cart", // Shopping List
|
||||
"hammer", // developer-tools
|
||||
];
|
||||
|
||||
// Given an icon name, load the SVG file
|
||||
function loadIcon(name) {
|
||||
const iconPath = path.resolve(ICON_PATH, `${name}.svg`);
|
||||
try {
|
||||
return fs.readFileSync(iconPath, "utf-8");
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Given an SVG file, convert it to an iron-iconset-svg definition
|
||||
function transformXMLtoPolymer(name, xml) {
|
||||
const start = xml.indexOf("><path") + 1;
|
||||
const end = xml.length - start - 6;
|
||||
const pth = xml.substr(start, end);
|
||||
return `<g id="${name}">${pth}</g>`;
|
||||
}
|
||||
|
||||
// Given an iconset name and icon names, generate a polymer iconset
|
||||
function generateIconset(iconsetName, iconNames) {
|
||||
const iconDefs = Array.from(iconNames)
|
||||
.map((name) => {
|
||||
const iconDef = loadIcon(name);
|
||||
if (!iconDef) {
|
||||
throw new Error(`Unknown icon referenced: ${name}`);
|
||||
}
|
||||
return transformXMLtoPolymer(name, iconDef);
|
||||
})
|
||||
.join("");
|
||||
return `<ha-iconset-svg name="${iconsetName}" size="24"><svg><defs>${iconDefs}</defs></svg></ha-iconset-svg>`;
|
||||
}
|
||||
|
||||
// Find all icons used by the project.
|
||||
function findIcons(searchPath, iconsetName) {
|
||||
const iconRegex = new RegExp(`${iconsetName}:[\\w-]+`, "g");
|
||||
const icons = new Set();
|
||||
function processFile(filename) {
|
||||
const content = fs.readFileSync(filename);
|
||||
let match;
|
||||
// eslint-disable-next-line
|
||||
while ((match = iconRegex.exec(content))) {
|
||||
// strip off "hass:" and add to set
|
||||
icons.add(match[0].substr(iconsetName.length + 1));
|
||||
}
|
||||
}
|
||||
mapFiles(searchPath, ".js", processFile);
|
||||
mapFiles(searchPath, ".ts", processFile);
|
||||
return icons;
|
||||
}
|
||||
|
||||
gulp.task("gen-icons-mdi", (done) => {
|
||||
const meta = JSON.parse(
|
||||
fs.readFileSync(path.resolve(ICON_PACKAGE_PATH, META_PATH), "UTF-8")
|
||||
);
|
||||
const iconNames = meta.map((iconInfo) => iconInfo.name);
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR);
|
||||
}
|
||||
fs.writeFileSync(MDI_OUTPUT_PATH, generateIconset("mdi", iconNames));
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-icons-app", (done) => {
|
||||
const iconNames = findIcons("./src", "hass");
|
||||
BUILT_IN_PANEL_ICONS.forEach((name) => iconNames.add(name));
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR);
|
||||
}
|
||||
fs.writeFileSync(HASS_OUTPUT_PATH, generateIconset("hass", iconNames));
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-icons-demo", (done) => {
|
||||
const iconNames = findIcons(path.resolve(paths.demo_dir, "./src"), "hademo");
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.demo_dir, "hademo-icons.html"),
|
||||
generateIconset("hademo", iconNames)
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-icons-hassio", (done) => {
|
||||
const iconNames = findIcons(
|
||||
path.resolve(paths.hassio_dir, "./src"),
|
||||
"hassio"
|
||||
);
|
||||
// Find hassio icons inside HA main repo.
|
||||
for (const item of findIcons(
|
||||
path.resolve(paths.polymer_dir, "./src"),
|
||||
"hassio"
|
||||
)) {
|
||||
iconNames.add(item);
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_dir, "hassio-icons.html"),
|
||||
generateIconset("hassio", iconNames)
|
||||
);
|
||||
done();
|
||||
});
|
@@ -3,7 +3,7 @@ const gulp = require("gulp");
|
||||
const envVars = require("../env");
|
||||
|
||||
require("./clean.js");
|
||||
require("./gen-icons.js");
|
||||
require("./gen-icons-json.js");
|
||||
require("./webpack.js");
|
||||
require("./compress.js");
|
||||
|
||||
@@ -14,7 +14,7 @@ gulp.task(
|
||||
process.env.NODE_ENV = "development";
|
||||
},
|
||||
"clean-hassio",
|
||||
gulp.parallel("gen-icons-hassio", "gen-icons-mdi"),
|
||||
"gen-icons-json",
|
||||
"webpack-watch-hassio"
|
||||
)
|
||||
);
|
||||
@@ -26,9 +26,9 @@ gulp.task(
|
||||
process.env.NODE_ENV = "production";
|
||||
},
|
||||
"clean-hassio",
|
||||
gulp.parallel("gen-icons-hassio", "gen-icons-mdi"),
|
||||
"gen-icons-json",
|
||||
"webpack-prod-hassio",
|
||||
...// Don't compress running tests
|
||||
(envVars.isTravis() ? [] : ["compress-hassio"])
|
||||
(envVars.isTest() ? [] : ["compress-hassio"])
|
||||
)
|
||||
);
|
||||
|
@@ -5,18 +5,22 @@
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const config = require("../paths.js");
|
||||
const workboxBuild = require("workbox-build");
|
||||
const sourceMapUrl = require("source-map-url");
|
||||
const paths = require("../paths.js");
|
||||
|
||||
const swPath = path.resolve(config.root, "service_worker.js");
|
||||
const swDest = path.resolve(paths.root, "service_worker.js");
|
||||
|
||||
const writeSW = (content) => fs.outputFileSync(swPath, content.trim() + "\n");
|
||||
const writeSW = (content) => fs.outputFileSync(swDest, content.trim() + "\n");
|
||||
|
||||
gulp.task("gen-service-worker-dev", (done) => {
|
||||
gulp.task("gen-service-worker-app-dev", (done) => {
|
||||
writeSW(
|
||||
`
|
||||
console.debug('Service worker disabled in development');
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
// This will activate the dev service worker,
|
||||
// removing any prod service worker the dev might have running
|
||||
self.skipWaiting();
|
||||
});
|
||||
`
|
||||
@@ -24,10 +28,63 @@ self.addEventListener('install', (event) => {
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("gen-service-worker-prod", (done) => {
|
||||
fs.copySync(
|
||||
path.resolve(config.output, "service_worker.js"),
|
||||
path.resolve(config.root, "service_worker.js")
|
||||
gulp.task("gen-service-worker-app-prod", async () => {
|
||||
// Read bundled source file
|
||||
const bundleManifestLatest = require(path.resolve(
|
||||
paths.output,
|
||||
"manifest.json"
|
||||
));
|
||||
let serviceWorkerContent = fs.readFileSync(
|
||||
paths.root + bundleManifestLatest["service_worker.js"],
|
||||
"utf-8"
|
||||
);
|
||||
done();
|
||||
|
||||
// Delete old file from frontend_latest so manifest won't pick it up
|
||||
fs.removeSync(paths.root + bundleManifestLatest["service_worker.js"]);
|
||||
fs.removeSync(paths.root + bundleManifestLatest["service_worker.js.map"]);
|
||||
|
||||
// Remove ES5
|
||||
const bundleManifestES5 = require(path.resolve(
|
||||
paths.output_es5,
|
||||
"manifest.json"
|
||||
));
|
||||
fs.removeSync(paths.root + bundleManifestES5["service_worker.js"]);
|
||||
fs.removeSync(paths.root + bundleManifestES5["service_worker.js.map"]);
|
||||
|
||||
const workboxManifest = await workboxBuild.getManifest({
|
||||
// Files that mach this pattern will be considered unique and skip revision check
|
||||
// ignore JS files + translation files
|
||||
dontCacheBustURLsMatching: /(frontend_latest\/.+|static\/translations\/.+)/,
|
||||
|
||||
globDirectory: paths.root,
|
||||
globPatterns: [
|
||||
"frontend_latest/*.js",
|
||||
// Cache all English translations because we catch them as fallback
|
||||
// Using pattern to match hash instead of * to avoid caching en-GB
|
||||
// 'v' added as valid hash letter because in dev we hash with 'dev'
|
||||
"static/translations/**/en-+([a-fv0-9]).json",
|
||||
// Icon shown on splash screen
|
||||
"static/icons/favicon-192x192.png",
|
||||
"static/icons/favicon.ico",
|
||||
// Common fonts
|
||||
"static/fonts/roboto/Roboto-Light.woff2",
|
||||
"static/fonts/roboto/Roboto-Medium.woff2",
|
||||
"static/fonts/roboto/Roboto-Regular.woff2",
|
||||
"static/fonts/roboto/Roboto-Bold.woff2",
|
||||
],
|
||||
});
|
||||
|
||||
for (const warning of workboxManifest.warnings) {
|
||||
console.warn(warning);
|
||||
}
|
||||
|
||||
// remove source map and add WB manifest
|
||||
serviceWorkerContent = sourceMapUrl.removeFrom(serviceWorkerContent);
|
||||
serviceWorkerContent = serviceWorkerContent.replace(
|
||||
"WB_MANIFEST",
|
||||
JSON.stringify(workboxManifest.manifestEntries)
|
||||
);
|
||||
|
||||
// Write new file to root
|
||||
fs.writeFileSync(swDest, serviceWorkerContent);
|
||||
});
|
||||
|
@@ -14,13 +14,20 @@ const { mapFiles } = require("../util");
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
|
||||
const inDir = "translations";
|
||||
const inFrontendDir = "translations/frontend";
|
||||
const inBackendDir = "translations/backend";
|
||||
const workDir = "build-translations";
|
||||
const fullDir = workDir + "/full";
|
||||
const coreDir = workDir + "/core";
|
||||
const outDir = workDir + "/output";
|
||||
let mergeBackend = false;
|
||||
|
||||
String.prototype.rsplit = function(sep, maxsplit) {
|
||||
gulp.task("translations-enable-merge-backend", (done) => {
|
||||
mergeBackend = true;
|
||||
done();
|
||||
});
|
||||
|
||||
String.prototype.rsplit = function (sep, maxsplit) {
|
||||
var split = this.split(sep);
|
||||
return maxsplit
|
||||
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
|
||||
@@ -45,7 +52,7 @@ const TRANSLATION_FRAGMENTS = [
|
||||
|
||||
function recursiveFlatten(prefix, data) {
|
||||
let output = {};
|
||||
Object.keys(data).forEach(function(key) {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (typeof data[key] === "object") {
|
||||
output = {
|
||||
...output,
|
||||
@@ -107,7 +114,12 @@ function lokaliseTransform(data, original, file) {
|
||||
output[key] = lokaliseTransform(value, original, file);
|
||||
} else {
|
||||
output[key] = value.replace(re_key_reference, (match, key) => {
|
||||
const replace = key.split("::").reduce((tr, k) => tr[k], original);
|
||||
const replace = key.split("::").reduce((tr, k) => {
|
||||
if (!tr) {
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return tr[k];
|
||||
}, original);
|
||||
if (typeof replace !== "string") {
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
@@ -118,7 +130,7 @@ function lokaliseTransform(data, original, file) {
|
||||
return output;
|
||||
}
|
||||
|
||||
gulp.task("clean-translations", function() {
|
||||
gulp.task("clean-translations", function () {
|
||||
return del([workDir]);
|
||||
});
|
||||
|
||||
@@ -129,7 +141,7 @@ gulp.task("ensure-translations-build-dir", (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", function(cb) {
|
||||
gulp.task("create-test-metadata", function (cb) {
|
||||
fs.writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
@@ -147,7 +159,7 @@ gulp.task(
|
||||
return gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(
|
||||
transform(function(data, file) {
|
||||
transform(function (data, file) {
|
||||
return recursiveEmpty(data);
|
||||
})
|
||||
)
|
||||
@@ -165,28 +177,40 @@ gulp.task(
|
||||
* project is buildable immediately after merging new translation keys, since
|
||||
* the Lokalise update to translations/en.json will not happen immediately.
|
||||
*/
|
||||
gulp.task("build-master-translation", function() {
|
||||
gulp.task("build-master-translation", function () {
|
||||
const src = [path.join(paths.translations_src, "en.json")];
|
||||
|
||||
if (mergeBackend) {
|
||||
src.push(path.join(inBackendDir, "en.json"));
|
||||
}
|
||||
|
||||
return gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.src(src)
|
||||
.pipe(
|
||||
transform(function(data, file) {
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(rename("translationMaster.json"))
|
||||
.pipe(
|
||||
merge({
|
||||
fileName: "translationMaster.json",
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-merged-translations", function() {
|
||||
gulp.task("build-merged-translations", function () {
|
||||
return gulp
|
||||
.src([inDir + "/*.json", workDir + "/test.json"], { allowEmpty: true })
|
||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
||||
allowEmpty: true,
|
||||
})
|
||||
.pipe(
|
||||
transform(function(data, file) {
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
foreach(function(stream, file) {
|
||||
foreach(function (stream, file) {
|
||||
// For each language generate a merged json file. It begins with the master
|
||||
// translation as a failsafe for untranslated strings, and merges all parent
|
||||
// tags into one file for each specific subtag
|
||||
@@ -202,7 +226,10 @@ gulp.task("build-merged-translations", function() {
|
||||
if (lang === "test") {
|
||||
src.push(workDir + "/test.json");
|
||||
} else if (lang !== "en") {
|
||||
src.push(inDir + "/" + lang + ".json");
|
||||
src.push(inFrontendDir + "/" + lang + ".json");
|
||||
if (mergeBackend) {
|
||||
src.push(inBackendDir + "/" + lang + ".json");
|
||||
}
|
||||
}
|
||||
}
|
||||
return gulp
|
||||
@@ -223,7 +250,7 @@ var taskName;
|
||||
const splitTasks = [];
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
taskName = "build-translation-fragment-" + fragment;
|
||||
gulp.task(taskName, function() {
|
||||
gulp.task(taskName, function () {
|
||||
// Return only the translations for this fragment.
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
@@ -242,12 +269,12 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
});
|
||||
|
||||
taskName = "build-translation-core";
|
||||
gulp.task(taskName, function() {
|
||||
gulp.task(taskName, function () {
|
||||
// Remove the fragment translations from the core translation.
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data) => {
|
||||
transform((data, file) => {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
@@ -259,7 +286,7 @@ gulp.task(taskName, function() {
|
||||
|
||||
splitTasks.push(taskName);
|
||||
|
||||
gulp.task("build-flattened-translations", function() {
|
||||
gulp.task("build-flattened-translations", function () {
|
||||
// Flatten the split versions of our translations, and move them into outDir
|
||||
return gulp
|
||||
.src(
|
||||
@@ -269,7 +296,7 @@ gulp.task("build-flattened-translations", function() {
|
||||
{ base: workDir }
|
||||
)
|
||||
.pipe(
|
||||
transform(function(data) {
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
})
|
||||
@@ -351,7 +378,7 @@ gulp.task(
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform(function(data) {
|
||||
transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
|
@@ -28,7 +28,7 @@ const runDevServer = ({
|
||||
open: true,
|
||||
watchContentBase: true,
|
||||
contentBase,
|
||||
}).listen(port, listenHost, function(err) {
|
||||
}).listen(port, listenHost, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
@@ -150,9 +150,8 @@ gulp.task(
|
||||
|
||||
gulp.task("webpack-dev-server-gallery", () => {
|
||||
runDevServer({
|
||||
compiler: webpack(
|
||||
createGalleryConfig({ latestBuild: true, isProdBuild: false })
|
||||
),
|
||||
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
||||
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
||||
contentBase: paths.gallery_root,
|
||||
port: 8100,
|
||||
});
|
||||
|
@@ -1,20 +1,12 @@
|
||||
const webpack = require("webpack");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const WorkboxPlugin = require("workbox-webpack-plugin");
|
||||
const ManifestPlugin = require("webpack-manifest-plugin");
|
||||
const WorkerPlugin = require("worker-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const env = require("./env.js");
|
||||
const { babelLoaderConfig } = require("./babel.js");
|
||||
|
||||
let version = fs
|
||||
.readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8")
|
||||
.match(/\d{8}\.\d+/);
|
||||
if (!version) {
|
||||
throw Error("Version not found");
|
||||
}
|
||||
version = version[0];
|
||||
|
||||
const createWebpackConfig = ({
|
||||
entry,
|
||||
outputRoot,
|
||||
@@ -29,7 +21,9 @@ const createWebpackConfig = ({
|
||||
}
|
||||
return {
|
||||
mode: isProdBuild ? "production" : "development",
|
||||
devtool: isProdBuild ? "source-map" : "inline-cheap-module-source-map",
|
||||
devtool: isProdBuild
|
||||
? "cheap-module-source-map"
|
||||
: "eval-cheap-module-source-map",
|
||||
entry,
|
||||
module: {
|
||||
rules: [
|
||||
@@ -38,17 +32,11 @@ const createWebpackConfig = ({
|
||||
test: /\.css$/,
|
||||
use: "raw-loader",
|
||||
},
|
||||
{
|
||||
test: /\.(html)$/,
|
||||
use: {
|
||||
loader: "html-loader",
|
||||
options: {
|
||||
exportAsEs6Default: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
externals: {
|
||||
esprima: "esprima",
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
@@ -64,12 +52,14 @@ const createWebpackConfig = ({
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new WorkerPlugin(),
|
||||
new ManifestPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
__DEV__: !isProdBuild,
|
||||
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
|
||||
__VERSION__: JSON.stringify(version),
|
||||
__VERSION__: JSON.stringify(env.version()),
|
||||
__DEMO__: false,
|
||||
__BACKWARDS_COMPAT__: false,
|
||||
__STATIC_PATH__: "/static/",
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProdBuild ? "production" : "development"
|
||||
@@ -88,6 +78,10 @@ const createWebpackConfig = ({
|
||||
/@polymer\/font-roboto\/roboto\.js$/,
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@vaadin\/vaadin-material-styles\/font-roboto\.js$/,
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// Ignore mwc icons pointing at CDN.
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/@material\/mwc-icon\/mwc-icon-font\.js$/,
|
||||
@@ -96,14 +90,6 @@ const createWebpackConfig = ({
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
alias: {
|
||||
react: "preact-compat",
|
||||
"react-dom": "preact-compat",
|
||||
// Not necessary unless you consume a module using `createClass`
|
||||
"create-react-class": "preact-compat/lib/create-react-class",
|
||||
// Not necessary unless you consume a module requiring `react-dom-factories`
|
||||
"react-dom-factories": "preact-compat/lib/react-dom-factories",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
@@ -121,70 +107,28 @@ const createWebpackConfig = ({
|
||||
latestBuild ? "frontend_latest" : "frontend_es5"
|
||||
),
|
||||
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
||||
// For workerize loader
|
||||
// To silence warning in worker plugin
|
||||
globalObject: "self",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
const config = createWebpackConfig({
|
||||
return createWebpackConfig({
|
||||
entry: {
|
||||
service_worker: "./src/entrypoints/service_worker.ts",
|
||||
app: "./src/entrypoints/app.ts",
|
||||
authorize: "./src/entrypoints/authorize.ts",
|
||||
onboarding: "./src/entrypoints/onboarding.ts",
|
||||
core: "./src/entrypoints/core.ts",
|
||||
compatibility: "./src/entrypoints/compatibility.ts",
|
||||
"custom-panel": "./src/entrypoints/custom-panel.ts",
|
||||
"hass-icons": "./src/entrypoints/hass-icons.ts",
|
||||
},
|
||||
outputRoot: paths.root,
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
isStatsBuild,
|
||||
});
|
||||
|
||||
if (latestBuild) {
|
||||
// Create an object mapping browser urls to their paths during build
|
||||
const translationMetadata = require("../build-translations/translationMetadata.json");
|
||||
const workBoxTranslationsTemplatedURLs = {};
|
||||
const englishFilename = `en-${translationMetadata.translations.en.hash}.json`;
|
||||
|
||||
// core
|
||||
workBoxTranslationsTemplatedURLs[
|
||||
`/static/translations/${englishFilename}`
|
||||
] = `build-translations/output/${englishFilename}`;
|
||||
|
||||
translationMetadata.fragments.forEach((fragment) => {
|
||||
workBoxTranslationsTemplatedURLs[
|
||||
`/static/translations/${fragment}/${englishFilename}`
|
||||
] = `build-translations/output/${fragment}/${englishFilename}`;
|
||||
});
|
||||
|
||||
config.plugins.push(
|
||||
new WorkboxPlugin.InjectManifest({
|
||||
swSrc: "./src/entrypoints/service-worker-hass.js",
|
||||
swDest: "service_worker.js",
|
||||
importWorkboxFrom: "local",
|
||||
include: [/\.js$/],
|
||||
templatedURLs: {
|
||||
...workBoxTranslationsTemplatedURLs,
|
||||
"/static/icons/favicon-192x192.png":
|
||||
"public/icons/favicon-192x192.png",
|
||||
"/static/fonts/roboto/Roboto-Light.woff2":
|
||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Light.woff2",
|
||||
"/static/fonts/roboto/Roboto-Medium.woff2":
|
||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.woff2",
|
||||
"/static/fonts/roboto/Roboto-Regular.woff2":
|
||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff2",
|
||||
"/static/fonts/roboto/Roboto-Bold.woff2":
|
||||
"node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff2",
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
@@ -198,7 +142,7 @@ const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
},
|
||||
outputRoot: paths.demo_root,
|
||||
defineOverlay: {
|
||||
__VERSION__: JSON.stringify(`DEMO-${version}`),
|
||||
__VERSION__: JSON.stringify(`DEMO-${env.version()}`),
|
||||
__DEMO__: true,
|
||||
},
|
||||
isProdBuild,
|
||||
@@ -221,6 +165,9 @@ const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
outputRoot: paths.cast_root,
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
defineOverlay: {
|
||||
__BACKWARDS_COMPAT__: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -245,9 +192,6 @@ const createHassioConfig = ({ isProdBuild, latestBuild }) => {
|
||||
};
|
||||
|
||||
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
|
||||
if (!latestBuild) {
|
||||
throw new Error("Gallery only supports latest build!");
|
||||
}
|
||||
const config = createWebpackConfig({
|
||||
entry: {
|
||||
entrypoint: path.resolve(paths.gallery_dir, "src/entrypoint.js"),
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import "../../../src/resources/ha-style";
|
||||
import "../../../src/resources/roboto";
|
||||
import "../../../src/components/ha-iconset-svg";
|
||||
import "../../../src/resources/hass-icons";
|
||||
import "./layout/hc-connect";
|
||||
|
@@ -1,51 +1,53 @@
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import { Connection, Auth } from "home-assistant-js-websocket";
|
||||
import "@polymer/iron-icon";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import {
|
||||
enableWrite,
|
||||
askWrite,
|
||||
saveTokens,
|
||||
} from "../../../../src/common/auth/token_storage";
|
||||
import {
|
||||
ensureConnectedCastSession,
|
||||
castSendShowLovelaceView,
|
||||
} from "../../../../src/cast/receiver_messages";
|
||||
import "../../../../src/layouts/loading-screen";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
LovelaceConfig,
|
||||
getLovelaceCollection,
|
||||
getLegacyLovelaceCollection,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import "./hc-layout";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
||||
castSendShowLovelaceView,
|
||||
ensureConnectedCastSession,
|
||||
} from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
askWrite,
|
||||
enableWrite,
|
||||
saveTokens,
|
||||
} from "../../../../src/common/auth/token_storage";
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import {
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import "../../../../src/layouts/loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
|
||||
@customElement("hc-cast")
|
||||
class HcCast extends LitElement {
|
||||
@property() public auth!: Auth;
|
||||
|
||||
@property() public connection!: Connection;
|
||||
|
||||
@property() public castManager!: CastManager;
|
||||
|
||||
@property() private askWrite = false;
|
||||
|
||||
@property() private lovelaceConfig?: LovelaceConfig | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
return html`
|
||||
<loading-screen></loading-screen>>
|
||||
`;
|
||||
return html` <loading-screen></loading-screen>> `;
|
||||
}
|
||||
|
||||
const error =
|
||||
@@ -75,14 +77,12 @@ class HcCast extends LitElement {
|
||||
`
|
||||
: ""}
|
||||
${error
|
||||
? html`
|
||||
<div class="card-content">${error}</div>
|
||||
`
|
||||
? html` <div class="card-content">${error}</div> `
|
||||
: !this.castManager.status
|
||||
? html`
|
||||
<p class="center-item">
|
||||
<mwc-button raised @click=${this._handleLaunch}>
|
||||
<iron-icon icon="hass:cast"></iron-icon>
|
||||
<ha-icon icon="hass:cast"></ha-icon>
|
||||
Start Casting
|
||||
</mwc-button>
|
||||
</p>
|
||||
@@ -120,7 +120,7 @@ class HcCast extends LitElement {
|
||||
${this.castManager.status
|
||||
? html`
|
||||
<mwc-button @click=${this._handleLaunch}>
|
||||
<iron-icon icon="hass:cast-connected"></iron-icon>
|
||||
<ha-icon icon="hass:cast-connected"></ha-icon>
|
||||
Manage
|
||||
</mwc-button>
|
||||
`
|
||||
@@ -242,7 +242,7 @@ class HcCast extends LitElement {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
mwc-button iron-icon {
|
||||
mwc-button ha-icon {
|
||||
margin-right: 8px;
|
||||
height: 18px;
|
||||
}
|
||||
|
@@ -1,35 +1,35 @@
|
||||
import {
|
||||
LitElement,
|
||||
customElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import {
|
||||
getAuth,
|
||||
createConnection,
|
||||
Auth,
|
||||
getAuthOptions,
|
||||
ERR_HASS_HOST_REQUIRED,
|
||||
ERR_INVALID_HTTPS_TO_HTTP,
|
||||
Connection,
|
||||
ERR_CANNOT_CONNECT,
|
||||
ERR_INVALID_AUTH,
|
||||
} from "home-assistant-js-websocket";
|
||||
import "@polymer/iron-icon";
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
Auth,
|
||||
Connection,
|
||||
createConnection,
|
||||
ERR_CANNOT_CONNECT,
|
||||
ERR_HASS_HOST_REQUIRED,
|
||||
ERR_INVALID_AUTH,
|
||||
ERR_INVALID_HTTPS_TO_HTTP,
|
||||
getAuth,
|
||||
getAuthOptions,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
|
||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
loadTokens,
|
||||
saveTokens,
|
||||
} from "../../../../src/common/auth/token_storage";
|
||||
import "../../../../src/components/ha-icon";
|
||||
import "../../../../src/layouts/loading-screen";
|
||||
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
|
||||
import "./hc-layout";
|
||||
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
|
||||
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
||||
import "./hc-layout";
|
||||
|
||||
const seeFAQ = (qid) => html`
|
||||
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
|
||||
@@ -61,13 +61,19 @@ const INTRO = html`
|
||||
@customElement("hc-connect")
|
||||
export class HcConnect extends LitElement {
|
||||
@property() private loading = false;
|
||||
|
||||
// If we had stored credentials but we cannot connect,
|
||||
// show a screen asking retry or logout.
|
||||
@property() private cannotConnect = false;
|
||||
|
||||
@property() private error?: string | TemplateResult;
|
||||
|
||||
@property() private auth?: Auth;
|
||||
|
||||
@property() private connection?: Connection;
|
||||
|
||||
@property() private castManager?: CastManager | null;
|
||||
|
||||
private openDemo = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -92,9 +98,7 @@ export class HcConnect extends LitElement {
|
||||
}
|
||||
|
||||
if (this.castManager === undefined || this.loading) {
|
||||
return html`
|
||||
<loading-screen></loading-screen>
|
||||
`;
|
||||
return html` <loading-screen></loading-screen> `;
|
||||
}
|
||||
|
||||
if (this.castManager === null) {
|
||||
@@ -127,20 +131,16 @@ export class HcConnect extends LitElement {
|
||||
@keydown=${this._handleInputKeyDown}
|
||||
></paper-input>
|
||||
</p>
|
||||
${this.error
|
||||
? html`
|
||||
<p class="error">${this.error}</p>
|
||||
`
|
||||
: ""}
|
||||
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._handleDemo}>
|
||||
Show Demo
|
||||
<iron-icon
|
||||
<ha-icon
|
||||
.icon=${this.castManager.castState === "CONNECTED"
|
||||
? "hass:cast-connected"
|
||||
: "hass:cast"}
|
||||
></iron-icon>
|
||||
></ha-icon>
|
||||
</mwc-button>
|
||||
<div class="spacer"></div>
|
||||
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
|
||||
@@ -184,7 +184,7 @@ export class HcConnect extends LitElement {
|
||||
this.castManager = null;
|
||||
}
|
||||
);
|
||||
registerServiceWorker(false);
|
||||
registerServiceWorker(this, false);
|
||||
}
|
||||
|
||||
private async _handleDemo() {
|
||||
@@ -211,7 +211,8 @@ export class HcConnect extends LitElement {
|
||||
if (value === "") {
|
||||
this.error = "Please enter a Home Assistant URL.";
|
||||
return;
|
||||
} else if (value.indexOf("://") === -1) {
|
||||
}
|
||||
if (value.indexOf("://") === -1) {
|
||||
this.error =
|
||||
"Please enter your full URL, including the protocol part (https://).";
|
||||
return;
|
||||
@@ -315,7 +316,7 @@ export class HcConnect extends LitElement {
|
||||
color: darkred;
|
||||
}
|
||||
|
||||
mwc-button iron-icon {
|
||||
mwc-button ha-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
|
@@ -1,25 +1,28 @@
|
||||
import {
|
||||
customElement,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
CSSResult,
|
||||
css,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import {
|
||||
Auth,
|
||||
Connection,
|
||||
HassUser,
|
||||
getUser,
|
||||
HassUser,
|
||||
} from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../src/components/ha-card";
|
||||
|
||||
@customElement("hc-layout")
|
||||
class HcLayout extends LitElement {
|
||||
@property() public subtitle?: string | undefined;
|
||||
|
||||
@property() public auth?: Auth;
|
||||
|
||||
@property() public connection?: Connection;
|
||||
|
||||
@property() public user?: HassUser;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -37,11 +40,7 @@ class HcLayout extends LitElement {
|
||||
this.auth.data.hassUrl.indexOf("//") + 2
|
||||
)}</a
|
||||
>
|
||||
${this.user
|
||||
? html`
|
||||
– ${this.user.name}
|
||||
`
|
||||
: ""}
|
||||
${this.user ? html` – ${this.user.name} ` : ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
|
@@ -1 +1,2 @@
|
||||
/* eslint-disable no-undef */
|
||||
export const castContext = cast.framework.CastReceiverContext.getInstance();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Entity, convertEntities } from "../../../../src/fake_data/entity";
|
||||
import { convertEntities, Entity } from "../../../../src/fake_data/entity";
|
||||
|
||||
export const castDemoEntities: () => Entity[] = () =>
|
||||
convertEntities({
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
LovelaceConfig,
|
||||
LovelaceCardConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { CAST_NS } from "../../../src/cast/const";
|
||||
import { HassMessage } from "../../../src/cast/receiver_messages";
|
||||
import "../../../src/resources/custom-card-support";
|
||||
import { castContext } from "./cast_context";
|
||||
import { ReceivedMessage } from "./types";
|
||||
import { HassMessage } from "../../../src/cast/receiver_messages";
|
||||
import { HcMain } from "./layout/hc-main";
|
||||
import { CAST_NS } from "../../../src/cast/const";
|
||||
import { ReceivedMessage } from "./types";
|
||||
|
||||
const controller = new HcMain();
|
||||
document.body.append(controller);
|
||||
|
@@ -1,19 +1,20 @@
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import "./hc-lovelace";
|
||||
import { customElement, TemplateResult, html, property } from "lit-element";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
} from "../../../../src/fake_data/provide_hass";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { castDemoEntities } from "../demo/cast-demo-entities";
|
||||
import { castDemoLovelace } from "../demo/cast-demo-lovelace";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import "./hc-lovelace";
|
||||
|
||||
@customElement("hc-demo")
|
||||
class HcDemo extends HassElement {
|
||||
@property() public lovelacePath!: string;
|
||||
|
||||
@property() private _lovelaceConfig?: LovelaceConfig;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -28,6 +29,7 @@ class HcDemo extends HassElement {
|
||||
></hc-lovelace>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this._initialize();
|
||||
|
@@ -1,17 +1,18 @@
|
||||
import {
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
CSSResult,
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
@customElement("hc-launch-screen")
|
||||
class HcLaunchScreen extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() public error?: string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -22,11 +23,7 @@ class HcLaunchScreen extends LitElement {
|
||||
/>
|
||||
<div class="status">
|
||||
${this.hass ? "Connected" : "Not Connected"}
|
||||
${this.error
|
||||
? html`
|
||||
<p>Error: ${this.error}</p>
|
||||
`
|
||||
: ""}
|
||||
${this.error ? html` <p>Error: ${this.error}</p> ` : ""}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import {
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
CSSResult,
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
import "../../../../src/panels/lovelace/views/hui-panel-view";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-panel-view";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
@customElement("hc-lovelace")
|
||||
@@ -46,6 +46,7 @@ class HcLovelace extends LitElement {
|
||||
? html`
|
||||
<hui-panel-view
|
||||
.hass=${this.hass}
|
||||
.lovelace=${lovelace}
|
||||
.config=${this.lovelaceConfig.views[index]}
|
||||
></hui-panel-view>
|
||||
`
|
||||
|
@@ -1,31 +1,31 @@
|
||||
import {
|
||||
getAuth,
|
||||
createConnection,
|
||||
getAuth,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { customElement, TemplateResult, html, property } from "lit-element";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import {
|
||||
HassMessage,
|
||||
ConnectMessage,
|
||||
ShowLovelaceViewMessage,
|
||||
GetStatusMessage,
|
||||
ShowDemoMessage,
|
||||
} from "../../../../src/cast/receiver_messages";
|
||||
import {
|
||||
LovelaceConfig,
|
||||
getLovelaceCollection,
|
||||
fetchResources,
|
||||
LegacyLovelaceConfig,
|
||||
getLegacyLovelaceCollection,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import "./hc-launch-screen";
|
||||
import { castContext } from "../cast_context";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { CAST_NS } from "../../../../src/cast/const";
|
||||
import {
|
||||
ConnectMessage,
|
||||
GetStatusMessage,
|
||||
HassMessage,
|
||||
ShowDemoMessage,
|
||||
ShowLovelaceViewMessage,
|
||||
} from "../../../../src/cast/receiver_messages";
|
||||
import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages";
|
||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import {
|
||||
fetchResources,
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
LegacyLovelaceConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import { castContext } from "../cast_context";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
let resourcesLoaded = false;
|
||||
|
||||
@@ -40,6 +40,7 @@ export class HcMain extends HassElement {
|
||||
@property() private _error?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
|
||||
private _urlPath?: string | null;
|
||||
|
||||
public processIncomingMessage(msg: HassMessage) {
|
||||
@@ -52,16 +53,14 @@ export class HcMain extends HassElement {
|
||||
} else if (msg.type === "show_demo") {
|
||||
this._handleShowDemo(msg);
|
||||
} else {
|
||||
// tslint:disable-next-line: no-console
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn("unknown msg type", msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._showDemo) {
|
||||
return html`
|
||||
<hc-demo .lovelacePath=${this._lovelacePath}></hc-demo>
|
||||
`;
|
||||
return html` <hc-demo .lovelacePath=${this._lovelacePath}></hc-demo> `;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -91,15 +90,17 @@ export class HcMain extends HassElement {
|
||||
super.firstUpdated(changedProps);
|
||||
import("../second-load");
|
||||
window.addEventListener("location-changed", () => {
|
||||
if (location.pathname.startsWith("/lovelace/")) {
|
||||
this._lovelacePath = location.pathname.substr(10);
|
||||
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
||||
if (location.pathname.startsWith(panelPath)) {
|
||||
this._lovelacePath = location.pathname.substr(panelPath.length);
|
||||
this._sendStatus();
|
||||
}
|
||||
});
|
||||
document.body.addEventListener("click", (ev) => {
|
||||
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
||||
const href = isNavigationClick(ev);
|
||||
if (href && href.startsWith("/lovelace/")) {
|
||||
this._lovelacePath = href.substr(10);
|
||||
if (href && href.startsWith(panelPath)) {
|
||||
this._lovelacePath = href.substr(panelPath.length);
|
||||
this._sendStatus();
|
||||
}
|
||||
});
|
||||
@@ -171,10 +172,10 @@ export class HcMain extends HassElement {
|
||||
this._error = "Cannot show Lovelace because we're not connected.";
|
||||
return;
|
||||
}
|
||||
if (msg.urlPath === "lovelace") {
|
||||
msg.urlPath = null;
|
||||
}
|
||||
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
|
||||
if (msg.urlPath === "lovelace") {
|
||||
msg.urlPath = null;
|
||||
}
|
||||
this._urlPath = msg.urlPath;
|
||||
if (this._unsubLovelace) {
|
||||
this._unsubLovelace();
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../src/resources/hass-icons";
|
||||
import "../../../src/resources/roboto";
|
||||
import "../../../src/components/ha-iconset-svg";
|
||||
import "./layout/hc-lovelace";
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 805 B After Width: | Height: | Size: 803 B |
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -7,5 +7,5 @@ set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
STATS=1 NODE_ENV=production ../node_modules/.bin/webpack --profile --json > compilation-stats.json
|
||||
npx webpack-bundle-analyzer compilation-stats.json dist
|
||||
npx webpack-bundle-analyzer compilation-stats.json dist/frontend_latest
|
||||
rm compilation-stats.json
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { DemoConfig } from "../types";
|
||||
import { demoLovelaceArsaboo } from "./lovelace";
|
||||
import { demoEntitiesArsaboo } from "./entities";
|
||||
import { demoLovelaceArsaboo } from "./lovelace";
|
||||
import { demoThemeArsaboo } from "./theme";
|
||||
|
||||
export const demoArsaboo: DemoConfig = {
|
||||
|
@@ -21,7 +21,9 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
),
|
||||
];
|
||||
|
||||
export let selectedDemoConfigIndex: number = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
||||
selectedDemoConfigIndex
|
||||
]();
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { DemoConfig } from "../types";
|
||||
import { demoLovelaceJimpower } from "./lovelace";
|
||||
import { demoEntitiesJimpower } from "./entities";
|
||||
import { demoLovelaceJimpower } from "./lovelace";
|
||||
import { demoThemeJimpower } from "./theme";
|
||||
|
||||
export const demoJimpower: DemoConfig = {
|
||||
|
@@ -33,7 +33,7 @@ export const demoThemeJimpower = () => ({
|
||||
"label-badge-border-color": "green",
|
||||
"paper-listbox-color": "var(--primary-color)",
|
||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
||||
"paper-card-background-color": "#434954",
|
||||
"card-background-color": "#434954",
|
||||
"label-badge-text-color": "var(--primary-text-color)",
|
||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { DemoConfig } from "../types";
|
||||
import { demoLovelaceKernehed } from "./lovelace";
|
||||
import { demoEntitiesKernehed } from "./entities";
|
||||
import { demoLovelaceKernehed } from "./lovelace";
|
||||
import { demoThemeKernehed } from "./theme";
|
||||
|
||||
export const demoKernehed: DemoConfig = {
|
||||
|
@@ -34,7 +34,7 @@ export const demoThemeKernehed = () => ({
|
||||
"label-badge-border-color": "green",
|
||||
"paper-listbox-color": "#777777",
|
||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
||||
"paper-card-background-color": "#292929",
|
||||
"card-background-color": "#292929",
|
||||
"label-badge-text-color": "var(--primary-text-color)",
|
||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { DemoConfig } from "../types";
|
||||
import { demoLovelaceTeachingbirds } from "./lovelace";
|
||||
import { demoEntitiesTeachingbirds } from "./entities";
|
||||
import { demoLovelaceTeachingbirds } from "./lovelace";
|
||||
import { demoThemeTeachingbirds } from "./theme";
|
||||
|
||||
export const demoTeachingbirds: DemoConfig = {
|
||||
|
@@ -63,8 +63,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
elements: [
|
||||
{
|
||||
style: {
|
||||
"--iron-icon-width": "100px",
|
||||
"--iron-icon-height": "100px",
|
||||
"--mdc-icon-size": "100%",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
},
|
||||
|
@@ -13,7 +13,7 @@ export const demoThemeTeachingbirds = () => ({
|
||||
"paper-listbox-color": "#FFFFFF",
|
||||
"paper-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
||||
"switch-unchecked-track-color": "var(--primary-text-color)",
|
||||
"paper-card-background-color": "#4e4e4e",
|
||||
"card-background-color": "#4e4e4e",
|
||||
"label-badge-text-color": "var(--text-primary-color)",
|
||||
"primary-background-color": "#303030",
|
||||
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
||||
|