Compare commits
337 Commits
delay-init
...
20231030.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b553a3a4b | ||
![]() |
8e1e75dee1 | ||
![]() |
d27b4e04a9 | ||
![]() |
36f7b34ac5 | ||
![]() |
c0ff24bf1b | ||
![]() |
abb5aa348f | ||
![]() |
37ab5cbdc3 | ||
![]() |
42be3b331c | ||
![]() |
71e05f79c2 | ||
![]() |
c5a0a5bbbf | ||
![]() |
c3d809fcf3 | ||
![]() |
cbd0c39091 | ||
![]() |
113eb5be24 | ||
![]() |
c497669fd3 | ||
![]() |
c32ca5885b | ||
![]() |
3c792c4019 | ||
![]() |
9762e61ee2 | ||
![]() |
c09c39998b | ||
![]() |
d37e29c247 | ||
![]() |
51f22cd74a | ||
![]() |
b57dc968bd | ||
![]() |
e2e8cb785a | ||
![]() |
3d674cf237 | ||
![]() |
9961a4ae3f | ||
![]() |
c25755bfcd | ||
![]() |
40983619d6 | ||
![]() |
54758b5962 | ||
![]() |
4f09485b20 | ||
![]() |
9207f6c407 | ||
![]() |
1a2312460a | ||
![]() |
951b88ab4c | ||
![]() |
d3cc57d8b4 | ||
![]() |
4e9b118728 | ||
![]() |
4e6e924a40 | ||
![]() |
f1748e4dd5 | ||
![]() |
d491d8f5ac | ||
![]() |
cf0fde0f3c | ||
![]() |
a7dc2cfaa6 | ||
![]() |
d8c7db6ebf | ||
![]() |
c3743b57ea | ||
![]() |
e16a101de8 | ||
![]() |
94ad47c60e | ||
![]() |
184ef7b7ff | ||
![]() |
81053f2e07 | ||
![]() |
e8b4eeec67 | ||
![]() |
b0b7e77e28 | ||
![]() |
073ead5828 | ||
![]() |
763f80b46a | ||
![]() |
0c2531a7ee | ||
![]() |
8d2ec8098c | ||
![]() |
fdaefadd18 | ||
![]() |
d2caed2b68 | ||
![]() |
402d443843 | ||
![]() |
399f12194a | ||
![]() |
a745539c33 | ||
![]() |
f6fddbc6ec | ||
![]() |
01f51f3247 | ||
![]() |
80112bb662 | ||
![]() |
7ce7cbb755 | ||
![]() |
464ecffda7 | ||
![]() |
33e0c691c7 | ||
![]() |
d94f7c90c0 | ||
![]() |
d8d16c4d5f | ||
![]() |
1cb238ec2a | ||
![]() |
3e6ab8b179 | ||
![]() |
10bcaadcdb | ||
![]() |
67517643ef | ||
![]() |
eb35eb3de5 | ||
![]() |
8350d71f6e | ||
![]() |
c840f1cbb1 | ||
![]() |
8efc0816bb | ||
![]() |
b12e4989db | ||
![]() |
2b67731906 | ||
![]() |
ccba7a7623 | ||
![]() |
c0dfc9f73e | ||
![]() |
be1624f66f | ||
![]() |
32edbd7b33 | ||
![]() |
18827db9ba | ||
![]() |
191250a66a | ||
![]() |
1fdf609606 | ||
![]() |
6ffc0625d3 | ||
![]() |
c9f5d16745 | ||
![]() |
eb4afedf2e | ||
![]() |
2b9540fe03 | ||
![]() |
53b8d1bb0a | ||
![]() |
0ff5bffd0c | ||
![]() |
82a464f50f | ||
![]() |
fdddc18291 | ||
![]() |
463a3244cf | ||
![]() |
6cae11f0a6 | ||
![]() |
65112b36ce | ||
![]() |
58625d2a9d | ||
![]() |
9bafbdd989 | ||
![]() |
eedb42b2f3 | ||
![]() |
4354ad3807 | ||
![]() |
aeaf091b50 | ||
![]() |
c6be4d6f4d | ||
![]() |
c48b620e03 | ||
![]() |
77e05decdf | ||
![]() |
b24e99c56c | ||
![]() |
768344c3f7 | ||
![]() |
03a21d5519 | ||
![]() |
1247a5c8d3 | ||
![]() |
db8287df89 | ||
![]() |
71edbd6352 | ||
![]() |
9d87a66908 | ||
![]() |
11d62cece2 | ||
![]() |
f15a65f5a6 | ||
![]() |
a03d3f796b | ||
![]() |
d0f5b0e864 | ||
![]() |
e4a67dd555 | ||
![]() |
f72ab94742 | ||
![]() |
b521be6d3b | ||
![]() |
3fa7001be6 | ||
![]() |
b45226509b | ||
![]() |
2a5f8097bc | ||
![]() |
0ffe0f38e1 | ||
![]() |
c48491088e | ||
![]() |
f115e4025d | ||
![]() |
7be8a799aa | ||
![]() |
d992b2d40b | ||
![]() |
96fbd8aefb | ||
![]() |
17df761a1b | ||
![]() |
79b2fa96ed | ||
![]() |
c14e3333cf | ||
![]() |
4af0ecbaa8 | ||
![]() |
ce11301516 | ||
![]() |
16766f8878 | ||
![]() |
5e933e8e15 | ||
![]() |
7eb92be84a | ||
![]() |
60ec4d31db | ||
![]() |
3526ba308f | ||
![]() |
02a212a47d | ||
![]() |
49f88a98a5 | ||
![]() |
feb371839c | ||
![]() |
24c37f5293 | ||
![]() |
8f3fea5a33 | ||
![]() |
b2bc529d7b | ||
![]() |
ad68782b79 | ||
![]() |
f432528388 | ||
![]() |
f98c0769b0 | ||
![]() |
b2cb0d8e0f | ||
![]() |
4c7c04bdc0 | ||
![]() |
ffb7469a7e | ||
![]() |
d88831b719 | ||
![]() |
3b2f6d71f5 | ||
![]() |
a08185a1a5 | ||
![]() |
7ee91ca8fc | ||
![]() |
b6fe0cfa1b | ||
![]() |
c3a9682861 | ||
![]() |
62d21bea4f | ||
![]() |
434b9595c0 | ||
![]() |
a0f1b7f365 | ||
![]() |
628c2c39cf | ||
![]() |
4b885cbd93 | ||
![]() |
02d9786f8c | ||
![]() |
88d14cd7b5 | ||
![]() |
4ea8f599cf | ||
![]() |
37ef444180 | ||
![]() |
4253feb8a2 | ||
![]() |
ce33cf7ff3 | ||
![]() |
faa4455951 | ||
![]() |
08d8b43f44 | ||
![]() |
15c67fe299 | ||
![]() |
a10ec1f53c | ||
![]() |
1b220abf70 | ||
![]() |
607175706b | ||
![]() |
f8966a2114 | ||
![]() |
4c94ac5dda | ||
![]() |
6d1e923b83 | ||
![]() |
2c743b7b56 | ||
![]() |
6686da1f24 | ||
![]() |
6bdeb45f6b | ||
![]() |
9f05a9679b | ||
![]() |
51a6376991 | ||
![]() |
c5056eb4d2 | ||
![]() |
79f3759756 | ||
![]() |
6c3b748279 | ||
![]() |
4293192e74 | ||
![]() |
ceaceaf47b | ||
![]() |
479a625662 | ||
![]() |
095d171a61 | ||
![]() |
8c3a7de6d9 | ||
![]() |
84e743c4c0 | ||
![]() |
51f8d91ddf | ||
![]() |
8f1a6ef1b1 | ||
![]() |
b3f1783269 | ||
![]() |
7e630d0fc5 | ||
![]() |
a4533251a1 | ||
![]() |
659db109aa | ||
![]() |
d3fd27910a | ||
![]() |
eae3c1309f | ||
![]() |
4a5b67e320 | ||
![]() |
86c014b677 | ||
![]() |
5a6d6dc7d3 | ||
![]() |
294df396f4 | ||
![]() |
cc01e8d6a8 | ||
![]() |
a3532a41da | ||
![]() |
ae35fd1eb8 | ||
![]() |
63095f1501 | ||
![]() |
bf9e2cd404 | ||
![]() |
5b7ef941e4 | ||
![]() |
352e721d0c | ||
![]() |
220b4794c5 | ||
![]() |
811ebde42a | ||
![]() |
bfeee618f4 | ||
![]() |
db9b16e9f5 | ||
![]() |
7861d813b1 | ||
![]() |
d7760c4b7a | ||
![]() |
a60a721ea5 | ||
![]() |
36219e1cb4 | ||
![]() |
7fdbc9dd32 | ||
![]() |
334be93254 | ||
![]() |
c14a6d59e2 | ||
![]() |
7a8139b650 | ||
![]() |
9d2a443217 | ||
![]() |
484b166233 | ||
![]() |
530208cb6a | ||
![]() |
b534ff8ca3 | ||
![]() |
02bd50c434 | ||
![]() |
9a84ce7b81 | ||
![]() |
6e00be6684 | ||
![]() |
91ec43b9bc | ||
![]() |
4a4d9a08d5 | ||
![]() |
0c32d1eb4e | ||
![]() |
f43171f91c | ||
![]() |
48593eee0d | ||
![]() |
c106a0ac85 | ||
![]() |
e1a71fbfaa | ||
![]() |
0489d8922e | ||
![]() |
d7f1e9d091 | ||
![]() |
32bc8bd01d | ||
![]() |
242b018ece | ||
![]() |
c25447d001 | ||
![]() |
d2d718475f | ||
![]() |
8e1e42cd50 | ||
![]() |
014f9b8b73 | ||
![]() |
774c7e275c | ||
![]() |
75c43d15e1 | ||
![]() |
e288b003d8 | ||
![]() |
4aa8518ed6 | ||
![]() |
6acbf6395c | ||
![]() |
2030feabf7 | ||
![]() |
46d1dbcb47 | ||
![]() |
2f6297ec17 | ||
![]() |
a3400a2f9c | ||
![]() |
c345f41416 | ||
![]() |
292cdc7621 | ||
![]() |
c5ba74e0b4 | ||
![]() |
41b24de559 | ||
![]() |
4fe7b18161 | ||
![]() |
399a979c33 | ||
![]() |
03c5482860 | ||
![]() |
a116a50604 | ||
![]() |
0dfa292c40 | ||
![]() |
5914a6c1a4 | ||
![]() |
8daff17d6a | ||
![]() |
59bd852e7a | ||
![]() |
246fe2861e | ||
![]() |
dbf623ada2 | ||
![]() |
c848356a6d | ||
![]() |
47022d3a04 | ||
![]() |
d732bd4776 | ||
![]() |
5a5265723c | ||
![]() |
30c6e4e35e | ||
![]() |
c5f909d89f | ||
![]() |
13a691606f | ||
![]() |
44748df3ac | ||
![]() |
4f3dc82fd9 | ||
![]() |
b2e260d6ba | ||
![]() |
7111a21173 | ||
![]() |
ad51d313a1 | ||
![]() |
60345f3fe8 | ||
![]() |
956723cf15 | ||
![]() |
dd6a69ea03 | ||
![]() |
98bd08c9dd | ||
![]() |
6b31c07459 | ||
![]() |
c3b41afb68 | ||
![]() |
46d8f2eefb | ||
![]() |
0ffc7b59d6 | ||
![]() |
74be4ae20a | ||
![]() |
5455ce2e0f | ||
![]() |
01405d96b6 | ||
![]() |
965f893a65 | ||
![]() |
0b6813d9dc | ||
![]() |
cbd424ff5a | ||
![]() |
b899e39a9e | ||
![]() |
49a66961e2 | ||
![]() |
c69fb77b62 | ||
![]() |
d794ec3408 | ||
![]() |
d8c98d8f96 | ||
![]() |
acb32ae5c8 | ||
![]() |
c567a61dd7 | ||
![]() |
2a8d98307e | ||
![]() |
5aaf0cd579 | ||
![]() |
f38e4dcf54 | ||
![]() |
1df1ce5423 | ||
![]() |
a68381a4d9 | ||
![]() |
f7604b136e | ||
![]() |
362d950515 | ||
![]() |
22f9dbd65d | ||
![]() |
579050bfc7 | ||
![]() |
6b33b4e656 | ||
![]() |
dac7c0f5fd | ||
![]() |
aaceff0d23 | ||
![]() |
48e5cc6b63 | ||
![]() |
799a0933ba | ||
![]() |
940618f72d | ||
![]() |
d8ff69b65d | ||
![]() |
391aca6388 | ||
![]() |
071d078e84 | ||
![]() |
61982bcb77 | ||
![]() |
1c79fcc244 | ||
![]() |
f3513e3e52 | ||
![]() |
b3b10fa2ef | ||
![]() |
0ffabcc055 | ||
![]() |
9da8499004 | ||
![]() |
04d1fccb87 | ||
![]() |
fdf1eb5170 | ||
![]() |
4e2877b035 | ||
![]() |
467059f515 | ||
![]() |
e6db63bb63 | ||
![]() |
78ddec2c8c | ||
![]() |
9217d5bf40 | ||
![]() |
90d01e4b63 | ||
![]() |
e7960bf8c0 | ||
![]() |
bf12eaa1b3 | ||
![]() |
6179c75182 | ||
![]() |
40bb6566b8 | ||
![]() |
db272e3e18 | ||
![]() |
35496ead23 | ||
![]() |
41403a5d35 | ||
![]() |
8acf557137 | ||
![]() |
4e62370d18 | ||
![]() |
f90ab60354 | ||
![]() |
d187aa0ac6 | ||
![]() |
96597b3963 |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,9 +2,7 @@
|
|||||||
You are amazing! Thanks for contributing to our project!
|
You are amazing! Thanks for contributing to our project!
|
||||||
Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
|
Please, DO NOT DELETE ANY TEXT from this template! (unless instructed).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Breaking change
|
## Breaking change
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
If your PR contains a breaking change for existing users, it is important
|
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.
|
to tell them what breaks, how to make it work again and why we did this.
|
||||||
@@ -13,8 +11,8 @@
|
|||||||
Note: Remove this section if this PR is NOT a breaking change.
|
Note: Remove this section if this PR is NOT a breaking change.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Proposed change
|
|
||||||
|
|
||||||
|
## Proposed change
|
||||||
<!--
|
<!--
|
||||||
Describe the big picture of your changes here to communicate to the
|
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
|
maintainers why we should accept this pull request. If it fixes a bug
|
||||||
@@ -22,8 +20,8 @@
|
|||||||
in the additional information section.
|
in the additional information section.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Type of change
|
|
||||||
|
|
||||||
|
## Type of change
|
||||||
<!--
|
<!--
|
||||||
What type of change does your PR introduce to the Home Assistant frontend?
|
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!
|
||||||
@@ -38,7 +36,6 @@
|
|||||||
- [ ] Code quality improvements to existing code or addition of tests
|
- [ ] Code quality improvements to existing code or addition of tests
|
||||||
|
|
||||||
## Example configuration
|
## Example configuration
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Supplying a configuration snippet, makes it easier for a maintainer to test
|
Supplying a configuration snippet, makes it easier for a maintainer to test
|
||||||
your PR.
|
your PR.
|
||||||
@@ -49,7 +46,6 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Additional information
|
## Additional information
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Details are important, and help maintainers processing your PR.
|
Details are important, and help maintainers processing your PR.
|
||||||
Please be sure to fill out additional details, if applicable.
|
Please be sure to fill out additional details, if applicable.
|
||||||
@@ -60,7 +56,6 @@
|
|||||||
- Link to documentation pull request:
|
- Link to documentation pull request:
|
||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Put an `x` in the boxes that apply. You can also fill these out after
|
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.
|
creating the PR. If you're unsure about any of them, don't hesitate to ask.
|
||||||
|
8
.github/workflows/cast_deployment.yaml
vendored
@@ -21,12 +21,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -57,12 +57,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
28
.github/workflows/ci.yaml
vendored
@@ -24,9 +24,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -55,9 +55,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -73,9 +73,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -85,15 +85,21 @@ jobs:
|
|||||||
run: ./node_modules/.bin/gulp build-app
|
run: ./node_modules/.bin/gulp build-app
|
||||||
env:
|
env:
|
||||||
IS_TEST: "true"
|
IS_TEST: "true"
|
||||||
|
- name: Upload bundle stats
|
||||||
|
uses: actions/upload-artifact@v3.1.3
|
||||||
|
with:
|
||||||
|
name: frontend-bundle-stats
|
||||||
|
path: build/stats/*.json
|
||||||
|
if-no-files-found: error
|
||||||
supervisor:
|
supervisor:
|
||||||
name: Build supervisor
|
name: Build supervisor
|
||||||
needs: [lint, test]
|
needs: [lint, test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -103,3 +109,9 @@ jobs:
|
|||||||
run: ./node_modules/.bin/gulp build-hassio
|
run: ./node_modules/.bin/gulp build-hassio
|
||||||
env:
|
env:
|
||||||
IS_TEST: "true"
|
IS_TEST: "true"
|
||||||
|
- name: Upload bundle stats
|
||||||
|
uses: actions/upload-artifact@v3.1.3
|
||||||
|
with:
|
||||||
|
name: supervisor-bundle-stats
|
||||||
|
path: build/stats/*.json
|
||||||
|
if-no-files-found: error
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
|
8
.github/workflows/demo_deployment.yaml
vendored
@@ -22,12 +22,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -58,12 +58,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/design_deployment.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/design_preview.yaml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
8
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -57,14 +57,14 @@ jobs:
|
|||||||
run: tar -czvf translations.tar.gz translations
|
run: tar -czvf translations.tar.gz translations
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3.1.3
|
||||||
with:
|
with:
|
||||||
name: wheels
|
name: wheels
|
||||||
path: dist/home_assistant_frontend*.whl
|
path: dist/home_assistant_frontend*.whl
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload translations
|
- name: Upload translations
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3.1.3
|
||||||
with:
|
with:
|
||||||
name: translations
|
name: translations
|
||||||
path: translations.tar.gz
|
path: translations.tar.gz
|
||||||
|
25
.github/workflows/relative-ci.yaml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: RelativeCI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: [CI]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
upload:
|
||||||
|
name: Upload stats
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
bundle: [frontend, supervisor]
|
||||||
|
build: [modern, legacy]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Send bundle stats and build information to RelativeCI
|
||||||
|
uses: relative-ci/agent-action@v2.1.10
|
||||||
|
with:
|
||||||
|
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
||||||
|
token: ${{ github.token }}
|
||||||
|
artifactName: ${{ format('{0}-bundle-stats', matrix.bundle) }}
|
||||||
|
webpackStatsFile: ${{ format('{0}-{1}.json', matrix.bundle, matrix.build) }}
|
6
.github/workflows/release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Verify version
|
- name: Verify version
|
||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v4.0.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: home-assistant/wheels@2023.04.0
|
uses: home-assistant/wheels@2023.10.5
|
||||||
with:
|
with:
|
||||||
abi: cp311
|
abi: cp311
|
||||||
tag: musllinux_1_2
|
tag: musllinux_1_2
|
||||||
|
2
.github/workflows/translations.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v4.0.0
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
3
.gitignore
vendored
@@ -47,3 +47,6 @@ src/cast/dev_const.ts
|
|||||||
|
|
||||||
# Home Assistant config
|
# Home Assistant config
|
||||||
/config/
|
/config/
|
||||||
|
|
||||||
|
# Jetbrains
|
||||||
|
/.idea/
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
CLA.md
|
CLA.md
|
||||||
CODE_OF_CONDUCT.md
|
CODE_OF_CONDUCT.md
|
||||||
LICENSE.md
|
LICENSE.md
|
||||||
|
PULL_REQUEST_TEMPLATE.md
|
@@ -8,4 +8,4 @@ plugins:
|
|||||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||||
spec: "@yarnpkg/plugin-interactive-tools"
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
yarnPath: .yarn/releases/yarn-3.6.4.cjs
|
||||||
|
@@ -98,7 +98,7 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
|||||||
"@babel/preset-env",
|
"@babel/preset-env",
|
||||||
{
|
{
|
||||||
useBuiltIns: latestBuild ? false : "entry",
|
useBuiltIns: latestBuild ? false : "entry",
|
||||||
corejs: latestBuild ? false : { version: "3.32", proposals: true },
|
corejs: latestBuild ? false : { version: "3.33", proposals: true },
|
||||||
bugfixes: true,
|
bugfixes: true,
|
||||||
shippedProposals: true,
|
shippedProposals: true,
|
||||||
},
|
},
|
||||||
@@ -149,7 +149,7 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
|||||||
sourceMaps: !isTestBuild,
|
sourceMaps: !isTestBuild,
|
||||||
});
|
});
|
||||||
|
|
||||||
const nameSuffix = (latestBuild) => (latestBuild ? "-latest" : "-es5");
|
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");
|
||||||
|
|
||||||
const outputPath = (outputRoot, latestBuild) =>
|
const outputPath = (outputRoot, latestBuild) =>
|
||||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||||
@@ -183,7 +183,7 @@ const publicPath = (latestBuild, root = "") =>
|
|||||||
module.exports.config = {
|
module.exports.config = {
|
||||||
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
||||||
return {
|
return {
|
||||||
name: "app" + nameSuffix(latestBuild),
|
name: "frontend" + nameSuffix(latestBuild),
|
||||||
entry: {
|
entry: {
|
||||||
service_worker: "./src/entrypoints/service_worker.ts",
|
service_worker: "./src/entrypoints/service_worker.ts",
|
||||||
app: "./src/entrypoints/app.ts",
|
app: "./src/entrypoints/app.ts",
|
||||||
|
@@ -45,8 +45,8 @@ gulp.task(
|
|||||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||||
"copy-static-app",
|
"copy-static-app",
|
||||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||||
|
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod"),
|
||||||
// Don't compress running tests
|
// Don't compress running tests
|
||||||
...(env.isTestBuild() ? [] : ["compress-app"]),
|
...(env.isTestBuild() ? [] : ["compress-app"])
|
||||||
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod")
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -4,6 +4,7 @@ import fs from "fs-extra";
|
|||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import paths from "../paths.cjs";
|
import paths from "../paths.cjs";
|
||||||
|
import env from "../env.cjs";
|
||||||
|
|
||||||
const npmPath = (...parts) =>
|
const npmPath = (...parts) =>
|
||||||
path.resolve(paths.polymer_dir, "node_modules", ...parts);
|
path.resolve(paths.polymer_dir, "node_modules", ...parts);
|
||||||
@@ -62,6 +63,9 @@ function copyPolyfills(staticDir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function copyLoaderJS(staticDir) {
|
function copyLoaderJS(staticDir) {
|
||||||
|
if (!env.useRollup()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const staticPath = genStaticPath(staticDir);
|
const staticPath = genStaticPath(staticDir);
|
||||||
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
|
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
|
||||||
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
|
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
|
||||||
|
@@ -1,51 +1,54 @@
|
|||||||
import { deleteSync } from "del";
|
import { deleteSync } from "del";
|
||||||
import { mkdir, readFile, writeFile } from "fs/promises";
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
import path from "path";
|
import { join, resolve } from "node:path";
|
||||||
import paths from "../paths.cjs";
|
import paths from "../paths.cjs";
|
||||||
|
|
||||||
const outDir = path.join(paths.build_dir, "locale-data");
|
const formatjsDir = join(paths.polymer_dir, "node_modules", "@formatjs");
|
||||||
|
const outDir = join(paths.build_dir, "locale-data");
|
||||||
|
|
||||||
const INTL_PACKAGES = {
|
const INTL_POLYFILLS = {
|
||||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
|
||||||
"intl-datetimeformat": "DateTimeFormat",
|
"intl-datetimeformat": "DateTimeFormat",
|
||||||
"intl-numberformat": "NumberFormat",
|
|
||||||
"intl-displaynames": "DisplayNames",
|
"intl-displaynames": "DisplayNames",
|
||||||
"intl-listformat": "ListFormat",
|
"intl-listformat": "ListFormat",
|
||||||
|
"intl-numberformat": "NumberFormat",
|
||||||
|
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||||
};
|
};
|
||||||
|
|
||||||
const convertToJSON = async (pkg, lang) => {
|
const convertToJSON = async (
|
||||||
|
pkg,
|
||||||
|
lang,
|
||||||
|
subDir = "locale-data",
|
||||||
|
addFunc = "__addLocaleData",
|
||||||
|
skipMissing = true
|
||||||
|
) => {
|
||||||
let localeData;
|
let localeData;
|
||||||
try {
|
try {
|
||||||
localeData = await readFile(
|
localeData = await readFile(
|
||||||
path.resolve(
|
join(formatjsDir, pkg, subDir, `${lang}.js`),
|
||||||
paths.polymer_dir,
|
|
||||||
`node_modules/@formatjs/${pkg}/locale-data/${lang}.js`
|
|
||||||
),
|
|
||||||
"utf-8"
|
"utf-8"
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore if language is missing (i.e. not supported by @formatjs)
|
// Ignore if language is missing (i.e. not supported by @formatjs)
|
||||||
if (e.code === "ENOENT") {
|
if (e.code === "ENOENT" && skipMissing) {
|
||||||
|
console.warn(`Skipped missing data for language ${lang} from ${pkg}`);
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Convert to JSON
|
// Convert to JSON
|
||||||
const className = INTL_PACKAGES[pkg];
|
const obj = INTL_POLYFILLS[pkg];
|
||||||
localeData = localeData
|
const dataRegex = new RegExp(
|
||||||
.replace(
|
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
|
||||||
new RegExp(
|
"s"
|
||||||
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
);
|
||||||
"im"
|
localeData = localeData.match(dataRegex)?.groups?.data;
|
||||||
),
|
if (!localeData) {
|
||||||
""
|
throw Error(`Failed to extract data for language ${lang} from ${pkg}`);
|
||||||
)
|
}
|
||||||
.replace(/\)\s*}/im, "");
|
|
||||||
// Parse to validate JSON, then stringify to minify
|
// Parse to validate JSON, then stringify to minify
|
||||||
localeData = JSON.stringify(JSON.parse(localeData));
|
localeData = JSON.stringify(JSON.parse(localeData));
|
||||||
await writeFile(path.join(outDir, `${pkg}/${lang}.json`), localeData);
|
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||||
};
|
};
|
||||||
|
|
||||||
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
||||||
@@ -53,17 +56,27 @@ gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
|||||||
gulp.task("create-locale-data", async () => {
|
gulp.task("create-locale-data", async () => {
|
||||||
const translationMeta = JSON.parse(
|
const translationMeta = JSON.parse(
|
||||||
await readFile(
|
await readFile(
|
||||||
path.resolve(paths.translations_src, "translationMetadata.json"),
|
resolve(paths.translations_src, "translationMetadata.json"),
|
||||||
"utf-8"
|
"utf-8"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const conversions = [];
|
const conversions = [];
|
||||||
for (const pkg of Object.keys(INTL_PACKAGES)) {
|
for (const pkg of Object.keys(INTL_POLYFILLS)) {
|
||||||
await mkdir(path.join(outDir, pkg), { recursive: true });
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await mkdir(join(outDir, pkg), { recursive: true });
|
||||||
for (const lang of Object.keys(translationMeta)) {
|
for (const lang of Object.keys(translationMeta)) {
|
||||||
conversions.push(convertToJSON(pkg, lang));
|
conversions.push(convertToJSON(pkg, lang));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
conversions.push(
|
||||||
|
convertToJSON(
|
||||||
|
"intl-datetimeformat",
|
||||||
|
"add-all-tz",
|
||||||
|
".",
|
||||||
|
"__addTZData",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
await Promise.all(conversions);
|
await Promise.all(conversions);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,12 +1,7 @@
|
|||||||
import { createHash } from "crypto";
|
import { createHash } from "crypto";
|
||||||
import { deleteSync } from "del";
|
import { deleteSync } from "del";
|
||||||
import {
|
import { mkdirSync, readdirSync, readFileSync, renameSync } from "fs";
|
||||||
mkdirSync,
|
import { writeFile } from "node:fs/promises";
|
||||||
readdirSync,
|
|
||||||
readFileSync,
|
|
||||||
renameSync,
|
|
||||||
writeFile,
|
|
||||||
} from "fs";
|
|
||||||
import gulp from "gulp";
|
import gulp from "gulp";
|
||||||
import flatmap from "gulp-flatmap";
|
import flatmap from "gulp-flatmap";
|
||||||
import transform from "gulp-json-transform";
|
import transform from "gulp-json-transform";
|
||||||
@@ -136,27 +131,23 @@ gulp.task("ensure-translations-build-dir", async () => {
|
|||||||
mkdirSync(workDir, { recursive: true });
|
mkdirSync(workDir, { recursive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("create-test-metadata", (cb) => {
|
gulp.task("create-test-metadata", () =>
|
||||||
writeFile(
|
env.isProdBuild()
|
||||||
|
? Promise.resolve()
|
||||||
|
: writeFile(
|
||||||
workDir + "/testMetadata.json",
|
workDir + "/testMetadata.json",
|
||||||
JSON.stringify({
|
JSON.stringify({ test: { nativeName: "Test" } })
|
||||||
test: {
|
)
|
||||||
nativeName: "Test",
|
);
|
||||||
},
|
|
||||||
}),
|
|
||||||
cb
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task(
|
gulp.task("create-test-translation", () =>
|
||||||
"create-test-translation",
|
env.isProdBuild()
|
||||||
gulp.series("create-test-metadata", () =>
|
? Promise.resolve()
|
||||||
gulp
|
: gulp
|
||||||
.src(path.join(paths.translations_src, "en.json"))
|
.src(path.join(paths.translations_src, "en.json"))
|
||||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||||
.pipe(rename("test.json"))
|
.pipe(rename("test.json"))
|
||||||
.pipe(gulp.dest(workDir))
|
.pipe(gulp.dest(workDir))
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -188,16 +179,11 @@ gulp.task("build-master-translation", () => {
|
|||||||
|
|
||||||
gulp.task("build-merged-translations", () =>
|
gulp.task("build-merged-translations", () =>
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src([
|
||||||
[
|
|
||||||
inFrontendDir + "/*.json",
|
inFrontendDir + "/*.json",
|
||||||
"!" + inFrontendDir + "/en.json",
|
"!" + inFrontendDir + "/en.json",
|
||||||
workDir + "/test.json",
|
...(env.isProdBuild() ? [] : [workDir + "/test.json"]),
|
||||||
],
|
])
|
||||||
{
|
|
||||||
allowEmpty: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||||
.pipe(
|
.pipe(
|
||||||
flatmap((stream, file) => {
|
flatmap((stream, file) => {
|
||||||
@@ -377,14 +363,11 @@ gulp.task("build-translation-flatten-supervisor", () =>
|
|||||||
|
|
||||||
gulp.task("build-translation-write-metadata", () =>
|
gulp.task("build-translation-write-metadata", () =>
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src([
|
||||||
[
|
|
||||||
path.join(paths.translations_src, "translationMetadata.json"),
|
path.join(paths.translations_src, "translationMetadata.json"),
|
||||||
workDir + "/testMetadata.json",
|
...(env.isProdBuild() ? [] : [workDir + "/testMetadata.json"]),
|
||||||
workDir + "/translationFingerprints.json",
|
workDir + "/translationFingerprints.json",
|
||||||
],
|
])
|
||||||
{ allowEmpty: true }
|
|
||||||
)
|
|
||||||
.pipe(merge({}))
|
.pipe(merge({}))
|
||||||
.pipe(
|
.pipe(
|
||||||
transform((data) => {
|
transform((data) => {
|
||||||
@@ -415,7 +398,7 @@ gulp.task("build-translation-write-metadata", () =>
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"create-translations",
|
"create-translations",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
...(env.isProdBuild() ? [] : ["create-test-translation"]),
|
gulp.parallel("create-test-metadata", "create-test-translation"),
|
||||||
"build-master-translation",
|
"build-master-translation",
|
||||||
"build-merged-translations",
|
"build-merged-translations",
|
||||||
gulp.parallel(...splitTasks),
|
gulp.parallel(...splitTasks),
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
const { existsSync } = require("fs");
|
const { existsSync } = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
|
const { StatsWriterPlugin } = require("webpack-stats-plugin");
|
||||||
|
const filterStats = require("@bundle-stats/plugin-webpack-filter").default;
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||||
const log = require("fancy-log");
|
const log = require("fancy-log");
|
||||||
@@ -152,6 +154,15 @@ const createWebpackConfig = ({
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
!isProdBuild && new LogStartCompilePlugin(),
|
!isProdBuild && new LogStartCompilePlugin(),
|
||||||
|
isProdBuild &&
|
||||||
|
new StatsWriterPlugin({
|
||||||
|
filename: path.relative(
|
||||||
|
outputPath,
|
||||||
|
path.join(paths.build_dir, "stats", `${name}.json`)
|
||||||
|
),
|
||||||
|
stats: { assets: true, chunks: true, modules: true },
|
||||||
|
transform: (stats) => JSON.stringify(filterStats(stats)),
|
||||||
|
}),
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".ts", ".js", ".json"],
|
extensions: [".ts", ".js", ".json"],
|
||||||
@@ -165,11 +176,14 @@ const createWebpackConfig = ({
|
|||||||
"lit/directives/guard$": "lit/directives/guard.js",
|
"lit/directives/guard$": "lit/directives/guard.js",
|
||||||
"lit/directives/cache$": "lit/directives/cache.js",
|
"lit/directives/cache$": "lit/directives/cache.js",
|
||||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||||
|
"lit/directives/live$": "lit/directives/live.js",
|
||||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||||
"@lit-labs/virtualizer/layouts/grid":
|
"@lit-labs/virtualizer/layouts/grid":
|
||||||
"@lit-labs/virtualizer/layouts/grid.js",
|
"@lit-labs/virtualizer/layouts/grid.js",
|
||||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
||||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
||||||
|
"@lit-labs/observers/resize-controller":
|
||||||
|
"@lit-labs/observers/resize-controller.js",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
@@ -182,6 +196,7 @@ const createWebpackConfig = ({
|
|||||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
|
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
|
||||||
assetModuleFilename:
|
assetModuleFilename:
|
||||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
|
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
|
||||||
|
crossOriginLoading: "use-credentials",
|
||||||
hashFunction: "xxhash64",
|
hashFunction: "xxhash64",
|
||||||
hashDigest: "base64url",
|
hashDigest: "base64url",
|
||||||
hashDigestLength: 11, // full length of 64 bit base64url
|
hashDigestLength: 11, // full length of 64 bit base64url
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import "../../../src/resources/safari-14-attachshadow-patch";
|
import "../../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "../../../src/resources/ha-style";
|
|
||||||
import "../../../src/resources/roboto";
|
|
||||||
import "./layout/hc-connect";
|
import "./layout/hc-connect";
|
||||||
|
|
||||||
|
import("../../../src/resources/ha-style");
|
||||||
|
@@ -32,6 +32,8 @@ import { HassElement } from "../../../../src/state/hass-element";
|
|||||||
import { castContext } from "../cast_context";
|
import { castContext } from "../cast_context";
|
||||||
import "./hc-launch-screen";
|
import "./hc-launch-screen";
|
||||||
|
|
||||||
|
const DEFAULT_STRATEGY = "original-states";
|
||||||
|
|
||||||
let resourcesLoaded = false;
|
let resourcesLoaded = false;
|
||||||
@customElement("hc-main")
|
@customElement("hc-main")
|
||||||
export class HcMain extends HassElement {
|
export class HcMain extends HassElement {
|
||||||
@@ -98,7 +100,9 @@ export class HcMain extends HassElement {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
import("../second-load");
|
import("./hc-lovelace");
|
||||||
|
import("../../../../src/resources/ha-style");
|
||||||
|
|
||||||
window.addEventListener("location-changed", () => {
|
window.addEventListener("location-changed", () => {
|
||||||
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
||||||
if (location.pathname.startsWith(panelPath)) {
|
if (location.pathname.startsWith(panelPath)) {
|
||||||
@@ -258,7 +262,6 @@ export class HcMain extends HassElement {
|
|||||||
{
|
{
|
||||||
strategy: {
|
strategy: {
|
||||||
type: "energy",
|
type: "energy",
|
||||||
options: { show_date_selection: true },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -306,7 +309,7 @@ export class HcMain extends HassElement {
|
|||||||
? await fetchResources(this.hass!.connection)
|
? await fetchResources(this.hass!.connection)
|
||||||
: (this._lovelaceConfig as LegacyLovelaceConfig).resources;
|
: (this._lovelaceConfig as LegacyLovelaceConfig).resources;
|
||||||
if (resources) {
|
if (resources) {
|
||||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
loadLovelaceResources(resources, this.hass!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,10 +323,9 @@ export class HcMain extends HassElement {
|
|||||||
this._handleNewLovelaceConfig(
|
this._handleNewLovelaceConfig(
|
||||||
await generateLovelaceDashboardStrategy(
|
await generateLovelaceDashboardStrategy(
|
||||||
{
|
{
|
||||||
hass: this.hass!,
|
type: DEFAULT_STRATEGY,
|
||||||
narrow: false,
|
|
||||||
},
|
},
|
||||||
"original-states"
|
this.hass!
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
import "../../../src/resources/ha-style";
|
|
||||||
import "../../../src/resources/roboto";
|
|
||||||
import "./layout/hc-lovelace";
|
|
@@ -8,25 +8,67 @@
|
|||||||
"src": "/static/icons/favicon-192x192.png",
|
"src": "/static/icons/favicon-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/icons/favicon-384x384.png",
|
"src": "/static/icons/favicon-384x384.png",
|
||||||
"sizes": "384x384",
|
"sizes": "384x384",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/icons/favicon-512x512.png",
|
"src": "/static/icons/favicon-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/static/icons/favicon-1024x1024.png",
|
"src": "/static/icons/favicon-1024x1024.png",
|
||||||
"sizes": "1024x1024",
|
"sizes": "1024x1024",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable any"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-48x48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-128x128.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/static/icons/maskable_icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lang": "en-US",
|
"lang": "en-US",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"zone.home": {
|
"zone.home": {
|
||||||
entity_id: "zone.home",
|
entity_id: "zone.home",
|
||||||
state: "zoning",
|
state: "zoning",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"zone.powertec": {
|
"zone.powertec": {
|
||||||
entity_id: "zone.powertec",
|
entity_id: "zone.powertec",
|
||||||
state: "zoning",
|
state: "zoning",
|
||||||
|
@@ -4,16 +4,11 @@ export const demoThemeJimpower = () => ({
|
|||||||
"primary-color": "#5294E2",
|
"primary-color": "#5294E2",
|
||||||
"label-badge-red": "var(--accent-color)",
|
"label-badge-red": "var(--accent-color)",
|
||||||
"paper-tabs-selection-bar-color": "green",
|
"paper-tabs-selection-bar-color": "green",
|
||||||
"paper-slider-knob-color": "var(--accent-color)",
|
|
||||||
"light-primary-color": "var(--accent-color)",
|
"light-primary-color": "var(--accent-color)",
|
||||||
"primary-background-color": "#383C45",
|
"primary-background-color": "#383C45",
|
||||||
"primary-text-color": "#FFFFFF",
|
"primary-text-color": "#FFFFFF",
|
||||||
"paper-item-selected_-_background-color": "#434954",
|
"paper-item-selected_-_background-color": "#434954",
|
||||||
"paper-slider-active-color": "var(--accent-color)",
|
|
||||||
"secondary-background-color": "#383C45",
|
"secondary-background-color": "#383C45",
|
||||||
"paper-slider-container-color":
|
|
||||||
"linear-gradient(var(--primary-background-color), var(--secondary-background-color)) no-repeat",
|
|
||||||
"paper-slider-disabled-active-color": "var(--disabled-text-color)",
|
|
||||||
"disabled-text-color": "#7F848E",
|
"disabled-text-color": "#7F848E",
|
||||||
"paper-item-icon_-_color": "green",
|
"paper-item-icon_-_color": "green",
|
||||||
"paper-grey-200": "#414A59",
|
"paper-grey-200": "#414A59",
|
||||||
@@ -32,14 +27,10 @@ export const demoThemeJimpower = () => ({
|
|||||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||||
"label-badge-border-color": "green",
|
"label-badge-border-color": "green",
|
||||||
"paper-listbox-color": "var(--primary-color)",
|
"paper-listbox-color": "var(--primary-color)",
|
||||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
|
||||||
"card-background-color": "#434954",
|
"card-background-color": "#434954",
|
||||||
"label-badge-text-color": "var(--primary-text-color)",
|
"label-badge-text-color": "var(--primary-text-color)",
|
||||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
|
||||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||||
"dark-primary-color": "var(--accent-color)",
|
"dark-primary-color": "var(--accent-color)",
|
||||||
"paper-slider-secondary-color": "var(--secondary-background-color)",
|
|
||||||
"paper-slider-pin-color": "var(--accent-color)",
|
|
||||||
"paper-item-icon-active-color": "#F9C536",
|
"paper-item-icon-active-color": "#F9C536",
|
||||||
"accent-color": "#E45E65",
|
"accent-color": "#E45E65",
|
||||||
"table-row-alternative-background-color": "#3E424B",
|
"table-row-alternative-background-color": "#3E424B",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"zone.anna": {
|
"zone.anna": {
|
||||||
entity_id: "zone.anna",
|
entity_id: "zone.anna",
|
||||||
state: "zoning",
|
state: "zoning",
|
||||||
|
@@ -5,17 +5,12 @@ export const demoThemeKernehed = () => ({
|
|||||||
"primary-color": "#2980b9",
|
"primary-color": "#2980b9",
|
||||||
"label-badge-red": "var(--accent-color)",
|
"label-badge-red": "var(--accent-color)",
|
||||||
"paper-tabs-selection-bar-color": "green",
|
"paper-tabs-selection-bar-color": "green",
|
||||||
"paper-slider-knob-color": "var(--accent-color)",
|
|
||||||
"primary-text-color": "#FFFFFF",
|
"primary-text-color": "#FFFFFF",
|
||||||
"light-primary-color": "var(--accent-color)",
|
"light-primary-color": "var(--accent-color)",
|
||||||
"primary-background-color": "#222222",
|
"primary-background-color": "#222222",
|
||||||
"sidebar-icon-color": "#777777",
|
"sidebar-icon-color": "#777777",
|
||||||
"paper-item-selected_-_background-color": "#292929",
|
"paper-item-selected_-_background-color": "#292929",
|
||||||
"paper-slider-active-color": "var(--accent-color)",
|
|
||||||
"secondary-background-color": "#222222",
|
"secondary-background-color": "#222222",
|
||||||
"paper-slider-container-color":
|
|
||||||
"linear-gradient(var(--primary-background-color), var(--secondary-background-color)) no-repeat",
|
|
||||||
"paper-slider-disabled-active-color": "var(--disabled-text-color)",
|
|
||||||
"disabled-text-color": "#777777",
|
"disabled-text-color": "#777777",
|
||||||
"paper-item-icon_-_color": "green",
|
"paper-item-icon_-_color": "green",
|
||||||
"paper-grey-200": "#222222",
|
"paper-grey-200": "#222222",
|
||||||
@@ -33,14 +28,10 @@ export const demoThemeKernehed = () => ({
|
|||||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||||
"label-badge-border-color": "green",
|
"label-badge-border-color": "green",
|
||||||
"paper-listbox-color": "#777777",
|
"paper-listbox-color": "#777777",
|
||||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
|
||||||
"card-background-color": "#292929",
|
"card-background-color": "#292929",
|
||||||
"label-badge-text-color": "var(--primary-text-color)",
|
"label-badge-text-color": "var(--primary-text-color)",
|
||||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
|
||||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||||
"dark-primary-color": "var(--accent-color)",
|
"dark-primary-color": "var(--accent-color)",
|
||||||
"paper-slider-secondary-color": "var(--secondary-background-color)",
|
|
||||||
"paper-slider-pin-color": "var(--accent-color)",
|
|
||||||
"paper-item-icon-active-color": "#b58e31",
|
"paper-item-icon-active-color": "#b58e31",
|
||||||
"accent-color": "#2980b9",
|
"accent-color": "#2980b9",
|
||||||
"table-row-alternative-background-color": "#292929",
|
"table-row-alternative-background-color": "#292929",
|
||||||
|
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
|||||||
|
|
||||||
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||||
convertEntities({
|
convertEntities({
|
||||||
|
"todo.shopping_list": {
|
||||||
|
entity_id: "todo.shopping_list",
|
||||||
|
state: "2",
|
||||||
|
attributes: {
|
||||||
|
supported_features: 15,
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
icon: "mdi:cart",
|
||||||
|
},
|
||||||
|
},
|
||||||
"sensor.pollen_grabo": {
|
"sensor.pollen_grabo": {
|
||||||
entity_id: "sensor.pollen_grabo",
|
entity_id: "sensor.pollen_grabo",
|
||||||
state: "",
|
state: "",
|
||||||
|
@@ -220,7 +220,8 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
state_filter: ["on"],
|
state_filter: ["on"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "shopping-list",
|
type: "todo-list",
|
||||||
|
entity: "todo.shopping_list",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
entities: [
|
entities: [
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
export const demoThemeTeachingbirds = () => ({
|
export const demoThemeTeachingbirds = () => ({
|
||||||
"paper-card-header-color": "var(--paper-item-icon-color)",
|
"paper-card-header-color": "var(--paper-item-icon-color)",
|
||||||
"paper-slider-pin-color": "var(--primary-color)",
|
|
||||||
"paper-listbox-background-color": "#202020",
|
"paper-listbox-background-color": "#202020",
|
||||||
"paper-grey-50": "var(--primary-text-color)",
|
"paper-grey-50": "var(--primary-text-color)",
|
||||||
"paper-item-icon-color": "#d3d3d3",
|
"paper-item-icon-color": "#d3d3d3",
|
||||||
@@ -8,8 +7,6 @@ export const demoThemeTeachingbirds = () => ({
|
|||||||
"primary-color": "#389638",
|
"primary-color": "#389638",
|
||||||
"light-primary-color": "#6f956f",
|
"light-primary-color": "#6f956f",
|
||||||
"label-badge-red": "var(--primary-color)",
|
"label-badge-red": "var(--primary-color)",
|
||||||
"paper-slider-secondary-color": "var(--light-primary-color)",
|
|
||||||
"paper-slider-knob-color": "var(--primary-color)",
|
|
||||||
"paper-listbox-color": "#FFFFFF",
|
"paper-listbox-color": "#FFFFFF",
|
||||||
"paper-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
"paper-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
||||||
"switch-unchecked-track-color": "var(--primary-text-color)",
|
"switch-unchecked-track-color": "var(--primary-text-color)",
|
||||||
@@ -17,9 +14,7 @@ export const demoThemeTeachingbirds = () => ({
|
|||||||
"label-badge-text-color": "var(--text-primary-color)",
|
"label-badge-text-color": "var(--text-primary-color)",
|
||||||
"primary-background-color": "#303030",
|
"primary-background-color": "#303030",
|
||||||
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
||||||
"paper-slider-active-color": "#d8bf50",
|
|
||||||
"secondary-background-color": "#2b2b2b",
|
"secondary-background-color": "#2b2b2b",
|
||||||
"paper-slider-knob-start-color": "var(--primary-color)",
|
|
||||||
"paper-item-icon-active-color": "#d8bf50",
|
"paper-item-icon-active-color": "#d8bf50",
|
||||||
"switch-checked-color": "var(--primary-color)",
|
"switch-checked-color": "var(--primary-color)",
|
||||||
"secondary-text-color": "#389638",
|
"secondary-text-color": "#389638",
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import "../../src/resources/ha-style";
|
|
||||||
import "../../src/resources/roboto";
|
|
||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
import "../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "./ha-demo";
|
import "./ha-demo";
|
||||||
|
|
||||||
|
import("../../src/resources/ha-style");
|
||||||
|
@@ -22,7 +22,7 @@ import { mockLovelace } from "./stubs/lovelace";
|
|||||||
import { mockMediaPlayer } from "./stubs/media_player";
|
import { mockMediaPlayer } from "./stubs/media_player";
|
||||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||||
import { mockRecorder } from "./stubs/recorder";
|
import { mockRecorder } from "./stubs/recorder";
|
||||||
import { mockShoppingList } from "./stubs/shopping_list";
|
import { mockTodo } from "./stubs/todo";
|
||||||
import { mockSystemLog } from "./stubs/system_log";
|
import { mockSystemLog } from "./stubs/system_log";
|
||||||
import { mockTemplate } from "./stubs/template";
|
import { mockTemplate } from "./stubs/template";
|
||||||
import { mockTranslations } from "./stubs/translations";
|
import { mockTranslations } from "./stubs/translations";
|
||||||
@@ -49,7 +49,7 @@ export class HaDemo extends HomeAssistantAppEl {
|
|||||||
mockTranslations(hass);
|
mockTranslations(hass);
|
||||||
mockHistory(hass);
|
mockHistory(hass);
|
||||||
mockRecorder(hass);
|
mockRecorder(hass);
|
||||||
mockShoppingList(hass);
|
mockTodo(hass);
|
||||||
mockSystemLog(hass);
|
mockSystemLog(hass);
|
||||||
mockTemplate(hass);
|
mockTemplate(hass);
|
||||||
mockEvents(hass);
|
mockEvents(hass);
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
import { ShoppingListItem } from "../../../src/data/shopping-list";
|
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
|
||||||
|
|
||||||
let items: ShoppingListItem[] = [
|
|
||||||
{
|
|
||||||
id: 12,
|
|
||||||
name: "Milk",
|
|
||||||
complete: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 13,
|
|
||||||
name: "Eggs",
|
|
||||||
complete: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 14,
|
|
||||||
name: "Oranges",
|
|
||||||
complete: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const mockShoppingList = (hass: MockHomeAssistant) => {
|
|
||||||
hass.mockWS("shopping_list/items", () => items);
|
|
||||||
hass.mockWS("shopping_list/items/add", (msg) => {
|
|
||||||
const item: ShoppingListItem = {
|
|
||||||
id: new Date().getTime(),
|
|
||||||
complete: false,
|
|
||||||
name: msg.name,
|
|
||||||
};
|
|
||||||
items.push(item);
|
|
||||||
hass.mockEvent("shopping_list_updated");
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
hass.mockWS("shopping_list/items/update", ({ type, item_id, ...updates }) => {
|
|
||||||
items = items.map((item) =>
|
|
||||||
item.id === item_id ? { ...item, ...updates } : item
|
|
||||||
);
|
|
||||||
hass.mockEvent("shopping_list_updated");
|
|
||||||
});
|
|
||||||
hass.mockWS("shopping_list/items/clear", () => {
|
|
||||||
items = items.filter((item) => !item.complete);
|
|
||||||
hass.mockEvent("shopping_list_updated");
|
|
||||||
});
|
|
||||||
};
|
|
24
demo/src/stubs/todo.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { TodoItem, TodoItemStatus } from "../../../src/data/todo";
|
||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockTodo = (hass: MockHomeAssistant) => {
|
||||||
|
hass.mockWS("todo/item/list", () => ({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
uid: "12",
|
||||||
|
summary: "Milk",
|
||||||
|
status: TodoItemStatus.NeedsAction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "13",
|
||||||
|
summary: "Eggs",
|
||||||
|
status: TodoItemStatus.NeedsAction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "14",
|
||||||
|
summary: "Oranges",
|
||||||
|
status: TodoItemStatus.Completed,
|
||||||
|
},
|
||||||
|
] as TodoItem[],
|
||||||
|
}));
|
||||||
|
};
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,5 +1,5 @@
|
|||||||
import "../../src/resources/ha-style";
|
|
||||||
import "../../src/resources/roboto";
|
|
||||||
import "./ha-gallery";
|
import "./ha-gallery";
|
||||||
|
|
||||||
|
import("../../src/resources/ha-style");
|
||||||
|
|
||||||
document.body.appendChild(document.createElement("ha-gallery"));
|
document.body.appendChild(document.createElement("ha-gallery"));
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { mdiHomeAssistant } from "@mdi/js";
|
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-chip";
|
import "../../../../src/components/ha-chip";
|
||||||
import "../../../../src/components/ha-chip-set";
|
import "../../../../src/components/ha-chip-set";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||||
|
|
||||||
const chips: {
|
const chips: {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
@@ -49,11 +49,11 @@ export class DemoHaCircularSlider extends LitElement {
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<p>Current</p>
|
<p>Current</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
min="10"
|
min="10"
|
||||||
max="30"
|
max="30"
|
||||||
.value=${this.current}
|
.value=${this.current}
|
||||||
@change=${this._currentChanged}
|
@change=${this._currentChanged}
|
||||||
pin
|
|
||||||
></ha-slider>
|
></ha-slider>
|
||||||
<p>${this.current} °C</p>
|
<p>${this.current} °C</p>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -57,6 +57,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: "backyard",
|
area_id: "backyard",
|
||||||
@@ -74,6 +75,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@@ -91,6 +93,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -57,8 +57,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
|||||||
></ha-hs-color-picker>
|
></ha-hs-color-picker>
|
||||||
<p>Hue : ${this.value[0]}</p>
|
<p>Hue : ${this.value[0]}</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
step="1"
|
step="1"
|
||||||
pin
|
|
||||||
min="0"
|
min="0"
|
||||||
max="360"
|
max="360"
|
||||||
.value=${this.value[0]}
|
.value=${this.value[0]}
|
||||||
@@ -67,8 +67,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
|||||||
</ha-slider>
|
</ha-slider>
|
||||||
<p>Saturation : ${this.value[1]}</p>
|
<p>Saturation : ${this.value[1]}</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
step="0.01"
|
step="0.01"
|
||||||
pin
|
|
||||||
min="0"
|
min="0"
|
||||||
max="1"
|
max="1"
|
||||||
.value=${this.value[1]}
|
.value=${this.value[1]}
|
||||||
@@ -77,8 +77,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
|||||||
</ha-slider>
|
</ha-slider>
|
||||||
<p>Color Brighness : ${this.brightness}</p>
|
<p>Color Brighness : ${this.brightness}</p>
|
||||||
<ha-slider
|
<ha-slider
|
||||||
|
labeled
|
||||||
step="1"
|
step="1"
|
||||||
pin
|
|
||||||
min="0"
|
min="0"
|
||||||
max="255"
|
max="255"
|
||||||
.value=${this.brightness}
|
.value=${this.brightness}
|
||||||
|
@@ -53,6 +53,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: "backyard",
|
area_id: "backyard",
|
||||||
@@ -70,6 +71,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@@ -87,6 +89,7 @@ const DEVICES = [
|
|||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: null,
|
hw_version: null,
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
serial_number: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Shopping List Card
|
|
||||||
---
|
|
3
gallery/src/pages/lovelace/todo-list-card.markdown
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Todo List Card
|
||||||
|
---
|
@@ -2,25 +2,39 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
|||||||
import { customElement, query } from "lit/decorators";
|
import { customElement, query } from "lit/decorators";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import "../../components/demo-cards";
|
import "../../components/demo-cards";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import { mockTodo } from "../../../../demo/src/stubs/todo";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("todo", "shopping_list", "2", {
|
||||||
|
friendly_name: "Shopping List",
|
||||||
|
supported_features: 15,
|
||||||
|
}),
|
||||||
|
getEntity("todo", "read_only", "2", {
|
||||||
|
friendly_name: "Read only",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
{
|
{
|
||||||
heading: "List example",
|
heading: "List example",
|
||||||
config: `
|
config: `
|
||||||
- type: shopping-list
|
- type: todo-list
|
||||||
|
entity: todo.shopping_list
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
heading: "List with title example",
|
heading: "List with title example",
|
||||||
config: `
|
config: `
|
||||||
- type: shopping-list
|
- type: todo-list
|
||||||
title: Shopping List
|
title: Shopping List
|
||||||
|
entity: todo.read_only
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-lovelace-shopping-list-card")
|
@customElement("demo-lovelace-todo-list-card")
|
||||||
class DemoShoppingListEntity extends LitElement {
|
class DemoTodoListEntity extends LitElement {
|
||||||
@query("#demos") private _demoRoot!: HTMLElement;
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@@ -32,18 +46,14 @@ class DemoShoppingListEntity extends LitElement {
|
|||||||
const hass = provideHass(this._demoRoot);
|
const hass = provideHass(this._demoRoot);
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
hass.updateTranslations("lovelace", "en");
|
hass.updateTranslations("lovelace", "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
|
||||||
hass.mockAPI("shopping_list", () => [
|
mockTodo(hass);
|
||||||
{ name: "list", id: 1, complete: false },
|
|
||||||
{ name: "all", id: 2, complete: false },
|
|
||||||
{ name: "the", id: 3, complete: false },
|
|
||||||
{ name: "things", id: 4, complete: true },
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"demo-lovelace-shopping-list-card": DemoShoppingListEntity;
|
"demo-lovelace-todo-list-card": DemoTodoListEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -213,6 +213,7 @@ const createDeviceRegistryEntries = (
|
|||||||
name: "Tag Reader",
|
name: "Tag Reader",
|
||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: "1.0.0",
|
hw_version: "1.0.0",
|
||||||
|
serial_number: "00_12_4B_00_22_98_88_7F",
|
||||||
id: "mock-device-id",
|
id: "mock-device-id",
|
||||||
identifiers: [],
|
identifiers: [],
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
@@ -7,7 +7,6 @@ import {
|
|||||||
mdiDocker,
|
mdiDocker,
|
||||||
mdiExclamationThick,
|
mdiExclamationThick,
|
||||||
mdiFlask,
|
mdiFlask,
|
||||||
mdiHomeAssistant,
|
|
||||||
mdiKey,
|
mdiKey,
|
||||||
mdiLinkLock,
|
mdiLinkLock,
|
||||||
mdiNetwork,
|
mdiNetwork,
|
||||||
@@ -22,7 +21,7 @@ import {
|
|||||||
mdiPound,
|
mdiPound,
|
||||||
mdiShield,
|
mdiShield,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -40,11 +39,11 @@ import "../../../../src/components/ha-svg-icon";
|
|||||||
import "../../../../src/components/ha-switch";
|
import "../../../../src/components/ha-switch";
|
||||||
import {
|
import {
|
||||||
AddonCapability,
|
AddonCapability,
|
||||||
fetchHassioAddonChangelog,
|
|
||||||
fetchHassioAddonInfo,
|
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
HassioAddonSetOptionParams,
|
HassioAddonSetOptionParams,
|
||||||
HassioAddonSetSecurityParams,
|
HassioAddonSetSecurityParams,
|
||||||
|
fetchHassioAddonChangelog,
|
||||||
|
fetchHassioAddonInfo,
|
||||||
installHassioAddon,
|
installHassioAddon,
|
||||||
rebuildLocalAddon,
|
rebuildLocalAddon,
|
||||||
restartHassioAddon,
|
restartHassioAddon,
|
||||||
@@ -56,9 +55,9 @@ import {
|
|||||||
validateHassioAddonOption,
|
validateHassioAddonOption,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import {
|
import {
|
||||||
|
HassioStats,
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
fetchHassioStats,
|
fetchHassioStats,
|
||||||
HassioStats,
|
|
||||||
} from "../../../../src/data/hassio/common";
|
} from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
StoreAddon,
|
StoreAddon,
|
||||||
@@ -69,6 +68,7 @@ import {
|
|||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
|
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../../src/types";
|
import { HomeAssistant, Route } from "../../../../src/types";
|
||||||
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../../src/util/bytes-to-string";
|
||||||
|
@@ -360,11 +360,9 @@ export class HassioBackups extends LitElement {
|
|||||||
if (this.supervisor!.info.state !== "running") {
|
if (this.supervisor!.info.state !== "running") {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor!.localize("backup.could_not_create"),
|
title: this.supervisor!.localize("backup.could_not_create"),
|
||||||
text: this.supervisor!.localize(
|
text: this.supervisor!.localize("backup.create_blocked_not_running", {
|
||||||
"backup.create_blocked_not_running",
|
state: this.supervisor!.info.state,
|
||||||
"state",
|
}),
|
||||||
this.supervisor!.info.state
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
|
import type { IFuseOptions } from "fuse.js";
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||||
|
|
||||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
||||||
const options: Fuse.IFuseOptions<StoreAddon> = {
|
const options: IFuseOptions<StoreAddon> = {
|
||||||
keys: ["name", "description", "slug"],
|
keys: ["name", "description", "slug"],
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
minMatchCharLength: 2,
|
minMatchCharLength: 2,
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { mdiFolder, mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
|
import { mdiFolder, mdiPuzzle } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
|
||||||
LitElement,
|
LitElement,
|
||||||
nothing,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
HassioPartialBackupCreateParams,
|
HassioPartialBackupCreateParams,
|
||||||
} from "../../../src/data/hassio/backup";
|
} from "../../../src/data/hassio/backup";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
|
import { mdiHomeAssistant } from "../../../src/resources/home-assistant-logo-svg";
|
||||||
import {
|
import {
|
||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
TranslationDict,
|
TranslationDict,
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { mdiHomeAssistant } from "@mdi/js";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
} from "../../../src/data/hassio/supervisor";
|
} from "../../../src/data/hassio/supervisor";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
|
import { mdiHomeAssistant } from "../../../src/resources/home-assistant-logo-svg";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
@@ -31,6 +31,7 @@ import { fileDownload } from "../../../../src/util/file_download";
|
|||||||
import "../../components/supervisor-backup-content";
|
import "../../components/supervisor-backup-content";
|
||||||
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||||
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
||||||
|
import { BackupOrRestoreKey } from "../../util/translations";
|
||||||
|
|
||||||
@customElement("dialog-hassio-backup")
|
@customElement("dialog-hassio-backup")
|
||||||
class HassioBackupDialog
|
class HassioBackupDialog
|
||||||
@@ -64,6 +65,13 @@ class HassioBackupDialog
|
|||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _localize(key: BackupOrRestoreKey) {
|
||||||
|
return (
|
||||||
|
this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
|
||||||
|
this._dialogParams!.localize!(`ui.panel.page-onboarding.restore.${key}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._dialogParams || !this._backup) {
|
if (!this._dialogParams || !this._backup) {
|
||||||
return nothing;
|
return nothing;
|
||||||
@@ -79,7 +87,7 @@ class HassioBackupDialog
|
|||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title">${this._backup.name}</span>
|
<span slot="title">${this._backup.name}</span>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass?.localize("ui.common.close") || "Close"}
|
.label=${this._localize("close")}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
slot="actionItems"
|
slot="actionItems"
|
||||||
dialogAction="cancel"
|
dialogAction="cancel"
|
||||||
@@ -87,8 +95,9 @@ class HassioBackupDialog
|
|||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
</div>
|
</div>
|
||||||
${this._restoringBackup
|
${this._restoringBackup
|
||||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||||
: html`<supervisor-backup-content
|
: html`
|
||||||
|
<supervisor-backup-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.supervisor=${this._dialogParams.supervisor}
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
.backup=${this._backup}
|
.backup=${this._backup}
|
||||||
@@ -96,20 +105,21 @@ class HassioBackupDialog
|
|||||||
.localize=${this._dialogParams.localize}
|
.localize=${this._dialogParams.localize}
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
>
|
>
|
||||||
</supervisor-backup-content>`}
|
</supervisor-backup-content>
|
||||||
|
`}
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: nothing}
|
||||||
|
|
||||||
<mwc-button
|
<mwc-button
|
||||||
.disabled=${this._restoringBackup}
|
.disabled=${this._restoringBackup}
|
||||||
slot="secondaryAction"
|
slot="secondaryAction"
|
||||||
@click=${this._restoreClicked}
|
@click=${this._restoreClicked}
|
||||||
>
|
>
|
||||||
Restore
|
${this._localize("restore")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
|
|
||||||
${!this._dialogParams.onboarding
|
${!this._dialogParams.onboarding && this._dialogParams.supervisor
|
||||||
? html`<ha-button-menu
|
? html`<ha-button-menu
|
||||||
fixed
|
fixed
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@@ -117,22 +127,24 @@ class HassioBackupDialog
|
|||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass!.localize("ui.common.menu") || "Menu"}
|
.label=${this._dialogParams.supervisor.localize(
|
||||||
|
"backup.more_actions"
|
||||||
|
)}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<mwc-list-item
|
<mwc-list-item
|
||||||
>${this._dialogParams.supervisor?.localize(
|
>${this._dialogParams.supervisor.localize(
|
||||||
"backup.download_backup"
|
"backup.download_backup"
|
||||||
)}</mwc-list-item
|
)}</mwc-list-item
|
||||||
>
|
>
|
||||||
<mwc-list-item class="error"
|
<mwc-list-item class="error"
|
||||||
>${this._dialogParams.supervisor?.localize(
|
>${this._dialogParams.supervisor.localize(
|
||||||
"backup.delete_backup_title"
|
"backup.delete_backup_title"
|
||||||
)}</mwc-list-item
|
)}</mwc-list-item
|
||||||
>
|
>
|
||||||
</ha-button-menu>`
|
</ha-button-menu>`
|
||||||
: ""}
|
: nothing}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -183,21 +195,22 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _partialRestoreClicked(backupDetails) {
|
private async _partialRestoreClicked(backupDetails) {
|
||||||
if (
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
this._dialogParams?.supervisor !== undefined &&
|
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||||
this._dialogParams?.supervisor.info.state !== "running"
|
|
||||||
) {
|
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore backup",
|
title: supervisor.localize("backup.could_not_restore"),
|
||||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
text: supervisor.localize("backup.restore_blocked_not_running", {
|
||||||
|
state: supervisor.info.state,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: "Are you sure you want to restore this partial backup?",
|
title: this._localize("confirm_restore_partial_backup_title"),
|
||||||
confirmText: "restore",
|
text: this._localize("confirm_restore_partial_backup_text"),
|
||||||
dismissText: "cancel",
|
confirmText: this._localize("restore"),
|
||||||
|
dismissText: this._localize("cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -230,22 +243,22 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _fullRestoreClicked(backupDetails) {
|
private async _fullRestoreClicked(backupDetails) {
|
||||||
if (
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
this._dialogParams?.supervisor !== undefined &&
|
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||||
this._dialogParams?.supervisor.info.state !== "running"
|
|
||||||
) {
|
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore backup",
|
title: supervisor.localize("backup.could_not_restore"),
|
||||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
text: supervisor.localize("backup.restore_blocked_not_running", {
|
||||||
|
state: supervisor.info.state,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title:
|
title: this._localize("confirm_restore_full_backup_title"),
|
||||||
"Are you sure you want to wipe your system and restore this backup?",
|
text: this._localize("confirm_restore_full_backup_text"),
|
||||||
confirmText: "restore",
|
confirmText: this._localize("restore"),
|
||||||
dismissText: "cancel",
|
dismissText: this._localize("cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -279,11 +292,15 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _deleteClicked() {
|
private async _deleteClicked() {
|
||||||
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
|
if (!supervisor) return;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: "Are you sure you want to delete this backup?",
|
title: supervisor!.localize("backup.confirm_delete_title"),
|
||||||
confirmText: "delete",
|
text: supervisor!.localize("backup.confirm_delete_text"),
|
||||||
dismissText: "cancel",
|
confirmText: supervisor!.localize("backup.delete"),
|
||||||
|
dismissText: supervisor!.localize("backup.cancel"),
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -301,6 +318,9 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _downloadClicked() {
|
private async _downloadClicked() {
|
||||||
|
const supervisor = this._dialogParams?.supervisor;
|
||||||
|
if (!supervisor) return;
|
||||||
|
|
||||||
let signedPath: { path: string };
|
let signedPath: { path: string };
|
||||||
try {
|
try {
|
||||||
signedPath = await getSignedPath(
|
signedPath = await getSignedPath(
|
||||||
@@ -320,10 +340,10 @@ class HassioBackupDialog
|
|||||||
|
|
||||||
if (window.location.href.includes("ui.nabu.casa")) {
|
if (window.location.href.includes("ui.nabu.casa")) {
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: "Potential slow download",
|
title: supervisor.localize("backup.remote_download_title"),
|
||||||
text: "Downloading backups over the Nabu Casa URL will take some time, it is recomended to use your local URL instead, do you want to continue?",
|
text: supervisor.localize("backup.remote_download_text"),
|
||||||
confirmText: "continue",
|
confirmText: supervisor.localize("backup.download"),
|
||||||
dismissText: "cancel",
|
dismissText: this._localize("cancel"),
|
||||||
});
|
});
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
return;
|
return;
|
||||||
|
@@ -89,8 +89,7 @@ class HassioCreateBackupDialog extends LitElement {
|
|||||||
),
|
),
|
||||||
text: this._dialogParams!.supervisor.localize(
|
text: this._dialogParams!.supervisor.localize(
|
||||||
"backup.create_blocked_not_running",
|
"backup.create_blocked_not_running",
|
||||||
"state",
|
{ state: this._dialogParams!.supervisor.info.state }
|
||||||
this._dialogParams!.supervisor.info.state
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
|
import { dump } from "js-yaml";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -9,7 +10,6 @@ import "../../../../src/components/ha-expansion-panel";
|
|||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/search-input";
|
import "../../../../src/components/search-input";
|
||||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||||
import { dump } from "../../../../src/resources/js-yaml-dump";
|
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
|
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
// Compat needs to be first import
|
// Compat needs to be first import
|
||||||
import "../../src/resources/compatibility";
|
import "../../src/resources/compatibility";
|
||||||
import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings";
|
|
||||||
import "../../src/resources/roboto";
|
|
||||||
import "../../src/resources/ha-style";
|
|
||||||
import "../../src/resources/safari-14-attachshadow-patch";
|
import "../../src/resources/safari-14-attachshadow-patch";
|
||||||
import "./hassio-main";
|
import "./hassio-main";
|
||||||
|
|
||||||
setCancelSyntheticClickEvents(false);
|
import("../../src/resources/ha-style");
|
||||||
|
import("@polymer/polymer/lib/utils/settings").then(
|
||||||
|
({ setCancelSyntheticClickEvents }) => setCancelSyntheticClickEvents(false)
|
||||||
|
);
|
||||||
|
|
||||||
const styleEl = document.createElement("style");
|
const styleEl = document.createElement("style");
|
||||||
styleEl.innerHTML = `
|
styleEl.textContent = `
|
||||||
body {
|
body {
|
||||||
font-family: Roboto, sans-serif;
|
font-family: Roboto, sans-serif;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
4
hassio/src/util/translations.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import type { TranslationDict } from "../../../src/types";
|
||||||
|
|
||||||
|
export type BackupOrRestoreKey = keyof TranslationDict["supervisor"]["backup"] &
|
||||||
|
keyof TranslationDict["ui"]["panel"]["page-onboarding"]["restore"];
|
172
package.json
@@ -25,43 +25,46 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.22.15",
|
"@babel/runtime": "7.23.2",
|
||||||
"@braintree/sanitize-url": "6.0.4",
|
"@braintree/sanitize-url": "6.0.4",
|
||||||
"@codemirror/autocomplete": "6.9.1",
|
"@codemirror/autocomplete": "6.10.2",
|
||||||
"@codemirror/commands": "6.2.5",
|
"@codemirror/commands": "6.3.0",
|
||||||
"@codemirror/language": "6.9.0",
|
"@codemirror/language": "6.9.2",
|
||||||
"@codemirror/legacy-modes": "6.3.3",
|
"@codemirror/legacy-modes": "6.3.3",
|
||||||
"@codemirror/search": "6.5.3",
|
"@codemirror/search": "6.5.4",
|
||||||
"@codemirror/state": "6.2.1",
|
"@codemirror/state": "6.3.1",
|
||||||
"@codemirror/view": "6.19.0",
|
"@codemirror/view": "6.21.4",
|
||||||
"@egjs/hammerjs": "2.0.17",
|
"@egjs/hammerjs": "2.0.17",
|
||||||
"@formatjs/intl-datetimeformat": "6.10.2",
|
"@formatjs/intl-datetimeformat": "6.11.1",
|
||||||
"@formatjs/intl-displaynames": "6.5.2",
|
"@formatjs/intl-displaynames": "6.6.1",
|
||||||
"@formatjs/intl-getcanonicallocales": "2.2.1",
|
"@formatjs/intl-getcanonicallocales": "2.3.0",
|
||||||
"@formatjs/intl-listformat": "7.4.2",
|
"@formatjs/intl-listformat": "7.5.0",
|
||||||
"@formatjs/intl-locale": "3.3.4",
|
"@formatjs/intl-locale": "3.4.0",
|
||||||
"@formatjs/intl-numberformat": "8.7.2",
|
"@formatjs/intl-numberformat": "8.8.0",
|
||||||
"@formatjs/intl-pluralrules": "5.2.6",
|
"@formatjs/intl-pluralrules": "5.2.7",
|
||||||
"@formatjs/intl-relativetimeformat": "11.2.6",
|
"@formatjs/intl-relativetimeformat": "11.2.7",
|
||||||
"@fullcalendar/core": "6.1.8",
|
"@fullcalendar/core": "6.1.9",
|
||||||
"@fullcalendar/daygrid": "6.1.8",
|
"@fullcalendar/daygrid": "6.1.9",
|
||||||
"@fullcalendar/interaction": "6.1.8",
|
"@fullcalendar/interaction": "6.1.9",
|
||||||
"@fullcalendar/list": "6.1.8",
|
"@fullcalendar/list": "6.1.9",
|
||||||
"@fullcalendar/luxon3": "6.1.8",
|
"@fullcalendar/luxon3": "6.1.9",
|
||||||
"@fullcalendar/timegrid": "6.1.8",
|
"@fullcalendar/timegrid": "6.1.9",
|
||||||
"@lezer/highlight": "1.1.6",
|
"@lezer/highlight": "1.1.6",
|
||||||
"@lit-labs/context": "0.4.1",
|
"@lit-labs/context": "0.4.1",
|
||||||
"@lit-labs/motion": "1.0.4",
|
"@lit-labs/motion": "1.0.4",
|
||||||
|
"@lit-labs/observers": "2.0.1",
|
||||||
"@lit-labs/virtualizer": "2.0.7",
|
"@lit-labs/virtualizer": "2.0.7",
|
||||||
"@lrnwebcomponents/simple-tooltip": "7.0.18",
|
"@lrnwebcomponents/simple-tooltip": "7.0.18",
|
||||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||||
|
"@material/mwc-base": "0.27.0",
|
||||||
"@material/mwc-button": "0.27.0",
|
"@material/mwc-button": "0.27.0",
|
||||||
"@material/mwc-checkbox": "0.27.0",
|
"@material/mwc-checkbox": "0.27.0",
|
||||||
"@material/mwc-circular-progress": "0.27.0",
|
"@material/mwc-circular-progress": "0.27.0",
|
||||||
"@material/mwc-dialog": "0.27.0",
|
"@material/mwc-dialog": "0.27.0",
|
||||||
"@material/mwc-drawer": "0.27.0",
|
"@material/mwc-drawer": "0.27.0",
|
||||||
"@material/mwc-fab": "0.27.0",
|
"@material/mwc-fab": "0.27.0",
|
||||||
|
"@material/mwc-floating-label": "0.27.0",
|
||||||
"@material/mwc-formfield": "0.27.0",
|
"@material/mwc-formfield": "0.27.0",
|
||||||
"@material/mwc-icon-button": "0.27.0",
|
"@material/mwc-icon-button": "0.27.0",
|
||||||
"@material/mwc-linear-progress": "0.27.0",
|
"@material/mwc-linear-progress": "0.27.0",
|
||||||
@@ -70,7 +73,6 @@
|
|||||||
"@material/mwc-radio": "0.27.0",
|
"@material/mwc-radio": "0.27.0",
|
||||||
"@material/mwc-ripple": "0.27.0",
|
"@material/mwc-ripple": "0.27.0",
|
||||||
"@material/mwc-select": "0.27.0",
|
"@material/mwc-select": "0.27.0",
|
||||||
"@material/mwc-slider": "0.27.0",
|
|
||||||
"@material/mwc-switch": "0.27.0",
|
"@material/mwc-switch": "0.27.0",
|
||||||
"@material/mwc-tab": "0.27.0",
|
"@material/mwc-tab": "0.27.0",
|
||||||
"@material/mwc-tab-bar": "0.27.0",
|
"@material/mwc-tab-bar": "0.27.0",
|
||||||
@@ -79,22 +81,21 @@
|
|||||||
"@material/mwc-top-app-bar": "0.27.0",
|
"@material/mwc-top-app-bar": "0.27.0",
|
||||||
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
||||||
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
||||||
"@material/web": "=1.0.0-pre.17",
|
"@material/web": "=1.0.1",
|
||||||
"@mdi/js": "7.2.96",
|
"@mdi/js": "7.3.67",
|
||||||
"@mdi/svg": "7.2.96",
|
"@mdi/svg": "7.3.67",
|
||||||
"@polymer/iron-flex-layout": "3.0.1",
|
"@polymer/iron-flex-layout": "3.0.1",
|
||||||
"@polymer/iron-input": "3.0.1",
|
"@polymer/iron-input": "3.0.1",
|
||||||
"@polymer/iron-resizable-behavior": "3.0.1",
|
"@polymer/iron-resizable-behavior": "3.0.1",
|
||||||
"@polymer/paper-input": "3.2.1",
|
"@polymer/paper-input": "3.2.1",
|
||||||
"@polymer/paper-item": "3.0.1",
|
"@polymer/paper-item": "3.0.1",
|
||||||
"@polymer/paper-listbox": "3.0.1",
|
"@polymer/paper-listbox": "3.0.1",
|
||||||
"@polymer/paper-slider": "3.0.1",
|
|
||||||
"@polymer/paper-tabs": "3.1.0",
|
"@polymer/paper-tabs": "3.1.0",
|
||||||
"@polymer/paper-toast": "3.0.1",
|
"@polymer/paper-toast": "3.0.1",
|
||||||
"@polymer/polymer": "3.5.1",
|
"@polymer/polymer": "3.5.1",
|
||||||
"@thomasloven/round-slider": "0.6.0",
|
"@thomasloven/round-slider": "0.6.0",
|
||||||
"@vaadin/combo-box": "24.1.7",
|
"@vaadin/combo-box": "24.2.1",
|
||||||
"@vaadin/vaadin-themable-mixin": "24.1.7",
|
"@vaadin/vaadin-themable-mixin": "24.2.1",
|
||||||
"@vibrant/color": "3.2.1-alpha.1",
|
"@vibrant/color": "3.2.1-alpha.1",
|
||||||
"@vibrant/core": "3.2.1-alpha.1",
|
"@vibrant/core": "3.2.1-alpha.1",
|
||||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||||
@@ -102,26 +103,26 @@
|
|||||||
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
||||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||||
"app-datepicker": "5.1.1",
|
"app-datepicker": "5.1.1",
|
||||||
"chart.js": "4.3.3",
|
"chart.js": "4.4.0",
|
||||||
"comlink": "4.4.1",
|
"comlink": "4.4.1",
|
||||||
"core-js": "3.32.2",
|
"core-js": "3.33.1",
|
||||||
"cropperjs": "1.6.0",
|
"cropperjs": "1.6.1",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"date-fns-tz": "2.0.0",
|
"date-fns-tz": "2.0.0",
|
||||||
"deep-clone-simple": "1.1.1",
|
"deep-clone-simple": "1.1.1",
|
||||||
"deep-freeze": "0.0.1",
|
"deep-freeze": "0.0.1",
|
||||||
"fuse.js": "6.6.2",
|
"fuse.js": "7.0.0",
|
||||||
"google-timezones-json": "1.2.0",
|
"google-timezones-json": "1.2.0",
|
||||||
"hls.js": "1.4.12",
|
"hls.js": "1.4.12",
|
||||||
"home-assistant-js-websocket": "8.2.0",
|
"home-assistant-js-websocket": "9.1.0",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.1",
|
||||||
"intl-messageformat": "10.5.2",
|
"intl-messageformat": "10.5.4",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"leaflet-draw": "1.0.4",
|
"leaflet-draw": "1.0.4",
|
||||||
"lit": "2.8.0",
|
"lit": "2.8.0",
|
||||||
"luxon": "3.4.3",
|
"luxon": "3.4.3",
|
||||||
"marked": "7.0.5",
|
"marked": "9.1.2",
|
||||||
"memoize-one": "6.0.0",
|
"memoize-one": "6.0.0",
|
||||||
"node-vibrant": "3.2.1-alpha.1",
|
"node-vibrant": "3.2.1-alpha.1",
|
||||||
"proxy-polyfill": "0.3.2",
|
"proxy-polyfill": "0.3.2",
|
||||||
@@ -137,11 +138,11 @@
|
|||||||
"tinykeys": "2.1.0",
|
"tinykeys": "2.1.0",
|
||||||
"tsparticles-engine": "2.12.0",
|
"tsparticles-engine": "2.12.0",
|
||||||
"tsparticles-preset-links": "2.12.0",
|
"tsparticles-preset-links": "2.12.0",
|
||||||
"ua-parser-js": "1.0.36",
|
"ua-parser-js": "1.0.37",
|
||||||
"unfetch": "5.0.0",
|
"unfetch": "5.0.0",
|
||||||
"vis-data": "7.1.7",
|
"vis-data": "7.1.7",
|
||||||
"vis-network": "9.1.6",
|
"vis-network": "9.1.8",
|
||||||
"vue": "2.7.14",
|
"vue": "2.7.15",
|
||||||
"vue2-daterange-picker": "0.6.8",
|
"vue2-daterange-picker": "0.6.8",
|
||||||
"weekstart": "2.0.0",
|
"weekstart": "2.0.0",
|
||||||
"workbox-cacheable-response": "7.0.0",
|
"workbox-cacheable-response": "7.0.0",
|
||||||
@@ -153,62 +154,63 @@
|
|||||||
"xss": "1.0.14"
|
"xss": "1.0.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.22.20",
|
"@babel/core": "7.23.2",
|
||||||
"@babel/plugin-proposal-decorators": "7.22.15",
|
"@babel/plugin-proposal-decorators": "7.23.2",
|
||||||
"@babel/plugin-transform-runtime": "7.22.15",
|
"@babel/plugin-transform-runtime": "7.23.2",
|
||||||
"@babel/preset-env": "7.22.20",
|
"@babel/preset-env": "7.23.2",
|
||||||
"@babel/preset-typescript": "7.22.15",
|
"@babel/preset-typescript": "7.23.2",
|
||||||
|
"@bundle-stats/plugin-webpack-filter": "4.7.8",
|
||||||
"@koa/cors": "4.0.0",
|
"@koa/cors": "4.0.0",
|
||||||
"@lokalise/node-api": "11.0.1",
|
"@lokalise/node-api": "12.0.0",
|
||||||
"@octokit/auth-oauth-device": "6.0.0",
|
"@octokit/auth-oauth-device": "6.0.1",
|
||||||
"@octokit/plugin-retry": "6.0.0",
|
"@octokit/plugin-retry": "6.0.1",
|
||||||
"@octokit/rest": "20.0.1",
|
"@octokit/rest": "20.0.2",
|
||||||
"@open-wc/dev-server-hmr": "0.1.4",
|
"@open-wc/dev-server-hmr": "0.1.4",
|
||||||
"@rollup/plugin-babel": "6.0.3",
|
"@rollup/plugin-babel": "6.0.4",
|
||||||
"@rollup/plugin-commonjs": "25.0.4",
|
"@rollup/plugin-commonjs": "25.0.7",
|
||||||
"@rollup/plugin-json": "6.0.0",
|
"@rollup/plugin-json": "6.0.1",
|
||||||
"@rollup/plugin-node-resolve": "15.2.1",
|
"@rollup/plugin-node-resolve": "15.2.3",
|
||||||
"@rollup/plugin-replace": "5.0.2",
|
"@rollup/plugin-replace": "5.0.4",
|
||||||
"@types/babel__plugin-transform-runtime": "7.9.3",
|
"@types/babel__plugin-transform-runtime": "7.9.4",
|
||||||
"@types/chromecast-caf-receiver": "6.0.10",
|
"@types/chromecast-caf-receiver": "6.0.11",
|
||||||
"@types/chromecast-caf-sender": "1.0.6",
|
"@types/chromecast-caf-sender": "1.0.7",
|
||||||
"@types/esprima": "4.0.3",
|
"@types/esprima": "4.0.5",
|
||||||
"@types/glob": "8.1.0",
|
"@types/glob": "8.1.0",
|
||||||
"@types/html-minifier-terser": "7.0.0",
|
"@types/html-minifier-terser": "7.0.1",
|
||||||
"@types/js-yaml": "4.0.6",
|
"@types/js-yaml": "4.0.8",
|
||||||
"@types/leaflet": "1.9.4",
|
"@types/leaflet": "1.9.7",
|
||||||
"@types/leaflet-draw": "1.0.8",
|
"@types/leaflet-draw": "1.0.9",
|
||||||
"@types/luxon": "3.3.2",
|
"@types/luxon": "3.3.3",
|
||||||
"@types/mocha": "10.0.1",
|
"@types/mocha": "10.0.3",
|
||||||
"@types/qrcode": "1.5.2",
|
"@types/qrcode": "1.5.4",
|
||||||
"@types/serve-handler": "6.1.1",
|
"@types/serve-handler": "6.1.3",
|
||||||
"@types/sortablejs": "1.15.2",
|
"@types/sortablejs": "1.15.4",
|
||||||
"@types/tar": "6.1.6",
|
"@types/tar": "6.1.7",
|
||||||
"@types/ua-parser-js": "0.7.37",
|
"@types/ua-parser-js": "0.7.38",
|
||||||
"@types/webspeechapi": "0.0.29",
|
"@types/webspeechapi": "0.0.29",
|
||||||
"@typescript-eslint/eslint-plugin": "6.7.0",
|
"@typescript-eslint/eslint-plugin": "6.9.0",
|
||||||
"@typescript-eslint/parser": "6.7.0",
|
"@typescript-eslint/parser": "6.9.0",
|
||||||
"@web/dev-server": "0.1.38",
|
"@web/dev-server": "0.1.38",
|
||||||
"@web/dev-server-rollup": "0.4.1",
|
"@web/dev-server-rollup": "0.4.1",
|
||||||
"babel-loader": "9.1.3",
|
"babel-loader": "9.1.3",
|
||||||
"babel-plugin-template-html-minifier": "4.1.0",
|
"babel-plugin-template-html-minifier": "4.1.0",
|
||||||
"chai": "4.3.8",
|
"chai": "4.3.10",
|
||||||
"del": "7.1.0",
|
"del": "7.1.0",
|
||||||
"eslint": "8.49.0",
|
"eslint": "8.52.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "17.1.0",
|
"eslint-config-airbnb-typescript": "17.1.0",
|
||||||
"eslint-config-prettier": "9.0.0",
|
"eslint-config-prettier": "9.0.0",
|
||||||
"eslint-import-resolver-webpack": "0.13.7",
|
"eslint-import-resolver-webpack": "0.13.8",
|
||||||
"eslint-plugin-disable": "2.0.3",
|
"eslint-plugin-disable": "2.0.3",
|
||||||
"eslint-plugin-import": "2.28.1",
|
"eslint-plugin-import": "2.29.0",
|
||||||
"eslint-plugin-lit": "1.9.1",
|
"eslint-plugin-lit": "1.10.1",
|
||||||
"eslint-plugin-lit-a11y": "3.0.0",
|
"eslint-plugin-lit-a11y": "4.1.0",
|
||||||
"eslint-plugin-unused-imports": "3.0.0",
|
"eslint-plugin-unused-imports": "3.0.0",
|
||||||
"eslint-plugin-wc": "1.5.0",
|
"eslint-plugin-wc": "2.0.4",
|
||||||
"esprima": "4.0.1",
|
"esprima": "4.0.1",
|
||||||
"fancy-log": "2.0.0",
|
"fancy-log": "2.0.0",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
"glob": "10.3.4",
|
"glob": "10.3.10",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-flatmap": "1.0.2",
|
"gulp-flatmap": "1.0.2",
|
||||||
"gulp-json-transform": "0.4.8",
|
"gulp-json-transform": "0.4.8",
|
||||||
@@ -219,10 +221,10 @@
|
|||||||
"husky": "8.0.3",
|
"husky": "8.0.3",
|
||||||
"instant-mocha": "1.5.2",
|
"instant-mocha": "1.5.2",
|
||||||
"jszip": "3.10.1",
|
"jszip": "3.10.1",
|
||||||
"lint-staged": "14.0.1",
|
"lint-staged": "15.0.2",
|
||||||
"lit-analyzer": "2.0.0-pre.3",
|
"lit-analyzer": "2.0.1",
|
||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
"magic-string": "0.30.3",
|
"magic-string": "0.30.5",
|
||||||
"map-stream": "0.0.7",
|
"map-stream": "0.0.7",
|
||||||
"mocha": "10.2.0",
|
"mocha": "10.2.0",
|
||||||
"object-hash": "3.0.0",
|
"object-hash": "3.0.0",
|
||||||
@@ -234,19 +236,20 @@
|
|||||||
"rollup-plugin-terser": "7.0.2",
|
"rollup-plugin-terser": "7.0.2",
|
||||||
"rollup-plugin-visualizer": "5.9.2",
|
"rollup-plugin-visualizer": "5.9.2",
|
||||||
"serve-handler": "6.1.5",
|
"serve-handler": "6.1.5",
|
||||||
"sinon": "16.0.0",
|
"sinon": "17.0.0",
|
||||||
"source-map-url": "0.4.1",
|
"source-map-url": "0.4.1",
|
||||||
"systemjs": "6.14.2",
|
"systemjs": "6.14.2",
|
||||||
"tar": "6.2.0",
|
"tar": "6.2.0",
|
||||||
"terser-webpack-plugin": "5.3.9",
|
"terser-webpack-plugin": "5.3.9",
|
||||||
"ts-lit-plugin": "2.0.0-pre.1",
|
"ts-lit-plugin": "2.0.1",
|
||||||
"typescript": "5.2.2",
|
"typescript": "5.2.2",
|
||||||
"vinyl-buffer": "1.0.1",
|
"vinyl-buffer": "1.0.1",
|
||||||
"vinyl-source-stream": "2.0.0",
|
"vinyl-source-stream": "2.0.0",
|
||||||
"webpack": "5.88.2",
|
"webpack": "5.89.0",
|
||||||
"webpack-cli": "5.1.4",
|
"webpack-cli": "5.1.4",
|
||||||
"webpack-dev-server": "4.15.1",
|
"webpack-dev-server": "4.15.1",
|
||||||
"webpack-manifest-plugin": "5.0.0",
|
"webpack-manifest-plugin": "5.0.0",
|
||||||
|
"webpack-stats-plugin": "1.1.3",
|
||||||
"webpackbar": "5.0.2",
|
"webpackbar": "5.0.2",
|
||||||
"workbox-build": "7.0.0"
|
"workbox-build": "7.0.0"
|
||||||
},
|
},
|
||||||
@@ -254,8 +257,9 @@
|
|||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@polymer/polymer": "patch:@polymer/polymer@3.5.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
"@polymer/polymer": "patch:@polymer/polymer@3.5.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
||||||
"@material/mwc-button@^0.25.3": "^0.27.0",
|
"@material/mwc-button@^0.25.3": "^0.27.0",
|
||||||
|
"lit@^2.7.4 || ^3.0.0": "^2.7.4",
|
||||||
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
|
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
|
||||||
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@3.6.3"
|
"packageManager": "yarn@3.6.4"
|
||||||
}
|
}
|
||||||
|
12
public/static/icons/browserconfig.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square70x70logo src="/static/icons/tile-win-70x70.png"/>
|
||||||
|
<square150x150logo src="/static/icons/tile-win-150x150.png"/>
|
||||||
|
<wide310x150logo src="/static/icons/tile-win-310x150.png"/>
|
||||||
|
<square310x310logo src="/static/icons/tile-win-310x310.png"/>
|
||||||
|
<TileColor>#18bcf2</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 21 KiB |
BIN
public/static/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 464 B |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
public/static/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
@@ -1 +1,23 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path fill-rule="nonzero" fill="#000" d="M9,16 L9,17 L7,17 L7,16 L1,16 L1,9 L-1,9 L8.00163907,0 L13,4.785368 L13,3 L15,3 L15,7.035368 L17,9 L15,9 L15,16 L9,16 Z M9,16 L9,13.5 C9.49775077,13.0022492 10.1813086,12.3186914 11.0506735,11.4493265 C11.1951058,11.4824829 11.3455072,11.5 11.5,11.5 C12.6045695,11.5 13.5,10.6045695 13.5,9.5 C13.5,8.3954305 12.6045695,7.5 11.5,7.5 C10.3954305,7.5 9.5,8.3954305 9.5,9.5 C9.5,9.65449279 9.5175171,9.80489423 9.55067348,9.94932652 L9,10.5 L9,7.73243561 C9.59780137,7.38662619 10,6.74028236 10,6 C10,4.8954305 9.1045695,4 8,4 C6.8954305,4 6,4.8954305 6,6 C6,6.74028236 6.40219863,7.38662619 7,7.73243561 L7,10.5 L6.44932652,9.94932652 C6.4824829,9.80489423 6.5,9.65449279 6.5,9.5 C6.5,8.3954305 5.6045695,7.5 4.5,7.5 C3.3954305,7.5 2.5,8.3954305 2.5,9.5 C2.5,10.6045695 3.3954305,11.5 4.5,11.5 C4.65352068,11.5 4.80300134,11.4827027 4.9465994,11.4499505 C5.81726201,12.3268973 6.50172888,13.0147433 7,13.5134884 L7,16 L9,16 Z M11.5,10 C11.2238576,10 11,9.77614237 11,9.5 C11,9.22385763 11.2238576,9 11.5,9 C11.7761424,9 12,9.22385763 12,9.5 C12,9.77614237 11.7761424,10 11.5,10 Z M4.5,10 C4.22385763,10 4,9.77614237 4,9.5 C4,9.22385763 4.22385763,9 4.5,9 C4.77614237,9 5,9.22385763 5,9.5 C5,9.77614237 4.77614237,10 4.5,10 Z M8,6.5 C7.72385763,6.5 7.5,6.27614237 7.5,6 C7.5,5.72385763 7.72385763,5.5 8,5.5 C8.27614237,5.5 8.5,5.72385763 8.5,6 C8.5,6.27614237 8.27614237,6.5 8,6.5 Z" id="house-small-tree"/></svg>
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="480.000000pt" height="480.000000pt" viewBox="0 0 480.000000 480.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<g transform="translate(0.000000,480.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M2313 4666 c-23 -7 -56 -23 -75 -34 -47 -30 -2059 -2048 -2095 -2102
|
||||||
|
-45 -67 -77 -135 -109 -230 l-29 -85 0 -995 0 -995 27 -51 c31 -59 93 -118
|
||||||
|
152 -145 39 -18 83 -19 1001 -19 l960 0 -406 405 c-395 395 -406 406 -433 395
|
||||||
|
-15 -5 -63 -10 -107 -10 -429 0 -566 577 -181 767 67 34 86 38 164 42 105 4
|
||||||
|
165 -13 246 -67 113 -74 175 -190 176 -327 1 -44 -3 -96 -7 -115 l-8 -35 316
|
||||||
|
-315 315 -315 0 1160 -1 1160 -51 35 c-260 177 -226 567 62 704 82 39 209 48
|
||||||
|
293 21 239 -78 354 -352 242 -575 -32 -63 -89 -125 -141 -156 l-44 -26 0 -811
|
||||||
|
0 -812 315 315 c218 217 313 320 309 330 -14 35 -16 134 -4 190 26 122 111
|
||||||
|
227 230 284 82 39 209 48 293 21 115 -38 214 -130 258 -242 19 -46 23 -78 24
|
||||||
|
-153 0 -86 -3 -101 -32 -163 -40 -84 -118 -163 -198 -202 -49 -23 -77 -29
|
||||||
|
-150 -33 -50 -2 -108 1 -130 7 l-40 11 -437 -438 -438 -437 0 -307 0 -308 998
|
||||||
|
0 c981 0 998 1 1042 21 58 26 115 81 148 144 l27 50 0 995 0 995 -33 95 c-72
|
||||||
|
209 -6 135 -1147 1278 -840 843 -1040 1037 -1082 1059 -64 31 -159 39 -220 19z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
public/static/icons/maskable_icon-128x128.png
Normal file
After Width: | Height: | Size: 992 B |
BIN
public/static/icons/maskable_icon-192x192.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
public/static/icons/maskable_icon-384x384.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
public/static/icons/maskable_icon-48x48.png
Normal file
After Width: | Height: | Size: 477 B |
BIN
public/static/icons/maskable_icon-512x512.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
public/static/icons/maskable_icon-72x72.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
public/static/icons/maskable_icon-96x96.png
Normal file
After Width: | Height: | Size: 686 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 824 B After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 1.3 KiB |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20230911.0"
|
version = "20231030.0"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
40
script/core
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# Helper to start Home Assistant Core inside the devcontainer
|
# Helper to start Home Assistant Core inside the devcontainer
|
||||||
|
|
||||||
# Stop on errors
|
# Stop on errors
|
||||||
@@ -11,11 +11,35 @@ if [ -z "${DEVCONTAINER}" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $(which hass) ]; then
|
# Default to installing (or upgrading to) dev branch
|
||||||
echo "Installing Home Asstant core from dev."
|
coreURL="https://github.com/home-assistant/core.git"
|
||||||
python3 -m pip install --upgrade \
|
ref="dev"
|
||||||
colorlog \
|
|
||||||
git+https://github.com/home-assistant/home-assistant.git@dev
|
while getopts "hr:s" opt; do
|
||||||
|
case $opt in
|
||||||
|
h) # Help
|
||||||
|
echo "Usage: $0 [-h|-r <ref>|-s]"
|
||||||
|
echo -n "Install and run core at the given ref, i.e. branch, tag, or commit. The dev branch is used if no option is specified."
|
||||||
|
echo "The -s flag skips the install/upgrade, using whatever version is currently installed."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
r) # Git ref
|
||||||
|
ref="${OPTARG}"
|
||||||
|
;;
|
||||||
|
s) # Skip (use current install)
|
||||||
|
ref=""
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Try $0 -h for help" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$ref" ]; then
|
||||||
|
echo "Installing Home Assistant core at ${ref}..."
|
||||||
|
python3 -m pip install --user --upgrade --src "$HOME/src" \
|
||||||
|
--editable "git+${coreURL}@${ref}#egg=homeassistant"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "${WD}/config" ]; then
|
if [ ! -d "${WD}/config" ]; then
|
||||||
@@ -30,7 +54,7 @@ logger:
|
|||||||
homeassistant.components.frontend: debug
|
homeassistant.components.frontend: debug
|
||||||
" >> "${WD}/config/configuration.yaml"
|
" >> "${WD}/config/configuration.yaml"
|
||||||
|
|
||||||
if [ ! -z "${HASSIO}" ]; then
|
if [ -n "${HASSIO}" ]; then
|
||||||
echo "
|
echo "
|
||||||
# frontend:
|
# frontend:
|
||||||
# development_repo: ${WD}
|
# development_repo: ${WD}
|
||||||
@@ -46,7 +70,7 @@ frontend:
|
|||||||
# development_repo: ${WD}" >> "${WD}/config/configuration.yaml"
|
# development_repo: ${WD}" >> "${WD}/config/configuration.yaml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -z "${CODESPACES}" ]; then
|
if [ -n "${CODESPACES}" ]; then
|
||||||
echo "
|
echo "
|
||||||
http:
|
http:
|
||||||
use_x_forwarded_for: true
|
use_x_forwarded_for: true
|
||||||
|
@@ -33,7 +33,7 @@ fi
|
|||||||
|
|
||||||
docker run \
|
docker run \
|
||||||
-v ${LOCAL_FILE}:/opt/src/${LOCAL_FILE} \
|
-v ${LOCAL_FILE}:/opt/src/${LOCAL_FILE} \
|
||||||
lokalise/lokalise-cli-2@sha256:f1860b26be22fa73b8c93bc5f8690f2afc867610a42de6fc27adc790e5d4425d lokalise2 \
|
lokalise/lokalise-cli-2:v2.6.10 lokalise2 \
|
||||||
--token ${LOKALISE_TOKEN} \
|
--token ${LOKALISE_TOKEN} \
|
||||||
--project-id ${PROJECT_ID} \
|
--project-id ${PROJECT_ID} \
|
||||||
file upload \
|
file upload \
|
||||||
|
@@ -1,19 +1,12 @@
|
|||||||
|
/* eslint-disable lit/prefer-static-styles */
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { genClientId } from "home-assistant-js-websocket";
|
import { genClientId } from "home-assistant-js-websocket";
|
||||||
import {
|
import { html, LitElement, nothing, PropertyValues } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
nothing,
|
|
||||||
PropertyValues,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import "../components/ha-alert";
|
import "../components/ha-alert";
|
||||||
import "../components/ha-checkbox";
|
import "../components/ha-checkbox";
|
||||||
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
||||||
import "../components/ha-form/ha-form";
|
|
||||||
import "../components/ha-formfield";
|
import "../components/ha-formfield";
|
||||||
import "../components/ha-markdown";
|
import "../components/ha-markdown";
|
||||||
import { AuthProvider, autocompleteLoginFields } from "../data/auth";
|
import { AuthProvider, autocompleteLoginFields } from "../data/auth";
|
||||||
@@ -21,7 +14,7 @@ import {
|
|||||||
DataEntryFlowStep,
|
DataEntryFlowStep,
|
||||||
DataEntryFlowStepForm,
|
DataEntryFlowStepForm,
|
||||||
} from "../data/data_entry_flow";
|
} from "../data/data_entry_flow";
|
||||||
import "./ha-password-manager-polyfill";
|
import "./ha-auth-form";
|
||||||
|
|
||||||
type State = "loading" | "error" | "step";
|
type State = "loading" | "error" | "step";
|
||||||
|
|
||||||
@@ -49,6 +42,10 @@ export class HaAuthFlow extends LitElement {
|
|||||||
|
|
||||||
@state() private _storeToken = false;
|
@state() private _storeToken = false;
|
||||||
|
|
||||||
|
createRenderRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
willUpdate(changedProps: PropertyValues) {
|
willUpdate(changedProps: PropertyValues) {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
@@ -79,13 +76,17 @@ export class HaAuthFlow extends LitElement {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-auth-flow .action {
|
||||||
|
margin: 24px 0 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
ha-auth-flow .store-token {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: -16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<form>${this._renderForm()}</form>
|
<form>${this._renderForm()}</form>
|
||||||
<ha-password-manager-polyfill
|
|
||||||
.step=${this._step}
|
|
||||||
.stepData=${this._stepData}
|
|
||||||
@form-submitted=${this._handleSubmit}
|
|
||||||
@value-changed=${this._stepDataChanged}
|
|
||||||
></ha-password-manager-polyfill>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,12 +129,6 @@ export class HaAuthFlow extends LitElement {
|
|||||||
(form as any).focus();
|
(form as any).focus();
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.renderRoot.querySelector(
|
|
||||||
"ha-password-manager-polyfill"
|
|
||||||
)!.boundingRect = this.getBoundingClientRect();
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderForm() {
|
private _renderForm() {
|
||||||
@@ -205,7 +200,7 @@ export class HaAuthFlow extends LitElement {
|
|||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-form
|
<ha-auth-form
|
||||||
.data=${this._stepData}
|
.data=${this._stepData}
|
||||||
.schema=${autocompleteLoginFields(step.data_schema)}
|
.schema=${autocompleteLoginFields(step.data_schema)}
|
||||||
.error=${step.errors}
|
.error=${step.errors}
|
||||||
@@ -213,7 +208,7 @@ export class HaAuthFlow extends LitElement {
|
|||||||
.computeLabel=${this._computeLabelCallback(step)}
|
.computeLabel=${this._computeLabelCallback(step)}
|
||||||
.computeError=${this._computeErrorCallback(step)}
|
.computeError=${this._computeErrorCallback(step)}
|
||||||
@value-changed=${this._stepDataChanged}
|
@value-changed=${this._stepDataChanged}
|
||||||
></ha-form>
|
></ha-auth-form>
|
||||||
${this.clientId === genClientId() &&
|
${this.clientId === genClientId() &&
|
||||||
!["select_mfa_module", "mfa"].includes(step.step_id)
|
!["select_mfa_module", "mfa"].includes(step.step_id)
|
||||||
? html`
|
? html`
|
||||||
@@ -395,20 +390,6 @@ export class HaAuthFlow extends LitElement {
|
|||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
.action {
|
|
||||||
margin: 24px 0 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
/* Align with the rest of the form. */
|
|
||||||
.store-token {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: -16px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
76
src/auth/ha-auth-form-string.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/* eslint-disable lit/prefer-static-styles */
|
||||||
|
import { TemplateResult, html } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import { HaFormString } from "../components/ha-form/ha-form-string";
|
||||||
|
import "../components/ha-icon-button";
|
||||||
|
import "./ha-auth-textfield";
|
||||||
|
|
||||||
|
@customElement("ha-auth-form-string")
|
||||||
|
export class HaAuthFormString extends HaFormString {
|
||||||
|
protected createRenderRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-auth-form-string {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
ha-auth-form-string[own-margin] {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
ha-auth-form-string ha-auth-textfield {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
ha-auth-form-string ha-icon-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
inset-inline-start: initial;
|
||||||
|
inset-inline-end: 8px;
|
||||||
|
--mdc-icon-button-size: 40px;
|
||||||
|
--mdc-icon-size: 20px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
direction: var(--direction);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ha-auth-textfield
|
||||||
|
.type=${
|
||||||
|
!this.isPassword
|
||||||
|
? this.stringType
|
||||||
|
: this.unmaskedPassword
|
||||||
|
? "text"
|
||||||
|
: "password"
|
||||||
|
}
|
||||||
|
.label=${this.label}
|
||||||
|
.value=${this.data || ""}
|
||||||
|
.helper=${this.helper}
|
||||||
|
helperPersistent
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.required=${this.schema.required}
|
||||||
|
.autoValidate=${this.schema.required}
|
||||||
|
.name=${this.schema.name}
|
||||||
|
.autocomplete=${this.schema.autocomplete}
|
||||||
|
.suffix=${
|
||||||
|
this.isPassword
|
||||||
|
? // reserve some space for the icon.
|
||||||
|
html`<div style="width: 24px"></div>`
|
||||||
|
: this.schema.description?.suffix
|
||||||
|
}
|
||||||
|
.validationMessage=${this.schema.required ? "Required" : undefined}
|
||||||
|
@input=${this._valueChanged}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-auth-textfield>
|
||||||
|
${this.renderIcon()}
|
||||||
|
</ha-auth-textfield>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-auth-form-string": HaAuthFormString;
|
||||||
|
}
|
||||||
|
}
|
44
src/auth/ha-auth-form.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/* eslint-disable lit/prefer-static-styles */
|
||||||
|
import { html } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import { HaForm } from "../components/ha-form/ha-form";
|
||||||
|
import "./ha-auth-form-string";
|
||||||
|
|
||||||
|
@customElement("ha-auth-form")
|
||||||
|
export class HaAuthForm extends HaForm {
|
||||||
|
protected fieldElementName(type: string): string {
|
||||||
|
if (type === "string") {
|
||||||
|
return `ha-auth-form-${type}`;
|
||||||
|
}
|
||||||
|
return super.fieldElementName(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createRenderRoot() {
|
||||||
|
// attach it as soon as possible to make sure we fetch all events.
|
||||||
|
this.addValueChangedListener(this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-auth-form .root > * {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
ha-auth-form .root > *:not([own-margin]):not(:last-child) {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
ha-auth-form ha-alert[own-margin] {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
${super.render()}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-auth-form": HaAuthForm;
|
||||||
|
}
|
||||||
|
}
|
254
src/auth/ha-auth-textfield.ts
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
/* eslint-disable lit/value-after-constraints */
|
||||||
|
/* eslint-disable lit/prefer-static-styles */
|
||||||
|
import { floatingLabel } from "@material/mwc-floating-label/mwc-floating-label-directive";
|
||||||
|
import { TemplateResult, html } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { live } from "lit/directives/live";
|
||||||
|
import { HaTextField } from "../components/ha-textfield";
|
||||||
|
|
||||||
|
@customElement("ha-auth-textfield")
|
||||||
|
export class HaAuthTextField extends HaTextField {
|
||||||
|
protected renderLabel(): TemplateResult | string {
|
||||||
|
return !this.label
|
||||||
|
? ""
|
||||||
|
: html`
|
||||||
|
<span
|
||||||
|
.floatingLabelFoundation=${floatingLabel(
|
||||||
|
this.label
|
||||||
|
) as unknown as any}
|
||||||
|
.id=${this.name}
|
||||||
|
>${this.label}</span
|
||||||
|
>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderInput(shouldRenderHelperText: boolean): TemplateResult {
|
||||||
|
const minOrUndef = this.minLength === -1 ? undefined : this.minLength;
|
||||||
|
const maxOrUndef = this.maxLength === -1 ? undefined : this.maxLength;
|
||||||
|
const autocapitalizeOrUndef = this.autocapitalize
|
||||||
|
? (this.autocapitalize as
|
||||||
|
| "off"
|
||||||
|
| "none"
|
||||||
|
| "on"
|
||||||
|
| "sentences"
|
||||||
|
| "words"
|
||||||
|
| "characters")
|
||||||
|
: undefined;
|
||||||
|
const showValidationMessage = this.validationMessage && !this.isUiValid;
|
||||||
|
const ariaLabelledbyOrUndef = this.label ? this.name : undefined;
|
||||||
|
const ariaControlsOrUndef = shouldRenderHelperText
|
||||||
|
? "helper-text"
|
||||||
|
: undefined;
|
||||||
|
const ariaDescribedbyOrUndef =
|
||||||
|
this.focused || this.helperPersistent || showValidationMessage
|
||||||
|
? "helper-text"
|
||||||
|
: undefined;
|
||||||
|
// TODO: live() directive needs casting for lit-analyzer
|
||||||
|
// https://github.com/runem/lit-analyzer/pull/91/files
|
||||||
|
// TODO: lit-analyzer labels min/max as (number|string) instead of string
|
||||||
|
return html` <input
|
||||||
|
aria-labelledby=${ifDefined(ariaLabelledbyOrUndef)}
|
||||||
|
aria-controls=${ifDefined(ariaControlsOrUndef)}
|
||||||
|
aria-describedby=${ifDefined(ariaDescribedbyOrUndef)}
|
||||||
|
class="mdc-text-field__input"
|
||||||
|
type=${this.type}
|
||||||
|
.value=${live(this.value) as unknown as string}
|
||||||
|
?disabled=${this.disabled}
|
||||||
|
placeholder=${this.placeholder}
|
||||||
|
?required=${this.required}
|
||||||
|
?readonly=${this.readOnly}
|
||||||
|
minlength=${ifDefined(minOrUndef)}
|
||||||
|
maxlength=${ifDefined(maxOrUndef)}
|
||||||
|
pattern=${ifDefined(this.pattern ? this.pattern : undefined)}
|
||||||
|
min=${ifDefined(this.min === "" ? undefined : (this.min as number))}
|
||||||
|
max=${ifDefined(this.max === "" ? undefined : (this.max as number))}
|
||||||
|
step=${ifDefined(this.step === null ? undefined : (this.step as number))}
|
||||||
|
size=${ifDefined(this.size === null ? undefined : this.size)}
|
||||||
|
name=${ifDefined(this.name === "" ? undefined : this.name)}
|
||||||
|
inputmode=${ifDefined(this.inputMode)}
|
||||||
|
autocapitalize=${ifDefined(autocapitalizeOrUndef)}
|
||||||
|
@input=${this.handleInputChange}
|
||||||
|
@focus=${this.onInputFocus}
|
||||||
|
@blur=${this.onInputBlur}
|
||||||
|
/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-auth-textfield {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
ha-auth-textfield:not([disabled]):hover
|
||||||
|
:not(.mdc-text-field--invalid):not(.mdc-text-field--focused)
|
||||||
|
mwc-notched-outline {
|
||||||
|
--mdc-notched-outline-border-color: var(
|
||||||
|
--mdc-text-field-outlined-hover-border-color,
|
||||||
|
rgba(0, 0, 0, 0.87)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field:not(.mdc-text-field--outlined) {
|
||||||
|
background-color: var(--mdc-text-field-fill-color, whitesmoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--invalid
|
||||||
|
mwc-notched-outline {
|
||||||
|
--mdc-notched-outline-border-color: var(
|
||||||
|
--mdc-text-field-error-color,
|
||||||
|
var(--mdc-theme-error, #b00020)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--invalid
|
||||||
|
+ .mdc-text-field-helper-line
|
||||||
|
.mdc-text-field-character-counter,
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--invalid
|
||||||
|
.mdc-text-field__icon {
|
||||||
|
color: var(
|
||||||
|
--mdc-text-field-error-color,
|
||||||
|
var(--mdc-theme-error, #b00020)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||||
|
.mdc-text-field--focused
|
||||||
|
)
|
||||||
|
.mdc-floating-label,
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||||
|
.mdc-text-field--focused
|
||||||
|
)
|
||||||
|
.mdc-floating-label::after {
|
||||||
|
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--focused
|
||||||
|
mwc-notched-outline {
|
||||||
|
--mdc-notched-outline-stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||||
|
mwc-notched-outline {
|
||||||
|
--mdc-notched-outline-border-color: var(
|
||||||
|
--mdc-text-field-focused-label-color,
|
||||||
|
var(--mdc-theme-primary, rgba(98, 0, 238, 0.87))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||||
|
.mdc-floating-label {
|
||||||
|
color: #6200ee;
|
||||||
|
color: var(--mdc-theme-primary, #6200ee);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field
|
||||||
|
.mdc-text-field__input {
|
||||||
|
color: var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87));
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field
|
||||||
|
.mdc-text-field__input::placeholder {
|
||||||
|
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field-helper-line
|
||||||
|
.mdc-text-field-helper-text:not(
|
||||||
|
.mdc-text-field-helper-text--validation-msg
|
||||||
|
),
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field-helper-line:not(.mdc-text-field--invalid)
|
||||||
|
.mdc-text-field-character-counter {
|
||||||
|
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field:not(.mdc-text-field--outlined) {
|
||||||
|
background-color: var(--mdc-text-field-disabled-fill-color, #fafafa);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field.mdc-text-field--outlined
|
||||||
|
mwc-notched-outline {
|
||||||
|
--mdc-notched-outline-border-color: var(
|
||||||
|
--mdc-text-field-outlined-disabled-border-color,
|
||||||
|
rgba(0, 0, 0, 0.06)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||||
|
.mdc-text-field--focused
|
||||||
|
)
|
||||||
|
.mdc-floating-label,
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||||
|
.mdc-text-field--focused
|
||||||
|
)
|
||||||
|
.mdc-floating-label::after {
|
||||||
|
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield[disabled] .mdc-text-field .mdc-text-field__input,
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field
|
||||||
|
.mdc-text-field__input::placeholder {
|
||||||
|
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field-helper-line
|
||||||
|
.mdc-text-field-helper-text,
|
||||||
|
ha-auth-textfield[disabled]
|
||||||
|
.mdc-text-field-helper-line
|
||||||
|
.mdc-text-field-character-counter {
|
||||||
|
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||||
|
}
|
||||||
|
ha-auth-textfield:not([disabled])
|
||||||
|
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||||
|
.mdc-floating-label {
|
||||||
|
color: var(--mdc-theme-primary, #6200ee);
|
||||||
|
}
|
||||||
|
ha-auth-textfield[no-spinner] input::-webkit-outer-spin-button,
|
||||||
|
ha-auth-textfield[no-spinner] input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
ha-auth-textfield[no-spinner] input[type="number"] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
${super.render()}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createRenderRoot() {
|
||||||
|
// add parent style to light dom
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = HaTextField.elementStyles as unknown as string;
|
||||||
|
this.append(style);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-auth-textfield": HaAuthTextField;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,7 @@
|
|||||||
import punycode from "punycode";
|
/* eslint-disable lit/prefer-static-styles */
|
||||||
import {
|
import { html, LitElement, nothing, PropertyValues } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
nothing,
|
|
||||||
PropertyValues,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import punycode from "punycode";
|
||||||
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
||||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
import { extractSearchParamsObject } from "../common/url/search-params";
|
||||||
import "../components/ha-alert";
|
import "../components/ha-alert";
|
||||||
@@ -61,13 +55,27 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (this._error) {
|
if (this._error) {
|
||||||
return html`<ha-alert alert-type="error"
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-authorize ha-alert {
|
||||||
|
display: block;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ha-alert alert-type="error"
|
||||||
>${this._error} ${this.redirectUri}</ha-alert
|
>${this._error} ${this.redirectUri}</ha-alert
|
||||||
>`;
|
>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._authProviders) {
|
if (!this._authProviders) {
|
||||||
return html`
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-authorize p {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<p>${this.localize("ui.panel.page-authorize.initializing")}</p>
|
<p>${this.localize("ui.panel.page-authorize.initializing")}</p>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -79,6 +87,25 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
const app = this.clientId && this.clientId in appNames;
|
const app = this.clientId && this.clientId in appNames;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
<style>
|
||||||
|
ha-pick-auth-provider {
|
||||||
|
display: block;
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
ha-auth-flow {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
ha-alert {
|
||||||
|
display: block;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
${!this._ownInstance
|
${!this._ownInstance
|
||||||
? html`<ha-alert .alertType=${app ? "info" : "warning"}>
|
? html`<ha-alert .alertType=${app ? "info" : "warning"}>
|
||||||
${app
|
${app
|
||||||
@@ -123,6 +150,10 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createRenderRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
@@ -217,25 +248,4 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
private async _handleAuthProviderPick(ev) {
|
private async _handleAuthProviderPick(ev) {
|
||||||
this._authProvider = ev.detail;
|
this._authProvider = ev.detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
ha-pick-auth-provider {
|
|
||||||
display: block;
|
|
||||||
margin-top: 48px;
|
|
||||||
}
|
|
||||||
ha-auth-flow {
|
|
||||||
display: block;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
ha-alert {
|
|
||||||
display: block;
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,172 +0,0 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
|
||||||
import { styleMap } from "lit/directives/style-map";
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
|
||||||
import type { HaFormSchema } from "../components/ha-form/types";
|
|
||||||
import { autocompleteLoginFields } from "../data/auth";
|
|
||||||
import type { DataEntryFlowStep } from "../data/data_entry_flow";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-password-manager-polyfill": HaPasswordManagerPolyfill;
|
|
||||||
}
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"form-submitted": undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ENABLED_HANDLERS = [
|
|
||||||
"homeassistant",
|
|
||||||
"legacy_api_password",
|
|
||||||
"command_line",
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("ha-password-manager-polyfill")
|
|
||||||
export class HaPasswordManagerPolyfill extends LitElement {
|
|
||||||
@property({ attribute: false }) public step?: DataEntryFlowStep;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public stepData: any;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public boundingRect?: DOMRect;
|
|
||||||
|
|
||||||
private _styleElement?: HTMLStyleElement;
|
|
||||||
|
|
||||||
public connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this._styleElement = document.createElement("style");
|
|
||||||
this._styleElement.textContent = css`
|
|
||||||
/* Polyfill form is sized and vertically aligned with true form, then positioned offscreen
|
|
||||||
rather than hiding so it does not create a new stacking context */
|
|
||||||
.password-manager-polyfill {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
/* Excluding our wrapper, move any children back on screen, including anything injected that might not already be positioned */
|
|
||||||
.password-manager-polyfill > *:not(.wrapper),
|
|
||||||
.password-manager-polyfill > .wrapper > * {
|
|
||||||
position: relative;
|
|
||||||
left: 10000px;
|
|
||||||
}
|
|
||||||
/* Size and hide our polyfill fields */
|
|
||||||
.password-manager-polyfill .underneath {
|
|
||||||
display: block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 16px;
|
|
||||||
border: 0;
|
|
||||||
z-index: -1;
|
|
||||||
height: 21px;
|
|
||||||
/* Transparency is only needed to hide during paint or in case of misalignment,
|
|
||||||
but LastPass will fail if it's 0, so we use 1% */
|
|
||||||
opacity: 0.01;
|
|
||||||
}
|
|
||||||
.password-manager-polyfill input.underneath {
|
|
||||||
height: 28px;
|
|
||||||
margin-bottom: 30.5px;
|
|
||||||
}
|
|
||||||
/* Button position is not important, but size should not be zero */
|
|
||||||
.password-manager-polyfill > input.underneath[type="submit"] {
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
margin: 0 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
/* Ensure injected elements will be on top */
|
|
||||||
.password-manager-polyfill > *:not(.underneath, .wrapper),
|
|
||||||
.password-manager-polyfill > .wrapper > *:not(.underneath) {
|
|
||||||
isolation: isolate;
|
|
||||||
z-index: auto;
|
|
||||||
}
|
|
||||||
`.toString();
|
|
||||||
document.head.append(this._styleElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
public disconnectedCallback() {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
this._styleElement?.remove();
|
|
||||||
delete this._styleElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createRenderRoot() {
|
|
||||||
// Add under document body so the element isn't placed inside any shadow roots
|
|
||||||
return document.body;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (
|
|
||||||
this.step &&
|
|
||||||
this.step.type === "form" &&
|
|
||||||
this.step.step_id === "init" &&
|
|
||||||
ENABLED_HANDLERS.includes(this.step.handler[0])
|
|
||||||
) {
|
|
||||||
return html`
|
|
||||||
<form
|
|
||||||
class="password-manager-polyfill"
|
|
||||||
style=${styleMap({
|
|
||||||
top: `${this.boundingRect?.y || 148}px`,
|
|
||||||
left: `calc(50% - ${
|
|
||||||
(this.boundingRect?.width || 360) / 2
|
|
||||||
}px - 10000px)`,
|
|
||||||
width: `${this.boundingRect?.width || 360}px`,
|
|
||||||
})}
|
|
||||||
action="/auth"
|
|
||||||
method="post"
|
|
||||||
@submit=${this._handleSubmit}
|
|
||||||
>
|
|
||||||
${autocompleteLoginFields(this.step.data_schema).map((input) =>
|
|
||||||
this.render_input(input)
|
|
||||||
)}
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
value="Login"
|
|
||||||
class="underneath"
|
|
||||||
tabindex="-2"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private render_input(schema: HaFormSchema) {
|
|
||||||
const inputType = schema.name.includes("password") ? "password" : "text";
|
|
||||||
if (schema.type !== "string") {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return html`
|
|
||||||
<!-- Label is a sibling so it can be stacked underneath without affecting injections adjacent to input (e.g. LastPass) -->
|
|
||||||
<label for=${schema.name} class="underneath" aria-hidden="true">
|
|
||||||
${schema.name}
|
|
||||||
</label>
|
|
||||||
<!-- LastPass fails if the input is hidden directly, so we trick it and hide a wrapper instead -->
|
|
||||||
<div class="wrapper" aria-hidden="true">
|
|
||||||
<!-- LastPass fails with tabindex of -1, so we trick with -2 -->
|
|
||||||
<input
|
|
||||||
class="underneath"
|
|
||||||
tabindex="-2"
|
|
||||||
.id=${schema.name}
|
|
||||||
.name=${schema.name}
|
|
||||||
.type=${inputType}
|
|
||||||
.value=${this.stepData[schema.name] || ""}
|
|
||||||
.autocomplete=${schema.autocomplete}
|
|
||||||
@input=${this._valueChanged}
|
|
||||||
@change=${this._valueChanged}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleSubmit(ev: SubmitEvent) {
|
|
||||||
ev.preventDefault();
|
|
||||||
fireEvent(this, "form-submitted");
|
|
||||||
}
|
|
||||||
|
|
||||||
private _valueChanged(ev: Event) {
|
|
||||||
const target = ev.target as HTMLInputElement;
|
|
||||||
this.stepData = { ...this.stepData, [target.id]: target.value };
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: this.stepData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
9
src/common/array/combinations.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export function getAllCombinations<T>(arr: T[]) {
|
||||||
|
return arr.reduce<T[][]>(
|
||||||
|
(combinations, element) =>
|
||||||
|
combinations.concat(
|
||||||
|
combinations.map((combination) => [...combination, element])
|
||||||
|
),
|
||||||
|
[[]]
|
||||||
|
);
|
||||||
|
}
|
@@ -15,6 +15,8 @@ import {
|
|||||||
mdiCalendarClock,
|
mdiCalendarClock,
|
||||||
mdiCarCoolantLevel,
|
mdiCarCoolantLevel,
|
||||||
mdiCash,
|
mdiCash,
|
||||||
|
mdiChatSleep,
|
||||||
|
mdiClipboardList,
|
||||||
mdiClock,
|
mdiClock,
|
||||||
mdiCloudUpload,
|
mdiCloudUpload,
|
||||||
mdiCog,
|
mdiCog,
|
||||||
@@ -31,7 +33,6 @@ import {
|
|||||||
mdiGauge,
|
mdiGauge,
|
||||||
mdiGoogleAssistant,
|
mdiGoogleAssistant,
|
||||||
mdiGoogleCirclesCommunities,
|
mdiGoogleCirclesCommunities,
|
||||||
mdiHomeAssistant,
|
|
||||||
mdiHomeAutomation,
|
mdiHomeAutomation,
|
||||||
mdiImage,
|
mdiImage,
|
||||||
mdiImageFilterFrames,
|
mdiImageFilterFrames,
|
||||||
@@ -70,6 +71,8 @@ import {
|
|||||||
mdiWifi,
|
mdiWifi,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
|
||||||
|
import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg";
|
||||||
|
|
||||||
// Constants should be alphabetically sorted by name.
|
// Constants should be alphabetically sorted by name.
|
||||||
// Arrays with values should be alphabetically sorted if order doesn't matter.
|
// Arrays with values should be alphabetically sorted if order doesn't matter.
|
||||||
// Each constant should have a description what it is supposed to be used for.
|
// Each constant should have a description what it is supposed to be used for.
|
||||||
@@ -118,11 +121,13 @@ export const FIXED_DOMAIN_ICONS = {
|
|||||||
siren: mdiBullhorn,
|
siren: mdiBullhorn,
|
||||||
stt: mdiMicrophoneMessage,
|
stt: mdiMicrophoneMessage,
|
||||||
text: mdiFormTextbox,
|
text: mdiFormTextbox,
|
||||||
|
todo: mdiClipboardList,
|
||||||
time: mdiClock,
|
time: mdiClock,
|
||||||
timer: mdiTimerOutline,
|
timer: mdiTimerOutline,
|
||||||
tts: mdiSpeakerMessage,
|
tts: mdiSpeakerMessage,
|
||||||
updater: mdiCloudUpload,
|
updater: mdiCloudUpload,
|
||||||
vacuum: mdiRobotVacuum,
|
vacuum: mdiRobotVacuum,
|
||||||
|
wake_word: mdiChatSleep,
|
||||||
zone: mdiMapMarkerRadius,
|
zone: mdiMapMarkerRadius,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -5,12 +5,15 @@ import { FrontendLocaleData, TimeZone } from "../../data/translation";
|
|||||||
const calcZonedDate = (
|
const calcZonedDate = (
|
||||||
date: Date,
|
date: Date,
|
||||||
tz: string,
|
tz: string,
|
||||||
fn: (date: Date, options?: any) => Date,
|
fn: (date: Date, options?: any) => Date | number | boolean,
|
||||||
options?
|
options?
|
||||||
) => {
|
) => {
|
||||||
const inputZoned = utcToZonedTime(date, tz);
|
const inputZoned = utcToZonedTime(date, tz);
|
||||||
const fnZoned = fn(inputZoned, options);
|
const fnZoned = fn(inputZoned, options);
|
||||||
return zonedTimeToUtc(fnZoned, tz);
|
if (fnZoned instanceof Date) {
|
||||||
|
return zonedTimeToUtc(fnZoned, tz) as Date;
|
||||||
|
}
|
||||||
|
return fnZoned;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const calcDate = (
|
export const calcDate = (
|
||||||
@@ -21,5 +24,16 @@ export const calcDate = (
|
|||||||
options?
|
options?
|
||||||
) =>
|
) =>
|
||||||
locale.time_zone === TimeZone.server
|
locale.time_zone === TimeZone.server
|
||||||
? calcZonedDate(date, config.time_zone, fn, options)
|
? (calcZonedDate(date, config.time_zone, fn, options) as Date)
|
||||||
|
: fn(date, options);
|
||||||
|
|
||||||
|
export const calcDateProperty = (
|
||||||
|
date: Date,
|
||||||
|
fn: (date: Date, options?: any) => boolean | number,
|
||||||
|
locale: FrontendLocaleData,
|
||||||
|
config: HassConfig,
|
||||||
|
options?
|
||||||
|
) =>
|
||||||
|
locale.time_zone === TimeZone.server
|
||||||
|
? (calcZonedDate(date, config.time_zone, fn, options) as number | boolean)
|
||||||
: fn(date, options);
|
: fn(date, options);
|
||||||
|
@@ -37,6 +37,23 @@ const formatDateMem = memoizeOne(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Aug 10, 2021
|
||||||
|
export const formatDateShort = (
|
||||||
|
dateObj: Date,
|
||||||
|
locale: FrontendLocaleData,
|
||||||
|
config: HassConfig
|
||||||
|
) => formatDateShortMem(locale, config.time_zone).format(dateObj);
|
||||||
|
|
||||||
|
const formatDateShortMem = memoizeOne(
|
||||||
|
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||||
|
new Intl.DateTimeFormat(locale.language, {
|
||||||
|
year: "numeric",
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// 10/08/2021
|
// 10/08/2021
|
||||||
export const formatDateNumeric = (
|
export const formatDateNumeric = (
|
||||||
dateObj: Date,
|
dateObj: Date,
|
||||||
@@ -102,13 +119,13 @@ const formatDateNumericMem = memoizeOne(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Aug 10
|
// Aug 10
|
||||||
export const formatDateShort = (
|
export const formatDateVeryShort = (
|
||||||
dateObj: Date,
|
dateObj: Date,
|
||||||
locale: FrontendLocaleData,
|
locale: FrontendLocaleData,
|
||||||
config: HassConfig
|
config: HassConfig
|
||||||
) => formatDateShortMem(locale, config.time_zone).format(dateObj);
|
) => formatDateVeryShortMem(locale, config.time_zone).format(dateObj);
|
||||||
|
|
||||||
const formatDateShortMem = memoizeOne(
|
const formatDateVeryShortMem = memoizeOne(
|
||||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||||
new Intl.DateTimeFormat(locale.language, {
|
new Intl.DateTimeFormat(locale.language, {
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
@@ -1,8 +1,13 @@
|
|||||||
import { HaDurationData } from "../../components/ha-duration-input";
|
import { HaDurationData } from "../../components/ha-duration-input";
|
||||||
|
import { FrontendLocaleData } from "../../data/translation";
|
||||||
|
import "../../resources/intl-polyfill";
|
||||||
|
|
||||||
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);
|
const leftPad = (num: number) => (num < 10 ? `0${num}` : num);
|
||||||
|
|
||||||
export const formatDuration = (duration: HaDurationData) => {
|
export const formatDuration = (
|
||||||
|
locale: FrontendLocaleData,
|
||||||
|
duration: HaDurationData
|
||||||
|
) => {
|
||||||
const d = duration.days || 0;
|
const d = duration.days || 0;
|
||||||
const h = duration.hours || 0;
|
const h = duration.hours || 0;
|
||||||
const m = duration.minutes || 0;
|
const m = duration.minutes || 0;
|
||||||
@@ -10,7 +15,11 @@ export const formatDuration = (duration: HaDurationData) => {
|
|||||||
const ms = duration.milliseconds || 0;
|
const ms = duration.milliseconds || 0;
|
||||||
|
|
||||||
if (d > 0) {
|
if (d > 0) {
|
||||||
return `${d} day${d === 1 ? "" : "s"} ${h}:${leftPad(m)}:${leftPad(s)}`;
|
return `${Intl.NumberFormat(locale.language, {
|
||||||
|
style: "unit",
|
||||||
|
unit: "day",
|
||||||
|
unitDisplay: "long",
|
||||||
|
}).format(d)} ${h}:${leftPad(m)}:${leftPad(s)}`;
|
||||||
}
|
}
|
||||||
if (h > 0) {
|
if (h > 0) {
|
||||||
return `${h}:${leftPad(m)}:${leftPad(s)}`;
|
return `${h}:${leftPad(m)}:${leftPad(s)}`;
|
||||||
@@ -19,10 +28,18 @@ export const formatDuration = (duration: HaDurationData) => {
|
|||||||
return `${m}:${leftPad(s)}`;
|
return `${m}:${leftPad(s)}`;
|
||||||
}
|
}
|
||||||
if (s > 0) {
|
if (s > 0) {
|
||||||
return `${s} second${s === 1 ? "" : "s"}`;
|
return Intl.NumberFormat(locale.language, {
|
||||||
|
style: "unit",
|
||||||
|
unit: "second",
|
||||||
|
unitDisplay: "long",
|
||||||
|
}).format(s);
|
||||||
}
|
}
|
||||||
if (ms > 0) {
|
if (ms > 0) {
|
||||||
return `${ms} millisecond${ms === 1 ? "" : "s"}`;
|
return Intl.NumberFormat(locale.language, {
|
||||||
|
style: "unit",
|
||||||
|
unit: "millisecond",
|
||||||
|
unitDisplay: "long",
|
||||||
|
}).format(ms);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|