mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-11 18:47:24 +00:00
Compare commits
344 Commits
conditiona
...
dashboard_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b44c7ce64 | ||
|
|
de370d6384 | ||
|
|
1cf928b425 | ||
|
|
b9c62c34a0 | ||
|
|
37554213dc | ||
|
|
3845fd2ef9 | ||
|
|
837b36a430 | ||
|
|
bff09a7a36 | ||
|
|
a5e5efa526 | ||
|
|
1cd5b9ff37 | ||
|
|
6a11155dd7 | ||
|
|
1acb76d2f0 | ||
|
|
2e8a40efd5 | ||
|
|
845a0e4e39 | ||
|
|
9417f13cc9 | ||
|
|
d3f52f3bc0 | ||
|
|
7d0ec00fde | ||
|
|
6dbb5dd4be | ||
|
|
f30e7948cb | ||
|
|
41472d8160 | ||
|
|
e596d32426 | ||
|
|
89de62812f | ||
|
|
3e391fcbe6 | ||
|
|
708ab2ed69 | ||
|
|
1d45cb78fe | ||
|
|
d3f6ebd1d0 | ||
|
|
21644c70b3 | ||
|
|
eb75389cac | ||
|
|
cd4f3a091b | ||
|
|
19c1973ec1 | ||
|
|
a8efbe5b06 | ||
|
|
44b5c66c19 | ||
|
|
3fa3bba4d8 | ||
|
|
ab09c821a2 | ||
|
|
37b7395986 | ||
|
|
4933413140 | ||
|
|
1c16bd5ab2 | ||
|
|
3a926c6f83 | ||
|
|
7a619ad55a | ||
|
|
bc21425981 | ||
|
|
f2505c0798 | ||
|
|
cee8c756fc | ||
|
|
396b3aace9 | ||
|
|
28dc1e4da9 | ||
|
|
aa0419e783 | ||
|
|
fd9c24d05e | ||
|
|
8bdbe8c6a6 | ||
|
|
39550cefa0 | ||
|
|
589e3b63c7 | ||
|
|
0cc38278b9 | ||
|
|
aafdf7bed7 | ||
|
|
a7f6ce3079 | ||
|
|
3d03f74d66 | ||
|
|
e09a6a23fc | ||
|
|
9e4bb6ed0c | ||
|
|
c45c8ab5c0 | ||
|
|
5ef6973933 | ||
|
|
1df344580a | ||
|
|
25acc479b9 | ||
|
|
c9f051374b | ||
|
|
bf5116fc0b | ||
|
|
edc8cb1d2e | ||
|
|
a60bb3ae0a | ||
|
|
784f753f07 | ||
|
|
e63c7e3763 | ||
|
|
cfa522068c | ||
|
|
5f2375fe84 | ||
|
|
a9e34d7590 | ||
|
|
463cfb869f | ||
|
|
acb5a2b283 | ||
|
|
08597d6e91 | ||
|
|
9478485268 | ||
|
|
0af9ec2fff | ||
|
|
f6e74de05e | ||
|
|
8b0373d5c0 | ||
|
|
d4218250af | ||
|
|
78783942be | ||
|
|
17ad8013ca | ||
|
|
f70767f084 | ||
|
|
3972394d82 | ||
|
|
5331fb4f5f | ||
|
|
16df352ba8 | ||
|
|
8e1e75dee1 | ||
|
|
d27b4e04a9 | ||
|
|
36f7b34ac5 | ||
|
|
c0ff24bf1b | ||
|
|
abb5aa348f | ||
|
|
37ab5cbdc3 | ||
|
|
42be3b331c | ||
|
|
71e05f79c2 | ||
|
|
c5a0a5bbbf | ||
|
|
c3d809fcf3 | ||
|
|
cbd0c39091 | ||
|
|
113eb5be24 | ||
|
|
c497669fd3 | ||
|
|
c32ca5885b | ||
|
|
3c792c4019 | ||
|
|
9762e61ee2 | ||
|
|
c09c39998b | ||
|
|
d37e29c247 | ||
|
|
51f22cd74a | ||
|
|
b57dc968bd | ||
|
|
e2e8cb785a | ||
|
|
9961a4ae3f | ||
|
|
c25755bfcd | ||
|
|
40983619d6 | ||
|
|
54758b5962 | ||
|
|
4f09485b20 | ||
|
|
9207f6c407 | ||
|
|
1a2312460a | ||
|
|
951b88ab4c | ||
|
|
d3cc57d8b4 | ||
|
|
4e9b118728 | ||
|
|
f1748e4dd5 | ||
|
|
d491d8f5ac | ||
|
|
cf0fde0f3c | ||
|
|
a7dc2cfaa6 | ||
|
|
d8c7db6ebf | ||
|
|
c3743b57ea | ||
|
|
e16a101de8 | ||
|
|
94ad47c60e | ||
|
|
184ef7b7ff | ||
|
|
81053f2e07 | ||
|
|
e8b4eeec67 | ||
|
|
b0b7e77e28 | ||
|
|
763f80b46a | ||
|
|
0c2531a7ee | ||
|
|
8d2ec8098c | ||
|
|
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 | ||
|
|
47022d3a04 | ||
|
|
60345f3fe8 | ||
|
|
40bb6566b8 | ||
|
|
db272e3e18 | ||
|
|
35496ead23 | ||
|
|
41403a5d35 | ||
|
|
8acf557137 | ||
|
|
4e62370d18 | ||
|
|
f90ab60354 | ||
|
|
d187aa0ac6 | ||
|
|
96597b3963 |
8
.github/workflows/cast_deployment.yaml
vendored
8
.github/workflows/cast_deployment.yaml
vendored
@@ -21,12 +21,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -57,12 +57,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
28
.github/workflows/ci.yaml
vendored
28
.github/workflows/ci.yaml
vendored
@@ -24,9 +24,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -55,9 +55,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -73,9 +73,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -85,15 +85,21 @@ jobs:
|
||||
run: ./node_modules/.bin/gulp build-app
|
||||
env:
|
||||
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:
|
||||
name: Build supervisor
|
||||
needs: [lint, test]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -103,3 +109,9 @@ jobs:
|
||||
run: ./node_modules/.bin/gulp build-hassio
|
||||
env:
|
||||
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
2
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
|
||||
8
.github/workflows/demo_deployment.yaml
vendored
8
.github/workflows/demo_deployment.yaml
vendored
@@ -22,12 +22,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -58,12 +58,12 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
4
.github/workflows/design_deployment.yaml
vendored
4
.github/workflows/design_deployment.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
4
.github/workflows/design_preview.yaml
vendored
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')
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
8
.github/workflows/nightly.yaml
vendored
8
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@v4
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -57,14 +57,14 @@ jobs:
|
||||
run: tar -czvf translations.tar.gz translations
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist/home_assistant_frontend*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.3
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
|
||||
25
.github/workflows/relative-ci.yaml
vendored
Normal file
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
6
.github/workflows/release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
contents: write # Required to upload release assets
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Verify version
|
||||
uses: home-assistant/actions/helpers/verify-version@master
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3.8.1
|
||||
uses: actions/setup-node@v4.0.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2023.09.1
|
||||
uses: home-assistant/wheels@2023.10.5
|
||||
with:
|
||||
abi: cp311
|
||||
tag: musllinux_1_2
|
||||
|
||||
2
.github/workflows/translations.yaml
vendored
2
.github/workflows/translations.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4.1.0
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -47,3 +47,6 @@ src/cast/dev_const.ts
|
||||
|
||||
# Home Assistant config
|
||||
/config/
|
||||
|
||||
# Jetbrains
|
||||
/.idea/
|
||||
|
||||
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
File diff suppressed because one or more lines are too long
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
9
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
File diff suppressed because one or more lines are too long
874
.yarn/releases/yarn-3.6.3.cjs
vendored
874
.yarn/releases/yarn-3.6.3.cjs
vendored
File diff suppressed because one or more lines are too long
893
.yarn/releases/yarn-4.0.1.cjs
vendored
Executable file
893
.yarn/releases/yarn-4.0.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
12
.yarnrc.yml
12
.yarnrc.yml
@@ -1,11 +1,9 @@
|
||||
compressionLevel: mixed
|
||||
|
||||
defaultSemverRangePrefix: ""
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.0.1.cjs
|
||||
|
||||
@@ -12,11 +12,7 @@ module.exports.sourceMapURL = () => {
|
||||
};
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
module.exports.ignorePackages = () => [];
|
||||
|
||||
// Files from NPM packages that we should replace with empty file
|
||||
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
@@ -98,7 +94,7 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
"@babel/preset-env",
|
||||
{
|
||||
useBuiltIns: latestBuild ? false : "entry",
|
||||
corejs: latestBuild ? false : { version: "3.32", proposals: true },
|
||||
corejs: latestBuild ? false : { version: "3.33", proposals: true },
|
||||
bugfixes: true,
|
||||
shippedProposals: true,
|
||||
},
|
||||
@@ -149,7 +145,7 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
|
||||
sourceMaps: !isTestBuild,
|
||||
});
|
||||
|
||||
const nameSuffix = (latestBuild) => (latestBuild ? "-latest" : "-es5");
|
||||
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
@@ -183,7 +179,7 @@ const publicPath = (latestBuild, root = "") =>
|
||||
module.exports.config = {
|
||||
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
|
||||
return {
|
||||
name: "app" + nameSuffix(latestBuild),
|
||||
name: "frontend" + nameSuffix(latestBuild),
|
||||
entry: {
|
||||
service_worker: "./src/entrypoints/service_worker.ts",
|
||||
app: "./src/entrypoints/app.ts",
|
||||
|
||||
@@ -45,8 +45,8 @@ gulp.task(
|
||||
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
|
||||
"copy-static-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
|
||||
...(env.isTestBuild() ? [] : ["compress-app"]),
|
||||
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod")
|
||||
...(env.isTestBuild() ? [] : ["compress-app"])
|
||||
)
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import fs from "fs-extra";
|
||||
import gulp from "gulp";
|
||||
import path from "path";
|
||||
import paths from "../paths.cjs";
|
||||
import env from "../env.cjs";
|
||||
|
||||
const npmPath = (...parts) =>
|
||||
path.resolve(paths.polymer_dir, "node_modules", ...parts);
|
||||
@@ -62,6 +63,9 @@ function copyPolyfills(staticDir) {
|
||||
}
|
||||
|
||||
function copyLoaderJS(staticDir) {
|
||||
if (!env.useRollup()) {
|
||||
return;
|
||||
}
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
copyFileDir(npmPath("systemjs/dist/s.min.js"), staticPath("js"));
|
||||
copyFileDir(npmPath("systemjs/dist/s.min.js.map"), staticPath("js"));
|
||||
|
||||
@@ -1,51 +1,54 @@
|
||||
import { deleteSync } from "del";
|
||||
import { mkdir, readFile, writeFile } from "fs/promises";
|
||||
import gulp from "gulp";
|
||||
import path from "path";
|
||||
import { join, resolve } from "node:path";
|
||||
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 = {
|
||||
"intl-relativetimeformat": "RelativeTimeFormat",
|
||||
const INTL_POLYFILLS = {
|
||||
"intl-datetimeformat": "DateTimeFormat",
|
||||
"intl-numberformat": "NumberFormat",
|
||||
"intl-displaynames": "DisplayNames",
|
||||
"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;
|
||||
try {
|
||||
localeData = await readFile(
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
`node_modules/@formatjs/${pkg}/locale-data/${lang}.js`
|
||||
),
|
||||
join(formatjsDir, pkg, subDir, `${lang}.js`),
|
||||
"utf-8"
|
||||
);
|
||||
} catch (e) {
|
||||
// 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;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
// Convert to JSON
|
||||
const className = INTL_PACKAGES[pkg];
|
||||
localeData = localeData
|
||||
.replace(
|
||||
new RegExp(
|
||||
`\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`,
|
||||
"im"
|
||||
),
|
||||
""
|
||||
)
|
||||
.replace(/\)\s*}/im, "");
|
||||
const obj = INTL_POLYFILLS[pkg];
|
||||
const dataRegex = new RegExp(
|
||||
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
|
||||
"s"
|
||||
);
|
||||
localeData = localeData.match(dataRegex)?.groups?.data;
|
||||
if (!localeData) {
|
||||
throw Error(`Failed to extract data for language ${lang} from ${pkg}`);
|
||||
}
|
||||
// Parse to validate JSON, then stringify to minify
|
||||
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]));
|
||||
@@ -53,17 +56,27 @@ gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
||||
gulp.task("create-locale-data", async () => {
|
||||
const translationMeta = JSON.parse(
|
||||
await readFile(
|
||||
path.resolve(paths.translations_src, "translationMetadata.json"),
|
||||
resolve(paths.translations_src, "translationMetadata.json"),
|
||||
"utf-8"
|
||||
)
|
||||
);
|
||||
const conversions = [];
|
||||
for (const pkg of Object.keys(INTL_PACKAGES)) {
|
||||
await mkdir(path.join(outDir, pkg), { recursive: true });
|
||||
for (const pkg of Object.keys(INTL_POLYFILLS)) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await mkdir(join(outDir, pkg), { recursive: true });
|
||||
for (const lang of Object.keys(translationMeta)) {
|
||||
conversions.push(convertToJSON(pkg, lang));
|
||||
}
|
||||
}
|
||||
conversions.push(
|
||||
convertToJSON(
|
||||
"intl-datetimeformat",
|
||||
"add-all-tz",
|
||||
".",
|
||||
"__addTZData",
|
||||
false
|
||||
)
|
||||
);
|
||||
await Promise.all(conversions);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { createHash } from "crypto";
|
||||
import { deleteSync } from "del";
|
||||
import {
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
writeFile,
|
||||
} from "fs";
|
||||
import { mkdirSync, readdirSync, readFileSync, renameSync } from "fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import gulp from "gulp";
|
||||
import flatmap from "gulp-flatmap";
|
||||
import transform from "gulp-json-transform";
|
||||
@@ -136,27 +131,23 @@ gulp.task("ensure-translations-build-dir", async () => {
|
||||
mkdirSync(workDir, { recursive: true });
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", (cb) => {
|
||||
writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
test: {
|
||||
nativeName: "Test",
|
||||
},
|
||||
}),
|
||||
cb
|
||||
);
|
||||
});
|
||||
gulp.task("create-test-metadata", () =>
|
||||
env.isProdBuild()
|
||||
? Promise.resolve()
|
||||
: writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({ test: { nativeName: "Test" } })
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"create-test-translation",
|
||||
gulp.series("create-test-metadata", () =>
|
||||
gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
)
|
||||
gulp.task("create-test-translation", () =>
|
||||
env.isProdBuild()
|
||||
? Promise.resolve()
|
||||
: gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -188,16 +179,11 @@ gulp.task("build-master-translation", () => {
|
||||
|
||||
gulp.task("build-merged-translations", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
inFrontendDir + "/*.json",
|
||||
"!" + inFrontendDir + "/en.json",
|
||||
workDir + "/test.json",
|
||||
],
|
||||
{
|
||||
allowEmpty: true,
|
||||
}
|
||||
)
|
||||
.src([
|
||||
inFrontendDir + "/*.json",
|
||||
"!" + inFrontendDir + "/en.json",
|
||||
...(env.isProdBuild() ? [] : [workDir + "/test.json"]),
|
||||
])
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
flatmap((stream, file) => {
|
||||
@@ -377,14 +363,11 @@ gulp.task("build-translation-flatten-supervisor", () =>
|
||||
|
||||
gulp.task("build-translation-write-metadata", () =>
|
||||
gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
workDir + "/testMetadata.json",
|
||||
workDir + "/translationFingerprints.json",
|
||||
],
|
||||
{ allowEmpty: true }
|
||||
)
|
||||
.src([
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
...(env.isProdBuild() ? [] : [workDir + "/testMetadata.json"]),
|
||||
workDir + "/translationFingerprints.json",
|
||||
])
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform((data) => {
|
||||
@@ -415,7 +398,7 @@ gulp.task("build-translation-write-metadata", () =>
|
||||
gulp.task(
|
||||
"create-translations",
|
||||
gulp.series(
|
||||
...(env.isProdBuild() ? [] : ["create-test-translation"]),
|
||||
gulp.parallel("create-test-metadata", "create-test-translation"),
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const { existsSync } = require("fs");
|
||||
const path = require("path");
|
||||
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 { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const log = require("fancy-log");
|
||||
@@ -152,6 +154,15 @@ const createWebpackConfig = ({
|
||||
)
|
||||
),
|
||||
!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),
|
||||
resolve: {
|
||||
extensions: [".ts", ".js", ".json"],
|
||||
@@ -171,6 +182,8 @@ const createWebpackConfig = ({
|
||||
"@lit-labs/virtualizer/layouts/grid.js",
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver":
|
||||
"@lit-labs/virtualizer/polyfills/resize-observer-polyfill/ResizeObserver.js",
|
||||
"@lit-labs/observers/resize-controller":
|
||||
"@lit-labs/observers/resize-controller.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
@@ -183,6 +196,7 @@ const createWebpackConfig = ({
|
||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
|
||||
assetModuleFilename:
|
||||
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
|
||||
crossOriginLoading: "use-credentials",
|
||||
hashFunction: "xxhash64",
|
||||
hashDigest: "base64url",
|
||||
hashDigestLength: 11, // full length of 64 bit base64url
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import "../../../src/resources/safari-14-attachshadow-patch";
|
||||
import "../../../src/resources/ha-style";
|
||||
import "../../../src/resources/roboto";
|
||||
import "./layout/hc-connect";
|
||||
|
||||
import("../../../src/resources/ha-style");
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mdiCast, mdiCastConnected } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
@@ -22,8 +22,9 @@ import "../../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
|
||||
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
@@ -38,10 +39,10 @@ class HcCast extends LitElement {
|
||||
|
||||
@state() private askWrite = false;
|
||||
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
if (this.lovelaceViews === undefined) {
|
||||
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
||||
}
|
||||
|
||||
@@ -86,9 +87,10 @@ class HcCast extends LitElement {
|
||||
attr-for-selected="data-path"
|
||||
.selected=${this.castManager.status.lovelacePath || ""}
|
||||
>
|
||||
${(this.lovelaceConfig
|
||||
? this.lovelaceConfig.views
|
||||
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
|
||||
${(
|
||||
this.lovelaceViews ?? [
|
||||
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
|
||||
]
|
||||
).map(
|
||||
(view, idx) => html`
|
||||
<paper-icon-item
|
||||
@@ -136,11 +138,15 @@ class HcCast extends LitElement {
|
||||
llColl.refresh().then(
|
||||
() => {
|
||||
llColl.subscribe((config) => {
|
||||
this.lovelaceConfig = config;
|
||||
if (isStrategyDashboard(config)) {
|
||||
this.lovelaceViews = null;
|
||||
} else {
|
||||
this.lovelaceViews = config.views;
|
||||
}
|
||||
});
|
||||
},
|
||||
async () => {
|
||||
this.lovelaceConfig = null;
|
||||
this.lovelaceViews = null;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -159,9 +165,7 @@ class HcCast extends LitElement {
|
||||
toggleAttribute(
|
||||
this,
|
||||
"hide-icons",
|
||||
this.lovelaceConfig
|
||||
? !this.lovelaceConfig.views.some((view) => view.icon)
|
||||
: true
|
||||
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
LovelaceCardConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
@@ -14,7 +14,8 @@ import "./hc-launch-screen";
|
||||
class HcLovelace extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
|
||||
@property({ attribute: false })
|
||||
public lovelaceConfig!: LovelaceConfig;
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
|
||||
|
||||
@@ -21,18 +21,26 @@ import {
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import {
|
||||
fetchResources,
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import {
|
||||
isStrategyDashboard,
|
||||
LegacyLovelaceConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
LovelaceDashboardStrategyConfig,
|
||||
} from "../../../../src/data/lovelace/config/types";
|
||||
import { fetchResources } from "../../../../src/data/lovelace/resource";
|
||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import { castContext } from "../cast_context";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
const DEFAULT_STRATEGY = "original-states";
|
||||
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
|
||||
strategy: {
|
||||
type: "original-states",
|
||||
},
|
||||
};
|
||||
|
||||
let resourcesLoaded = false;
|
||||
@customElement("hc-main")
|
||||
@@ -93,14 +101,16 @@ export class HcMain extends HassElement {
|
||||
.lovelaceConfig=${this._lovelaceConfig}
|
||||
.viewPath=${this._lovelacePath}
|
||||
.urlPath=${this._urlPath}
|
||||
@config-refresh=${this._generateLovelaceConfig}
|
||||
@config-refresh=${this._generateDefaultLovelaceConfig}
|
||||
></hc-lovelace>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
import("../second-load");
|
||||
import("./hc-lovelace");
|
||||
import("../../../../src/resources/ha-style");
|
||||
|
||||
window.addEventListener("location-changed", () => {
|
||||
const panelPath = `/${this._urlPath || "lovelace"}/`;
|
||||
if (location.pathname.startsWith(panelPath)) {
|
||||
@@ -260,7 +270,6 @@ export class HcMain extends HassElement {
|
||||
{
|
||||
strategy: {
|
||||
type: "energy",
|
||||
show_date_selection: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -283,9 +292,20 @@ export class HcMain extends HassElement {
|
||||
// configuration.
|
||||
try {
|
||||
await llColl.refresh();
|
||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||
);
|
||||
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
||||
if (isStrategyDashboard(rawConfig)) {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
const config = await generateLovelaceDashboardStrategy(
|
||||
rawConfig.strategy,
|
||||
this.hass!
|
||||
);
|
||||
this._handleNewLovelaceConfig(config);
|
||||
} else {
|
||||
this._handleNewLovelaceConfig(rawConfig);
|
||||
}
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (
|
||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
||||
@@ -299,7 +319,7 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
// Generate a Lovelace config.
|
||||
this._unsubLovelace = () => undefined;
|
||||
await this._generateLovelaceConfig();
|
||||
await this._generateDefaultLovelaceConfig();
|
||||
}
|
||||
}
|
||||
if (!resourcesLoaded) {
|
||||
@@ -308,24 +328,21 @@ export class HcMain extends HassElement {
|
||||
? await fetchResources(this.hass!.connection)
|
||||
: (this._lovelaceConfig as LegacyLovelaceConfig).resources;
|
||||
if (resources) {
|
||||
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
|
||||
loadLovelaceResources(resources, this.hass!);
|
||||
}
|
||||
}
|
||||
|
||||
this._sendStatus();
|
||||
}
|
||||
|
||||
private async _generateLovelaceConfig() {
|
||||
private async _generateDefaultLovelaceConfig() {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceDashboardStrategy(
|
||||
{
|
||||
type: DEFAULT_STRATEGY,
|
||||
},
|
||||
this.hass!,
|
||||
{ narrow: false }
|
||||
DEFAULT_CONFIG.strategy,
|
||||
this.hass!
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import "../../../src/resources/ha-style";
|
||||
import "../../../src/resources/roboto";
|
||||
import "./layout/hc-lovelace";
|
||||
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
convertEntities({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
supported_features: 15,
|
||||
friendly_name: "Shopping List",
|
||||
icon: "mdi:cart",
|
||||
},
|
||||
},
|
||||
"zone.home": {
|
||||
entity_id: "zone.home",
|
||||
state: "zoning",
|
||||
|
||||
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
supported_features: 15,
|
||||
friendly_name: "Shopping List",
|
||||
icon: "mdi:cart",
|
||||
},
|
||||
},
|
||||
"zone.powertec": {
|
||||
entity_id: "zone.powertec",
|
||||
state: "zoning",
|
||||
|
||||
@@ -4,16 +4,11 @@ export const demoThemeJimpower = () => ({
|
||||
"primary-color": "#5294E2",
|
||||
"label-badge-red": "var(--accent-color)",
|
||||
"paper-tabs-selection-bar-color": "green",
|
||||
"paper-slider-knob-color": "var(--accent-color)",
|
||||
"light-primary-color": "var(--accent-color)",
|
||||
"primary-background-color": "#383C45",
|
||||
"primary-text-color": "#FFFFFF",
|
||||
"paper-item-selected_-_background-color": "#434954",
|
||||
"paper-slider-active-color": "var(--accent-color)",
|
||||
"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",
|
||||
"paper-item-icon_-_color": "green",
|
||||
"paper-grey-200": "#414A59",
|
||||
@@ -32,14 +27,10 @@ export const demoThemeJimpower = () => ({
|
||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||
"label-badge-border-color": "green",
|
||||
"paper-listbox-color": "var(--primary-color)",
|
||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
||||
"card-background-color": "#434954",
|
||||
"label-badge-text-color": "var(--primary-text-color)",
|
||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||
"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",
|
||||
"accent-color": "#E45E65",
|
||||
"table-row-alternative-background-color": "#3E424B",
|
||||
|
||||
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
supported_features: 15,
|
||||
friendly_name: "Shopping List",
|
||||
icon: "mdi:cart",
|
||||
},
|
||||
},
|
||||
"zone.anna": {
|
||||
entity_id: "zone.anna",
|
||||
state: "zoning",
|
||||
|
||||
@@ -5,17 +5,12 @@ export const demoThemeKernehed = () => ({
|
||||
"primary-color": "#2980b9",
|
||||
"label-badge-red": "var(--accent-color)",
|
||||
"paper-tabs-selection-bar-color": "green",
|
||||
"paper-slider-knob-color": "var(--accent-color)",
|
||||
"primary-text-color": "#FFFFFF",
|
||||
"light-primary-color": "var(--accent-color)",
|
||||
"primary-background-color": "#222222",
|
||||
"sidebar-icon-color": "#777777",
|
||||
"paper-item-selected_-_background-color": "#292929",
|
||||
"paper-slider-active-color": "var(--accent-color)",
|
||||
"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",
|
||||
"paper-item-icon_-_color": "green",
|
||||
"paper-grey-200": "#222222",
|
||||
@@ -33,14 +28,10 @@ export const demoThemeKernehed = () => ({
|
||||
"switch-unchecked-button-color": "var(--disabled-text-color)",
|
||||
"label-badge-border-color": "green",
|
||||
"paper-listbox-color": "#777777",
|
||||
"paper-slider-disabled-secondary-color": "var(--disabled-text-color)",
|
||||
"card-background-color": "#292929",
|
||||
"label-badge-text-color": "var(--primary-text-color)",
|
||||
"paper-slider-knob-start-color": "var(--accent-color)",
|
||||
"switch-unchecked-track-color": "var(--disabled-text-color)",
|
||||
"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",
|
||||
"accent-color": "#2980b9",
|
||||
"table-row-alternative-background-color": "#292929",
|
||||
|
||||
@@ -3,6 +3,15 @@ import { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
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": {
|
||||
entity_id: "sensor.pollen_grabo",
|
||||
state: "",
|
||||
|
||||
@@ -220,7 +220,8 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
||||
state_filter: ["on"],
|
||||
},
|
||||
{
|
||||
type: "shopping-list",
|
||||
type: "todo-list",
|
||||
entity: "todo.shopping_list",
|
||||
},
|
||||
{
|
||||
entities: [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export const demoThemeTeachingbirds = () => ({
|
||||
"paper-card-header-color": "var(--paper-item-icon-color)",
|
||||
"paper-slider-pin-color": "var(--primary-color)",
|
||||
"paper-listbox-background-color": "#202020",
|
||||
"paper-grey-50": "var(--primary-text-color)",
|
||||
"paper-item-icon-color": "#d3d3d3",
|
||||
@@ -8,8 +7,6 @@ export const demoThemeTeachingbirds = () => ({
|
||||
"primary-color": "#389638",
|
||||
"light-primary-color": "#6f956f",
|
||||
"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-toggle-button-checked-bar-color": "var(--light-primary-color)",
|
||||
"switch-unchecked-track-color": "var(--primary-text-color)",
|
||||
@@ -17,9 +14,7 @@ export const demoThemeTeachingbirds = () => ({
|
||||
"label-badge-text-color": "var(--text-primary-color)",
|
||||
"primary-background-color": "#303030",
|
||||
"sidebar-icon-color": "var(--paper-item-icon-color)",
|
||||
"paper-slider-active-color": "#d8bf50",
|
||||
"secondary-background-color": "#2b2b2b",
|
||||
"paper-slider-knob-start-color": "var(--primary-color)",
|
||||
"paper-item-icon-active-color": "#d8bf50",
|
||||
"switch-checked-color": "var(--primary-color)",
|
||||
"secondary-text-color": "#389638",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import { LovelaceConfig } from "../../../src/data/lovelace";
|
||||
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||
import { Entity } from "../../../src/fake_data/entity";
|
||||
|
||||
export interface DemoConfig {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
import "./ha-demo";
|
||||
|
||||
import("../../src/resources/ha-style");
|
||||
|
||||
@@ -22,7 +22,7 @@ import { mockLovelace } from "./stubs/lovelace";
|
||||
import { mockMediaPlayer } from "./stubs/media_player";
|
||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||
import { mockRecorder } from "./stubs/recorder";
|
||||
import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockTodo } from "./stubs/todo";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
@@ -49,7 +49,7 @@ export class HaDemo extends HomeAssistantAppEl {
|
||||
mockTranslations(hass);
|
||||
mockHistory(hass);
|
||||
mockRecorder(hass);
|
||||
mockShoppingList(hass);
|
||||
mockTodo(hass);
|
||||
mockSystemLog(hass);
|
||||
mockTemplate(hass);
|
||||
mockEvents(hass);
|
||||
|
||||
@@ -74,19 +74,9 @@
|
||||
<body>
|
||||
<div id="ha-launch-screen">
|
||||
<div class="ha-launch-screen-spacer"></div>
|
||||
<svg
|
||||
viewBox="0 0 240 240"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
|
||||
fill="#F2F4F9"
|
||||
/>
|
||||
<path
|
||||
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
|
||||
fill="#18BCF2"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
|
||||
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
|
||||
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
|
||||
</svg>
|
||||
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
|
||||
</div>
|
||||
|
||||
@@ -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
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[],
|
||||
}));
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "./ha-gallery";
|
||||
|
||||
import("../../src/resources/ha-style");
|
||||
|
||||
document.body.appendChild(document.createElement("ha-gallery"));
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, TemplateResult, nothing } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-assist-chip";
|
||||
import "../../../../src/components/chips/ha-input-chip";
|
||||
import "../../../../src/components/chips/ha-filter-chip";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||
|
||||
@@ -10,10 +12,6 @@ const chips: {
|
||||
icon?: string;
|
||||
content?: string;
|
||||
}[] = [
|
||||
{},
|
||||
{
|
||||
icon: mdiHomeAssistant,
|
||||
},
|
||||
{
|
||||
content: "Content",
|
||||
},
|
||||
@@ -29,31 +27,73 @@ export class DemoHaChips extends LitElement {
|
||||
return html`
|
||||
<ha-card header="ha-chip demo">
|
||||
<div class="card-content">
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</ha-card>
|
||||
<ha-card header="ha-chip-set demo">
|
||||
<div class="card-content">
|
||||
<p>Action chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
<ha-assist-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-assist-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-assist-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-assist-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
<p>Filter chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-filter-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-filter-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
<p>Input chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-input-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-chip>
|
||||
</ha-input-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-input-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-input-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
@@ -68,12 +108,10 @@ export class DemoHaChips extends LitElement {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
ha-chip {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -49,11 +49,11 @@ export class DemoHaCircularSlider extends LitElement {
|
||||
<div class="field">
|
||||
<p>Current</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
min="10"
|
||||
max="30"
|
||||
.value=${this.current}
|
||||
@change=${this._currentChanged}
|
||||
pin
|
||||
></ha-slider>
|
||||
<p>${this.current} °C</p>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ const buttons: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
unit?: string;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
@@ -29,6 +30,11 @@ const buttons: {
|
||||
label: "Custom",
|
||||
class: "custom",
|
||||
},
|
||||
{
|
||||
id: "unit",
|
||||
label: "With unit",
|
||||
unit: "m",
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-components-ha-control-number-buttons")
|
||||
@@ -50,6 +56,7 @@ export class DemoHarControlNumberButtons extends LitElement {
|
||||
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||
<ha-control-number-buttons
|
||||
.value=${this.value}
|
||||
.unit=${config.unit}
|
||||
.min=${config.min}
|
||||
.max=${config.max}
|
||||
.step=${config.step}
|
||||
|
||||
@@ -9,6 +9,7 @@ const sliders: {
|
||||
id: string;
|
||||
label: string;
|
||||
mode?: "start" | "end" | "cursor";
|
||||
unit?: string;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
@@ -31,18 +32,21 @@ const sliders: {
|
||||
label: "Slider (start mode) and custom style",
|
||||
mode: "start",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
{
|
||||
id: "slider-end-custom",
|
||||
label: "Slider (end mode) and custom style",
|
||||
mode: "end",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
{
|
||||
id: "slider-cursor-custom",
|
||||
label: "Slider (cursor mode) and custom style",
|
||||
mode: "cursor",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -93,6 +97,7 @@ export class DemoHaBarSlider extends LitElement {
|
||||
@value-changed=${this.handleValueChanged}
|
||||
@slider-moved=${this.handleSliderMoved}
|
||||
aria-labelledby=${id}
|
||||
.unit=${config.unit}
|
||||
>
|
||||
</ha-control-slider>
|
||||
</div>
|
||||
@@ -114,6 +119,7 @@ export class DemoHaBarSlider extends LitElement {
|
||||
@value-changed=${this.handleValueChanged}
|
||||
@slider-moved=${this.handleSliderMoved}
|
||||
aria-label=${label}
|
||||
.unit=${config.unit}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
|
||||
@@ -5,9 +5,22 @@ subtitle: Dialogs provide important prompts in a user flow.
|
||||
|
||||
# Material Design 3
|
||||
|
||||
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||
|
||||
# Highlighted guidelines
|
||||
# Guidelines
|
||||
|
||||
## Design
|
||||
|
||||
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
|
||||
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
|
||||
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
|
||||
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
|
||||
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
|
||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||
- Destructive actions should be a red warning button.
|
||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||
|
||||
## Content
|
||||
|
||||
@@ -17,14 +30,6 @@ Our dialogs are based on the latest version of Material Design. Specs and guidel
|
||||
- If users become unsure, they read the description. Make sure this explains what will happen.
|
||||
- Strive for minimalism.
|
||||
|
||||
## Buttons and X-icon
|
||||
|
||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||
- Destructive actions should be a red warning button.
|
||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||
|
||||
## Example
|
||||
|
||||
### Confirmation dialog
|
||||
|
||||
@@ -57,6 +57,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: "backyard",
|
||||
@@ -74,6 +75,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: null,
|
||||
@@ -91,6 +93,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -57,8 +57,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
></ha-hs-color-picker>
|
||||
<p>Hue : ${this.value[0]}</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="1"
|
||||
pin
|
||||
min="0"
|
||||
max="360"
|
||||
.value=${this.value[0]}
|
||||
@@ -67,8 +67,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
</ha-slider>
|
||||
<p>Saturation : ${this.value[1]}</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="0.01"
|
||||
pin
|
||||
min="0"
|
||||
max="1"
|
||||
.value=${this.value[1]}
|
||||
@@ -77,8 +77,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
</ha-slider>
|
||||
<p>Color Brighness : ${this.brightness}</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="1"
|
||||
pin
|
||||
min="0"
|
||||
max="255"
|
||||
.value=${this.brightness}
|
||||
|
||||
@@ -53,6 +53,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: "backyard",
|
||||
@@ -70,6 +71,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
},
|
||||
{
|
||||
area_id: null,
|
||||
@@ -87,6 +89,7 @@ const DEVICES = [
|
||||
sw_version: null,
|
||||
hw_version: 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
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 { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
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 = [
|
||||
{
|
||||
heading: "List example",
|
||||
config: `
|
||||
- type: shopping-list
|
||||
- type: todo-list
|
||||
entity: todo.shopping_list
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "List with title example",
|
||||
config: `
|
||||
- type: shopping-list
|
||||
- type: todo-list
|
||||
title: Shopping List
|
||||
entity: todo.read_only
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-lovelace-shopping-list-card")
|
||||
class DemoShoppingListEntity extends LitElement {
|
||||
@customElement("demo-lovelace-todo-list-card")
|
||||
class DemoTodoListEntity extends LitElement {
|
||||
@query("#demos") private _demoRoot!: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -32,18 +46,14 @@ class DemoShoppingListEntity extends LitElement {
|
||||
const hass = provideHass(this._demoRoot);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
|
||||
hass.mockAPI("shopping_list", () => [
|
||||
{ name: "list", id: 1, complete: false },
|
||||
{ name: "all", id: 2, complete: false },
|
||||
{ name: "the", id: 3, complete: false },
|
||||
{ name: "things", id: 4, complete: true },
|
||||
]);
|
||||
mockTodo(hass);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-lovelace-shopping-list-card": DemoShoppingListEntity;
|
||||
"demo-lovelace-todo-list-card": DemoTodoListEntity;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import { computeStateDisplay } from "../../../../src/common/entity/compute_state
|
||||
import "../../../../src/components/data-table/ha-data-table";
|
||||
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||
import "../../../../src/components/entity/state-badge";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
|
||||
@@ -213,6 +213,7 @@ const createDeviceRegistryEntries = (
|
||||
name: "Tag Reader",
|
||||
sw_version: null,
|
||||
hw_version: "1.0.0",
|
||||
serial_number: "00_12_4B_00_22_98_88_7F",
|
||||
id: "mock-device-id",
|
||||
identifiers: [],
|
||||
via_device_id: null,
|
||||
|
||||
@@ -2,7 +2,7 @@ import "@material/mwc-button";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
|
||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
|
||||
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||
|
||||
@customElement("demo-misc-util-long-press")
|
||||
|
||||
@@ -31,8 +31,8 @@ import { navigate } from "../../../../src/common/navigate";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-assist-chip";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
@@ -78,6 +78,7 @@ import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-has
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import "../../update-available/update-available-card";
|
||||
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
|
||||
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
|
||||
|
||||
const STAGE_ICON = {
|
||||
stable: mdiCheckCircle,
|
||||
@@ -234,28 +235,32 @@ class HassioAddonInfo extends LitElement {
|
||||
|
||||
<ha-chip-set class="capabilities">
|
||||
${this.addon.stage !== "stable"
|
||||
? html` <ha-chip
|
||||
hasIcon
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
)
|
||||
)}
|
||||
>
|
||||
</ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
)}
|
||||
</ha-chip>`
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
>
|
||||
</ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
class=${classMap({
|
||||
green: Number(this.addon.rating) >= 6,
|
||||
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
|
||||
@@ -263,138 +268,183 @@ class HassioAddonInfo extends LitElement {
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="rating"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
|
||||
</ha-svg-icon>
|
||||
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
${this.addon.host_network
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_network"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.full_access
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="full_access"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.homeassistant_api
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="homeassistant_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this._computeHassioApi
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="hassio_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.docker_api
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
|
||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="docker_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.host_pid
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
|
||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_pid"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.apparmor !== "default"
|
||||
? html`
|
||||
<ha-chip
|
||||
hasIcon
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
class=${this._computeApparmorClassName}
|
||||
id="apparmor"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auth_api
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
|
||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="auth_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="ingress"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiCursorDefaultClickOutline}
|
||||
></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)}
|
||||
</ha-chip>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.signed
|
||||
? html`
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="signed">
|
||||
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.signed"
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="signed"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.signed"
|
||||
)
|
||||
)}
|
||||
</ha-chip>
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
: ""}
|
||||
</ha-chip-set>
|
||||
@@ -1185,23 +1235,35 @@ class HassioAddonInfo extends LitElement {
|
||||
.description a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-chip {
|
||||
text-transform: capitalize;
|
||||
--ha-chip-text-color: var(--text-primary-color);
|
||||
--ha-chip-background-color: var(--primary-color);
|
||||
ha-assist-chip {
|
||||
--md-sys-color-primary: var(--text-primary-color);
|
||||
--md-sys-color-on-surface: var(--text-primary-color);
|
||||
--ha-assist-chip-filled-container-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.red {
|
||||
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-red,
|
||||
#df4c1e
|
||||
);
|
||||
}
|
||||
.blue {
|
||||
--ha-chip-background-color: var(--label-badge-blue, #039be5);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-blue,
|
||||
#039be5
|
||||
);
|
||||
}
|
||||
.green {
|
||||
--ha-chip-background-color: var(--label-badge-green, #0da035);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-green,
|
||||
#0da035
|
||||
);
|
||||
}
|
||||
.yellow {
|
||||
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-yellow,
|
||||
#f4b400
|
||||
);
|
||||
}
|
||||
.capabilities {
|
||||
margin-bottom: 16px;
|
||||
@@ -1260,9 +1322,6 @@ class HassioAddonInfo extends LitElement {
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
ha-chip {
|
||||
line-height: 36px;
|
||||
}
|
||||
.addon-options {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@@ -360,11 +360,9 @@ export class HassioBackups extends LitElement {
|
||||
if (this.supervisor!.info.state !== "running") {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor!.localize("backup.could_not_create"),
|
||||
text: this.supervisor!.localize(
|
||||
"backup.create_blocked_not_running",
|
||||
"state",
|
||||
this.supervisor!.info.state
|
||||
),
|
||||
text: this.supervisor!.localize("backup.create_blocked_not_running", {
|
||||
state: this.supervisor!.info.state,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import Fuse from "fuse.js";
|
||||
import type { IFuseOptions } from "fuse.js";
|
||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||
|
||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
||||
const options: Fuse.IFuseOptions<StoreAddon> = {
|
||||
const options: IFuseOptions<StoreAddon> = {
|
||||
keys: ["name", "description", "slug"],
|
||||
isCaseSensitive: false,
|
||||
minMatchCharLength: 2,
|
||||
|
||||
@@ -31,6 +31,7 @@ import { fileDownload } from "../../../../src/util/file_download";
|
||||
import "../../components/supervisor-backup-content";
|
||||
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
||||
import { BackupOrRestoreKey } from "../../util/translations";
|
||||
|
||||
@customElement("dialog-hassio-backup")
|
||||
class HassioBackupDialog
|
||||
@@ -64,6 +65,13 @@ class HassioBackupDialog
|
||||
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() {
|
||||
if (!this._dialogParams || !this._backup) {
|
||||
return nothing;
|
||||
@@ -79,7 +87,7 @@ class HassioBackupDialog
|
||||
<ha-header-bar>
|
||||
<span slot="title">${this._backup.name}</span>
|
||||
<ha-icon-button
|
||||
.label=${this.hass?.localize("ui.common.close") || "Close"}
|
||||
.label=${this._localize("close")}
|
||||
.path=${mdiClose}
|
||||
slot="actionItems"
|
||||
dialogAction="cancel"
|
||||
@@ -87,29 +95,31 @@ class HassioBackupDialog
|
||||
</ha-header-bar>
|
||||
</div>
|
||||
${this._restoringBackup
|
||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||
: html`<supervisor-backup-content
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this._dialogParams.supervisor}
|
||||
.backup=${this._backup}
|
||||
.onboarding=${this._dialogParams.onboarding || false}
|
||||
.localize=${this._dialogParams.localize}
|
||||
dialogInitialFocus
|
||||
>
|
||||
</supervisor-backup-content>`}
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
: html`
|
||||
<supervisor-backup-content
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this._dialogParams.supervisor}
|
||||
.backup=${this._backup}
|
||||
.onboarding=${this._dialogParams.onboarding || false}
|
||||
.localize=${this._dialogParams.localize}
|
||||
dialogInitialFocus
|
||||
>
|
||||
</supervisor-backup-content>
|
||||
`}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
: nothing}
|
||||
|
||||
<mwc-button
|
||||
.disabled=${this._restoringBackup}
|
||||
slot="secondaryAction"
|
||||
@click=${this._restoreClicked}
|
||||
>
|
||||
Restore
|
||||
${this._localize("restore")}
|
||||
</mwc-button>
|
||||
|
||||
${!this._dialogParams.onboarding
|
||||
${!this._dialogParams.onboarding && this._dialogParams.supervisor
|
||||
? html`<ha-button-menu
|
||||
fixed
|
||||
slot="primaryAction"
|
||||
@@ -117,22 +127,24 @@ class HassioBackupDialog
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize("ui.common.menu") || "Menu"}
|
||||
.label=${this._dialogParams.supervisor.localize(
|
||||
"backup.more_actions"
|
||||
)}
|
||||
.path=${mdiDotsVertical}
|
||||
slot="trigger"
|
||||
></ha-icon-button>
|
||||
<mwc-list-item
|
||||
>${this._dialogParams.supervisor?.localize(
|
||||
>${this._dialogParams.supervisor.localize(
|
||||
"backup.download_backup"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
<mwc-list-item class="error"
|
||||
>${this._dialogParams.supervisor?.localize(
|
||||
>${this._dialogParams.supervisor.localize(
|
||||
"backup.delete_backup_title"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
</ha-button-menu>`
|
||||
: ""}
|
||||
: nothing}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
@@ -183,21 +195,22 @@ class HassioBackupDialog
|
||||
}
|
||||
|
||||
private async _partialRestoreClicked(backupDetails) {
|
||||
if (
|
||||
this._dialogParams?.supervisor !== undefined &&
|
||||
this._dialogParams?.supervisor.info.state !== "running"
|
||||
) {
|
||||
const supervisor = this._dialogParams?.supervisor;
|
||||
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||
await showAlertDialog(this, {
|
||||
title: "Could not restore backup",
|
||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||
title: supervisor.localize("backup.could_not_restore"),
|
||||
text: supervisor.localize("backup.restore_blocked_not_running", {
|
||||
state: supervisor.info.state,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want to restore this partial backup?",
|
||||
confirmText: "restore",
|
||||
dismissText: "cancel",
|
||||
title: this._localize("confirm_restore_partial_backup_title"),
|
||||
text: this._localize("confirm_restore_partial_backup_text"),
|
||||
confirmText: this._localize("restore"),
|
||||
dismissText: this._localize("cancel"),
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
@@ -230,22 +243,22 @@ class HassioBackupDialog
|
||||
}
|
||||
|
||||
private async _fullRestoreClicked(backupDetails) {
|
||||
if (
|
||||
this._dialogParams?.supervisor !== undefined &&
|
||||
this._dialogParams?.supervisor.info.state !== "running"
|
||||
) {
|
||||
const supervisor = this._dialogParams?.supervisor;
|
||||
if (supervisor !== undefined && supervisor.info.state !== "running") {
|
||||
await showAlertDialog(this, {
|
||||
title: "Could not restore backup",
|
||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||
title: supervisor.localize("backup.could_not_restore"),
|
||||
text: supervisor.localize("backup.restore_blocked_not_running", {
|
||||
state: supervisor.info.state,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title:
|
||||
"Are you sure you want to wipe your system and restore this backup?",
|
||||
confirmText: "restore",
|
||||
dismissText: "cancel",
|
||||
title: this._localize("confirm_restore_full_backup_title"),
|
||||
text: this._localize("confirm_restore_full_backup_text"),
|
||||
confirmText: this._localize("restore"),
|
||||
dismissText: this._localize("cancel"),
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
@@ -279,11 +292,15 @@ class HassioBackupDialog
|
||||
}
|
||||
|
||||
private async _deleteClicked() {
|
||||
const supervisor = this._dialogParams?.supervisor;
|
||||
if (!supervisor) return;
|
||||
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want to delete this backup?",
|
||||
confirmText: "delete",
|
||||
dismissText: "cancel",
|
||||
title: supervisor!.localize("backup.confirm_delete_title"),
|
||||
text: supervisor!.localize("backup.confirm_delete_text"),
|
||||
confirmText: supervisor!.localize("backup.delete"),
|
||||
dismissText: supervisor!.localize("backup.cancel"),
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
@@ -301,6 +318,9 @@ class HassioBackupDialog
|
||||
}
|
||||
|
||||
private async _downloadClicked() {
|
||||
const supervisor = this._dialogParams?.supervisor;
|
||||
if (!supervisor) return;
|
||||
|
||||
let signedPath: { path: string };
|
||||
try {
|
||||
signedPath = await getSignedPath(
|
||||
@@ -320,10 +340,10 @@ class HassioBackupDialog
|
||||
|
||||
if (window.location.href.includes("ui.nabu.casa")) {
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: "Potential slow download",
|
||||
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?",
|
||||
confirmText: "continue",
|
||||
dismissText: "cancel",
|
||||
title: supervisor.localize("backup.remote_download_title"),
|
||||
text: supervisor.localize("backup.remote_download_text"),
|
||||
confirmText: supervisor.localize("backup.download"),
|
||||
dismissText: this._localize("cancel"),
|
||||
});
|
||||
if (!confirm) {
|
||||
return;
|
||||
|
||||
@@ -89,8 +89,7 @@ class HassioCreateBackupDialog extends LitElement {
|
||||
),
|
||||
text: this._dialogParams!.supervisor.localize(
|
||||
"backup.create_blocked_not_running",
|
||||
"state",
|
||||
this._dialogParams!.supervisor.info.state
|
||||
{ state: this._dialogParams!.supervisor.info.state }
|
||||
),
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { dump } from "js-yaml";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -9,7 +10,6 @@ import "../../../../src/components/ha-expansion-panel";
|
||||
import "../../../../src/components/ha-icon-button";
|
||||
import "../../../../src/components/search-input";
|
||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||
import { dump } from "../../../../src/resources/js-yaml-dump";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Compat needs to be first import
|
||||
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 "./hassio-main";
|
||||
|
||||
setCancelSyntheticClickEvents(false);
|
||||
import("../../src/resources/ha-style");
|
||||
import("@polymer/polymer/lib/utils/settings").then(
|
||||
({ setCancelSyntheticClickEvents }) => setCancelSyntheticClickEvents(false)
|
||||
);
|
||||
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.innerHTML = `
|
||||
styleEl.textContent = `
|
||||
body {
|
||||
font-family: Roboto, sans-serif;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
4
hassio/src/util/translations.ts
Normal file
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"];
|
||||
153
package.json
153
package.json
@@ -25,24 +25,24 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.23.1",
|
||||
"@babel/runtime": "7.23.2",
|
||||
"@braintree/sanitize-url": "6.0.4",
|
||||
"@codemirror/autocomplete": "6.9.1",
|
||||
"@codemirror/commands": "6.2.5",
|
||||
"@codemirror/language": "6.9.1",
|
||||
"@codemirror/autocomplete": "6.11.0",
|
||||
"@codemirror/commands": "6.3.0",
|
||||
"@codemirror/language": "6.9.2",
|
||||
"@codemirror/legacy-modes": "6.3.3",
|
||||
"@codemirror/search": "6.5.4",
|
||||
"@codemirror/state": "6.2.1",
|
||||
"@codemirror/view": "6.20.2",
|
||||
"@codemirror/state": "6.3.1",
|
||||
"@codemirror/view": "6.22.0",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "6.10.3",
|
||||
"@formatjs/intl-displaynames": "6.5.2",
|
||||
"@formatjs/intl-getcanonicallocales": "2.2.1",
|
||||
"@formatjs/intl-listformat": "7.4.2",
|
||||
"@formatjs/intl-locale": "3.3.4",
|
||||
"@formatjs/intl-numberformat": "8.7.2",
|
||||
"@formatjs/intl-pluralrules": "5.2.6",
|
||||
"@formatjs/intl-relativetimeformat": "11.2.6",
|
||||
"@formatjs/intl-datetimeformat": "6.11.2",
|
||||
"@formatjs/intl-displaynames": "6.6.2",
|
||||
"@formatjs/intl-getcanonicallocales": "2.3.0",
|
||||
"@formatjs/intl-listformat": "7.5.1",
|
||||
"@formatjs/intl-locale": "3.4.1",
|
||||
"@formatjs/intl-numberformat": "8.8.1",
|
||||
"@formatjs/intl-pluralrules": "5.2.8",
|
||||
"@formatjs/intl-relativetimeformat": "11.2.8",
|
||||
"@fullcalendar/core": "6.1.9",
|
||||
"@fullcalendar/daygrid": "6.1.9",
|
||||
"@fullcalendar/interaction": "6.1.9",
|
||||
@@ -51,11 +51,13 @@
|
||||
"@fullcalendar/timegrid": "6.1.9",
|
||||
"@lezer/highlight": "1.1.6",
|
||||
"@lit-labs/context": "0.4.1",
|
||||
"@lit-labs/motion": "1.0.4",
|
||||
"@lit-labs/virtualizer": "2.0.7",
|
||||
"@lit-labs/motion": "1.0.6",
|
||||
"@lit-labs/observers": "2.0.2",
|
||||
"@lit-labs/virtualizer": "2.0.10",
|
||||
"@lrnwebcomponents/simple-tooltip": "7.0.18",
|
||||
"@material/chips": "=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-checkbox": "0.27.0",
|
||||
"@material/mwc-circular-progress": "0.27.0",
|
||||
@@ -71,7 +73,6 @@
|
||||
"@material/mwc-radio": "0.27.0",
|
||||
"@material/mwc-ripple": "0.27.0",
|
||||
"@material/mwc-select": "0.27.0",
|
||||
"@material/mwc-slider": "0.27.0",
|
||||
"@material/mwc-switch": "0.27.0",
|
||||
"@material/mwc-tab": "0.27.0",
|
||||
"@material/mwc-tab-bar": "0.27.0",
|
||||
@@ -80,22 +81,21 @@
|
||||
"@material/mwc-top-app-bar": "0.27.0",
|
||||
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
||||
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/web": "=1.0.0-pre.17",
|
||||
"@mdi/js": "7.2.96",
|
||||
"@mdi/svg": "7.2.96",
|
||||
"@material/web": "=1.0.1",
|
||||
"@mdi/js": "7.3.67",
|
||||
"@mdi/svg": "7.3.67",
|
||||
"@polymer/iron-flex-layout": "3.0.1",
|
||||
"@polymer/iron-input": "3.0.1",
|
||||
"@polymer/iron-resizable-behavior": "3.0.1",
|
||||
"@polymer/paper-input": "3.2.1",
|
||||
"@polymer/paper-item": "3.0.1",
|
||||
"@polymer/paper-listbox": "3.0.1",
|
||||
"@polymer/paper-slider": "3.0.1",
|
||||
"@polymer/paper-tabs": "3.1.0",
|
||||
"@polymer/paper-toast": "3.0.1",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@vaadin/combo-box": "24.1.9",
|
||||
"@vaadin/vaadin-themable-mixin": "24.1.9",
|
||||
"@vaadin/combo-box": "24.2.2",
|
||||
"@vaadin/vaadin-themable-mixin": "24.2.2",
|
||||
"@vibrant/color": "3.2.1-alpha.1",
|
||||
"@vibrant/core": "3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||
@@ -105,28 +105,28 @@
|
||||
"app-datepicker": "5.1.1",
|
||||
"chart.js": "4.4.0",
|
||||
"comlink": "4.4.1",
|
||||
"core-js": "3.32.2",
|
||||
"core-js": "3.33.2",
|
||||
"cropperjs": "1.6.1",
|
||||
"date-fns": "2.30.0",
|
||||
"date-fns-tz": "2.0.0",
|
||||
"deep-clone-simple": "1.1.1",
|
||||
"deep-freeze": "0.0.1",
|
||||
"fuse.js": "6.6.2",
|
||||
"fuse.js": "7.0.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"hls.js": "1.4.12",
|
||||
"home-assistant-js-websocket": "8.2.0",
|
||||
"home-assistant-js-websocket": "9.1.0",
|
||||
"idb-keyval": "6.2.1",
|
||||
"intl-messageformat": "10.5.3",
|
||||
"intl-messageformat": "10.5.5",
|
||||
"js-yaml": "4.1.0",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "1.0.4",
|
||||
"lit": "2.8.0",
|
||||
"luxon": "3.4.3",
|
||||
"marked": "9.0.3",
|
||||
"marked": "9.1.6",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "0.3.2",
|
||||
"punycode": "2.3.0",
|
||||
"punycode": "2.3.1",
|
||||
"qr-scanner": "1.4.2",
|
||||
"qrcode": "1.5.3",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
@@ -138,11 +138,11 @@
|
||||
"tinykeys": "2.1.0",
|
||||
"tsparticles-engine": "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",
|
||||
"vis-data": "7.1.7",
|
||||
"vis-network": "9.1.6",
|
||||
"vue": "2.7.14",
|
||||
"vis-data": "7.1.8",
|
||||
"vis-network": "9.1.9",
|
||||
"vue": "2.7.15",
|
||||
"vue2-daterange-picker": "0.6.8",
|
||||
"weekstart": "2.0.0",
|
||||
"workbox-cacheable-response": "7.0.0",
|
||||
@@ -154,62 +154,61 @@
|
||||
"xss": "1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.23.0",
|
||||
"@babel/plugin-proposal-decorators": "7.23.0",
|
||||
"@babel/plugin-transform-runtime": "7.22.15",
|
||||
"@babel/preset-env": "7.22.20",
|
||||
"@babel/preset-typescript": "7.23.0",
|
||||
"@babel/core": "7.23.3",
|
||||
"@babel/plugin-proposal-decorators": "7.23.3",
|
||||
"@babel/plugin-transform-runtime": "7.23.3",
|
||||
"@babel/preset-env": "7.23.3",
|
||||
"@babel/preset-typescript": "7.23.3",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.8.0",
|
||||
"@koa/cors": "4.0.0",
|
||||
"@lokalise/node-api": "12.0.0",
|
||||
"@octokit/auth-oauth-device": "6.0.1",
|
||||
"@octokit/plugin-retry": "6.0.1",
|
||||
"@octokit/rest": "20.0.2",
|
||||
"@open-wc/dev-server-hmr": "0.1.4",
|
||||
"@rollup/plugin-babel": "6.0.3",
|
||||
"@rollup/plugin-commonjs": "25.0.4",
|
||||
"@rollup/plugin-json": "6.0.0",
|
||||
"@rollup/plugin-node-resolve": "15.2.1",
|
||||
"@rollup/plugin-replace": "5.0.2",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.3",
|
||||
"@types/chromecast-caf-receiver": "6.0.10",
|
||||
"@types/chromecast-caf-sender": "1.0.6",
|
||||
"@types/esprima": "4.0.4",
|
||||
"@rollup/plugin-babel": "6.0.4",
|
||||
"@rollup/plugin-commonjs": "25.0.7",
|
||||
"@rollup/plugin-json": "6.0.1",
|
||||
"@rollup/plugin-node-resolve": "15.2.3",
|
||||
"@rollup/plugin-replace": "5.0.5",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.12",
|
||||
"@types/chromecast-caf-sender": "1.0.8",
|
||||
"@types/glob": "8.1.0",
|
||||
"@types/html-minifier-terser": "7.0.0",
|
||||
"@types/js-yaml": "4.0.6",
|
||||
"@types/leaflet": "1.9.6",
|
||||
"@types/leaflet-draw": "1.0.8",
|
||||
"@types/luxon": "3.3.2",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/qrcode": "1.5.2",
|
||||
"@types/serve-handler": "6.1.2",
|
||||
"@types/sortablejs": "1.15.3",
|
||||
"@types/tar": "6.1.6",
|
||||
"@types/ua-parser-js": "0.7.37",
|
||||
"@types/html-minifier-terser": "7.0.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/leaflet": "1.9.8",
|
||||
"@types/leaflet-draw": "1.0.10",
|
||||
"@types/luxon": "3.3.4",
|
||||
"@types/mocha": "10.0.4",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/serve-handler": "6.1.4",
|
||||
"@types/sortablejs": "1.15.5",
|
||||
"@types/tar": "6.1.9",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.3",
|
||||
"@typescript-eslint/parser": "6.7.3",
|
||||
"@typescript-eslint/eslint-plugin": "6.10.0",
|
||||
"@typescript-eslint/parser": "6.10.0",
|
||||
"@web/dev-server": "0.1.38",
|
||||
"@web/dev-server-rollup": "0.4.1",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"chai": "4.3.8",
|
||||
"chai": "4.3.10",
|
||||
"del": "7.1.0",
|
||||
"eslint": "8.50.0",
|
||||
"eslint": "8.53.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "17.1.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-import": "2.28.1",
|
||||
"eslint-plugin-lit": "1.9.1",
|
||||
"eslint-plugin-lit-a11y": "4.1.0",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-lit": "1.10.1",
|
||||
"eslint-plugin-lit-a11y": "4.1.1",
|
||||
"eslint-plugin-unused-imports": "3.0.0",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"esprima": "4.0.1",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"glob": "10.3.7",
|
||||
"glob": "10.3.10",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-flatmap": "1.0.2",
|
||||
"gulp-json-transform": "0.4.8",
|
||||
@@ -220,10 +219,10 @@
|
||||
"husky": "8.0.3",
|
||||
"instant-mocha": "1.5.2",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "14.0.1",
|
||||
"lit-analyzer": "2.0.0-pre.3",
|
||||
"lint-staged": "15.1.0",
|
||||
"lit-analyzer": "2.0.1",
|
||||
"lodash.template": "4.5.0",
|
||||
"magic-string": "0.30.3",
|
||||
"magic-string": "0.30.5",
|
||||
"map-stream": "0.0.7",
|
||||
"mocha": "10.2.0",
|
||||
"object-hash": "3.0.0",
|
||||
@@ -235,19 +234,20 @@
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rollup-plugin-visualizer": "5.9.2",
|
||||
"serve-handler": "6.1.5",
|
||||
"sinon": "16.0.0",
|
||||
"sinon": "17.0.1",
|
||||
"source-map-url": "0.4.1",
|
||||
"systemjs": "6.14.2",
|
||||
"tar": "6.2.0",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-lit-plugin": "2.0.0-pre.1",
|
||||
"ts-lit-plugin": "2.0.1",
|
||||
"typescript": "5.2.2",
|
||||
"vinyl-buffer": "1.0.1",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
"webpack": "5.88.2",
|
||||
"webpack": "5.89.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1",
|
||||
"webpack-manifest-plugin": "5.0.0",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "5.0.2",
|
||||
"workbox-build": "7.0.0"
|
||||
},
|
||||
@@ -255,8 +255,11 @@
|
||||
"resolutions": {
|
||||
"@polymer/polymer": "patch:@polymer/polymer@3.5.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
||||
"@material/mwc-button@^0.25.3": "^0.27.0",
|
||||
"lit": "2.8.0",
|
||||
"clean-css": "5.3.2",
|
||||
"@lit/reactive-element": "1.6.3",
|
||||
"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"
|
||||
},
|
||||
"packageManager": "yarn@3.6.3"
|
||||
"packageManager": "yarn@4.0.1"
|
||||
}
|
||||
|
||||
@@ -1,23 +1 @@
|
||||
<?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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="640" viewBox="0 0 240 240"><path d="M120.001 0c-3.787 0-7.573 1.499-10.444 4.49L12.211 105.905a25.921 25.921 0 0 0-2.098 2.501 35.25 35.25 0 0 0-1.96 2.942c-3.01 5.021-5.285 11.318-6.074 16.898-.03.21-.088.429-.11.636a27.355 27.355 0 0 0-.213 3.317v93.023a14.78 14.78 90 0 0 14.78 14.78h90.92L67.422 198.29a20.2 20.2 90 1 1 12.542-13.06l31.17 32.474V98.726a20.2 20.2 90 1 1 17.734 0v83.44l31.001-32.299a20.2 20.2 90 1 1 12.267 13.357l-43.269 45.082V240h94.9a14.479 14.479 90 0 0 14.478-14.479v-93.314c0-1.059-.069-2.168-.214-3.314-.7-5.73-3.06-12.327-6.183-17.537a35.801 35.801 0 0 0-1.955-2.937 26.271 26.271 0 0 0-2.102-2.506L130.444 4.486C127.573 1.494 123.786-.002 120.001 0"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 747 B |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20230928.0"
|
||||
version = "20231030.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"configMigration": true,
|
||||
"extends": [
|
||||
":ignoreModulesAndTests",
|
||||
":label(Dependencies)",
|
||||
@@ -10,7 +11,7 @@
|
||||
"group:recommended",
|
||||
"npm:unpublishSafe"
|
||||
],
|
||||
"enabledManagers": ["npm"],
|
||||
"enabledManagers": ["npm", "nvm"],
|
||||
"postUpdateOptions": ["yarnDedupeHighest"],
|
||||
"lockFileMaintenance": {
|
||||
"description": ["Run after patch releases but before next beta"],
|
||||
@@ -28,11 +29,22 @@
|
||||
"matchPackageNames": ["vue"],
|
||||
"allowedVersions": "< 3"
|
||||
},
|
||||
{
|
||||
"description": "Group MDI packages",
|
||||
"groupName": "Material Design Icons",
|
||||
"matchPackageNames": ["@mdi/js", "@mdi/svg"]
|
||||
},
|
||||
{
|
||||
"description": "Group tsparticles engine and presets",
|
||||
"groupName": "tsparticles",
|
||||
"matchPackageNames": ["tsparticles-engine"],
|
||||
"matchPackagePrefixes": ["tsparticles-preset-"]
|
||||
},
|
||||
{
|
||||
"description": "Group and temporarily disable WDS packages",
|
||||
"groupName": "Web Dev Server",
|
||||
"matchPackagePrefixes": ["@web/dev-server"],
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ fi
|
||||
|
||||
docker run \
|
||||
-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} \
|
||||
--project-id ${PROJECT_ID} \
|
||||
file upload \
|
||||
|
||||
@@ -8,7 +8,6 @@ import "../components/ha-alert";
|
||||
import "../components/ha-checkbox";
|
||||
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
||||
import "../components/ha-formfield";
|
||||
import "../components/ha-markdown";
|
||||
import { AuthProvider, autocompleteLoginFields } from "../data/auth";
|
||||
import {
|
||||
DataEntryFlowStep,
|
||||
@@ -182,24 +181,13 @@ export class HaAuthFlow extends LitElement {
|
||||
case "abort":
|
||||
return html`
|
||||
${this.localize("ui.panel.page-authorize.abort_intro")}:
|
||||
<ha-markdown
|
||||
allowsvg
|
||||
breaks
|
||||
.content=${this.localize(
|
||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
|
||||
)}
|
||||
></ha-markdown>
|
||||
${this.localize(
|
||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
|
||||
)}
|
||||
`;
|
||||
case "form":
|
||||
return html`
|
||||
${this._computeStepDescription(step)
|
||||
? html`
|
||||
<ha-markdown
|
||||
breaks
|
||||
.content=${this._computeStepDescription(step)}
|
||||
></ha-markdown>
|
||||
`
|
||||
: nothing}
|
||||
${this._computeStepDescription(step)}
|
||||
<ha-auth-form
|
||||
.data=${this._stepData}
|
||||
.schema=${autocompleteLoginFields(step.data_schema)}
|
||||
@@ -314,13 +302,7 @@ export class HaAuthFlow extends LitElement {
|
||||
private _computeStepDescription(step: DataEntryFlowStepForm) {
|
||||
const resourceKey =
|
||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description` as const;
|
||||
const args: string[] = [];
|
||||
const placeholders = step.description_placeholders || {};
|
||||
Object.keys(placeholders).forEach((key) => {
|
||||
args.push(key);
|
||||
args.push(placeholders[key]);
|
||||
});
|
||||
return this.localize(resourceKey, ...args);
|
||||
return this.localize(resourceKey, step.description_placeholders);
|
||||
}
|
||||
|
||||
private _computeLabelCallback(step: DataEntryFlowStepForm) {
|
||||
|
||||
@@ -26,14 +26,13 @@ export class HaAuthFormString extends HaFormString {
|
||||
}
|
||||
ha-auth-form-string ha-icon-button {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 12px;
|
||||
--mdc-icon-button-size: 24px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-auth-form-string ha-icon-button {
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 12px;
|
||||
inset-inline-end: 8px;
|
||||
--mdc-icon-button-size: 40px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
direction: var(--direction);
|
||||
}
|
||||
</style>
|
||||
@@ -63,7 +62,7 @@ export class HaAuthFormString extends HaFormString {
|
||||
.validationMessage=${this.schema.required ? "Required" : undefined}
|
||||
@input=${this._valueChanged}
|
||||
@change=${this._valueChanged}
|
||||
></ha-auth-textfield>
|
||||
></ha-auth-textfield>
|
||||
${this.renderIcon()}
|
||||
</ha-auth-textfield>
|
||||
`;
|
||||
|
||||
9
src/common/array/combinations.ts
Normal file
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])
|
||||
),
|
||||
[[]]
|
||||
);
|
||||
}
|
||||
@@ -58,10 +58,10 @@ const matchMaxScale = (
|
||||
return outputColors.map((value) => Math.round(value * factor));
|
||||
};
|
||||
|
||||
const mired2kelvin = (miredTemperature: number) =>
|
||||
export const mired2kelvin = (miredTemperature: number) =>
|
||||
Math.floor(1000000 / miredTemperature);
|
||||
|
||||
const kelvin2mired = (kelvintTemperature: number) =>
|
||||
export const kelvin2mired = (kelvintTemperature: number) =>
|
||||
Math.floor(1000000 / kelvintTemperature);
|
||||
|
||||
export const rgbww2rgb = (
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
mdiCarCoolantLevel,
|
||||
mdiCash,
|
||||
mdiChatSleep,
|
||||
mdiClipboardList,
|
||||
mdiClock,
|
||||
mdiCloudUpload,
|
||||
mdiCog,
|
||||
@@ -120,6 +121,7 @@ export const FIXED_DOMAIN_ICONS = {
|
||||
siren: mdiBullhorn,
|
||||
stt: mdiMicrophoneMessage,
|
||||
text: mdiFormTextbox,
|
||||
todo: mdiClipboardList,
|
||||
time: mdiClock,
|
||||
timer: mdiTimerOutline,
|
||||
tts: mdiSpeakerMessage,
|
||||
|
||||
@@ -5,12 +5,15 @@ import { FrontendLocaleData, TimeZone } from "../../data/translation";
|
||||
const calcZonedDate = (
|
||||
date: Date,
|
||||
tz: string,
|
||||
fn: (date: Date, options?: any) => Date,
|
||||
fn: (date: Date, options?: any) => Date | number | boolean,
|
||||
options?
|
||||
) => {
|
||||
const inputZoned = utcToZonedTime(date, tz);
|
||||
const fnZoned = fn(inputZoned, options);
|
||||
return zonedTimeToUtc(fnZoned, tz);
|
||||
if (fnZoned instanceof Date) {
|
||||
return zonedTimeToUtc(fnZoned, tz) as Date;
|
||||
}
|
||||
return fnZoned;
|
||||
};
|
||||
|
||||
export const calcDate = (
|
||||
@@ -21,5 +24,16 @@ export const calcDate = (
|
||||
options?
|
||||
) =>
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
export const formatDateNumeric = (
|
||||
dateObj: Date,
|
||||
@@ -102,13 +119,13 @@ const formatDateNumericMem = memoizeOne(
|
||||
);
|
||||
|
||||
// Aug 10
|
||||
export const formatDateShort = (
|
||||
export const formatDateVeryShort = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
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) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
day: "numeric",
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
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);
|
||||
|
||||
export const formatDuration = (duration: HaDurationData) => {
|
||||
export const formatDuration = (
|
||||
locale: FrontendLocaleData,
|
||||
duration: HaDurationData
|
||||
) => {
|
||||
const d = duration.days || 0;
|
||||
const h = duration.hours || 0;
|
||||
const m = duration.minutes || 0;
|
||||
@@ -10,7 +15,11 @@ export const formatDuration = (duration: HaDurationData) => {
|
||||
const ms = duration.milliseconds || 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) {
|
||||
return `${h}:${leftPad(m)}:${leftPad(s)}`;
|
||||
@@ -19,10 +28,18 @@ export const formatDuration = (duration: HaDurationData) => {
|
||||
return `${m}:${leftPad(s)}`;
|
||||
}
|
||||
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) {
|
||||
return `${ms} millisecond${ms === 1 ? "" : "s"}`;
|
||||
return Intl.NumberFormat(locale.language, {
|
||||
style: "unit",
|
||||
unit: "millisecond",
|
||||
unitDisplay: "long",
|
||||
}).format(ms);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ThemeVars } from "../../data/ws-themes";
|
||||
import { darkStyles, derivedStyles } from "../../resources/styles";
|
||||
import { darkStyles, derivedStyles } from "../../resources/styles-data";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import {
|
||||
hex2rgb,
|
||||
@@ -41,9 +41,7 @@ export const applyThemesOnElement = (
|
||||
// If there is no explicitly desired dark mode provided, we automatically
|
||||
// use the active one from `themes`.
|
||||
const darkMode =
|
||||
themeSettings && themeSettings?.dark !== undefined
|
||||
? themeSettings?.dark
|
||||
: themes.darkMode;
|
||||
themeSettings?.dark !== undefined ? themeSettings.dark : themes.darkMode;
|
||||
|
||||
let cacheKey = themeToApply;
|
||||
let themeRules: Partial<ThemeVars> = {};
|
||||
@@ -135,10 +133,19 @@ export const applyThemesOnElement = (
|
||||
|
||||
// Set and/or reset styles
|
||||
if (element.updateStyles) {
|
||||
// Use updateStyles() method of Polymer elements
|
||||
element.updateStyles(styles);
|
||||
} else if (window.ShadyCSS) {
|
||||
// Implement updateStyles() method of Polymer elements
|
||||
// Use ShadyCSS if available
|
||||
window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */ element, styles);
|
||||
} else {
|
||||
for (const s in styles) {
|
||||
if (s === null) {
|
||||
element.style.removeProperty(s);
|
||||
} else {
|
||||
element.style.setProperty(s, styles[s]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { blankBeforePercent } from "../translations/blank_before_percent";
|
||||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { computeStateDomain } from "./compute_state_domain";
|
||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||
|
||||
export const computeAttributeValueDisplay = (
|
||||
localize: LocalizeFunc,
|
||||
@@ -55,20 +56,12 @@ export const computeAttributeValueDisplay = (
|
||||
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
|
||||
}
|
||||
|
||||
if (unit === "%") {
|
||||
return `${formattedValue}${blankBeforePercent(locale)}${unit}`;
|
||||
}
|
||||
|
||||
if (unit === "°") {
|
||||
return `${formattedValue}${unit}`;
|
||||
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
|
||||
unit = config.unit_system.temperature;
|
||||
}
|
||||
|
||||
if (unit) {
|
||||
return `${formattedValue} ${unit}`;
|
||||
}
|
||||
|
||||
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
|
||||
return `${formattedValue} ${config.unit_system.temperature}`;
|
||||
return `${formattedValue}${blankBeforeUnit(unit, locale)}${unit}`;
|
||||
}
|
||||
|
||||
return formattedValue;
|
||||
|
||||
@@ -3,13 +3,13 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
|
||||
import { FrontendLocaleData, TimeZone } from "../../data/translation";
|
||||
import {
|
||||
updateIsInstallingFromAttributes,
|
||||
UPDATE_SUPPORT_PROGRESS,
|
||||
updateIsInstallingFromAttributes,
|
||||
} from "../../data/update";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import {
|
||||
formatDuration,
|
||||
UNIT_TO_MILLISECOND_CONVERT,
|
||||
formatDuration,
|
||||
} from "../datetime/duration";
|
||||
import { formatDate } from "../datetime/format_date";
|
||||
import { formatDateTime } from "../datetime/format_date_time";
|
||||
@@ -19,10 +19,10 @@ import {
|
||||
getNumberFormatOptions,
|
||||
isNumericFromAttributes,
|
||||
} from "../number/format_number";
|
||||
import { blankBeforePercent } from "../translations/blank_before_percent";
|
||||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { supportsFeatureFromAttributes } from "./supports-feature";
|
||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||
|
||||
export const computeStateDisplaySingleEntity = (
|
||||
localize: LocalizeFunc,
|
||||
@@ -108,16 +108,20 @@ export const computeStateDisplayFromEntityAttributes = (
|
||||
// fallback to default
|
||||
}
|
||||
}
|
||||
const unit = !attributes.unit_of_measurement
|
||||
? ""
|
||||
: attributes.unit_of_measurement === "%"
|
||||
? blankBeforePercent(locale) + "%"
|
||||
: ` ${attributes.unit_of_measurement}`;
|
||||
return `${formatNumber(
|
||||
|
||||
const value = formatNumber(
|
||||
state,
|
||||
locale,
|
||||
getNumberFormatOptions({ state, attributes } as HassEntity, entity)
|
||||
)}${unit}`;
|
||||
);
|
||||
|
||||
const unit = attributes.unit_of_measurement;
|
||||
|
||||
if (unit) {
|
||||
return `${value}${blankBeforeUnit(unit)}${unit}`;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
const domain = computeDomain(entityId);
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
// https://gist.github.com/hagemann/382adfc57adbd5af078dc93feef01fe1
|
||||
export const slugify = (value: string, delimiter = "_") => {
|
||||
const a =
|
||||
"àáäâãåăæąçćčđďèéěėëêęğǵḧìíïîįłḿǹńňñòóöôœøṕŕřßşśšșťțùúüûǘůűūųẃẍÿýźžż·/_,:;";
|
||||
const b = `aaaaaaaaacccddeeeeeeegghiiiiilmnnnnooooooprrsssssttuuuuuuuuuwxyyzzz${delimiter}${delimiter}${delimiter}${delimiter}${delimiter}${delimiter}`;
|
||||
"àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìıİłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·";
|
||||
const b = `aaaaaaaaaacccddeeeeeeeegghiiiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz${delimiter}`;
|
||||
const p = new RegExp(a.split("").join("|"), "g");
|
||||
|
||||
return value
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, delimiter) // Replace spaces with delimiter
|
||||
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
.replace(/&/g, `${delimiter}and${delimiter}`) // Replace & with 'and'
|
||||
.replace(/[^\w-]+/g, "") // Remove all non-word characters
|
||||
.replace(/-/g, delimiter) // Replace - with delimiter
|
||||
.replace(new RegExp(`(${delimiter})\\1+`, "g"), "$1") // Replace multiple delimiters with single delimiter
|
||||
.replace(new RegExp(`^${delimiter}+`), "") // Trim delimiter from start of text
|
||||
.replace(new RegExp(`${delimiter}+$`), ""); // Trim delimiter from end of text
|
||||
let slugified;
|
||||
|
||||
if (value === "") {
|
||||
slugified = "";
|
||||
} else {
|
||||
slugified = value
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
.replace(/(\d),(?=\d)/g, "$1") // Remove Commas between numbers
|
||||
.replace(/[^a-z0-9]+/g, delimiter) // Replace all non-word characters
|
||||
.replace(new RegExp(`(${delimiter})\\1+`, "g"), "$1") // Replace multiple delimiters with single delimiter
|
||||
.replace(new RegExp(`^${delimiter}+`), "") // Trim delimiter from start of text
|
||||
.replace(new RegExp(`${delimiter}+$`), ""); // Trim delimiter from end of text
|
||||
|
||||
if (slugified === "") {
|
||||
slugified = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
return slugified;
|
||||
};
|
||||
|
||||
@@ -13,45 +13,33 @@ export const handleStructError = (
|
||||
for (const failure of err.failures()) {
|
||||
if (failure.value === undefined) {
|
||||
errors.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_missing",
|
||||
"key",
|
||||
failure.path.join(".")
|
||||
)
|
||||
hass.localize("ui.errors.config.key_missing", {
|
||||
key: failure.path.join("."),
|
||||
})
|
||||
);
|
||||
} else if (failure.type === "never") {
|
||||
warnings.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_not_expected",
|
||||
"key",
|
||||
failure.path.join(".")
|
||||
)
|
||||
hass.localize("ui.errors.config.key_not_expected", {
|
||||
key: failure.path.join("."),
|
||||
})
|
||||
);
|
||||
} else if (failure.type === "union") {
|
||||
continue;
|
||||
} else if (failure.type === "enums") {
|
||||
warnings.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_wrong_type",
|
||||
"key",
|
||||
failure.path.join("."),
|
||||
"type_correct",
|
||||
failure.message.replace("Expected ", "").split(", ")[0],
|
||||
"type_wrong",
|
||||
JSON.stringify(failure.value)
|
||||
)
|
||||
hass.localize("ui.errors.config.key_wrong_type", {
|
||||
key: failure.path.join("."),
|
||||
type_correct: failure.message.replace("Expected ", "").split(", ")[0],
|
||||
type_wrong: JSON.stringify(failure.value),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
warnings.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_wrong_type",
|
||||
"key",
|
||||
failure.path.join("."),
|
||||
"type_correct",
|
||||
failure.refinement || failure.type,
|
||||
"type_wrong",
|
||||
JSON.stringify(failure.value)
|
||||
)
|
||||
hass.localize("ui.errors.config.key_wrong_type", {
|
||||
key: failure.path.join("."),
|
||||
type_correct: failure.refinement || failure.type,
|
||||
type_wrong: JSON.stringify(failure.value),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
15
src/common/translations/blank_before_unit.ts
Normal file
15
src/common/translations/blank_before_unit.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { blankBeforePercent } from "./blank_before_percent";
|
||||
|
||||
export const blankBeforeUnit = (
|
||||
unit: string,
|
||||
localeOptions?: FrontendLocaleData
|
||||
): string => {
|
||||
if (unit === "°") {
|
||||
return "";
|
||||
}
|
||||
if (localeOptions && unit === "%") {
|
||||
return blankBeforePercent(localeOptions);
|
||||
}
|
||||
return " ";
|
||||
};
|
||||
@@ -23,6 +23,7 @@ export function computeDirectionStyles(isRTL: boolean, element: LitElement) {
|
||||
}
|
||||
|
||||
export function setDirectionStyles(direction: string, element: LitElement) {
|
||||
document.dir = direction;
|
||||
element.style.direction = direction;
|
||||
element.style.setProperty("--direction", direction);
|
||||
element.style.setProperty(
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import "@material/mwc-button";
|
||||
import { mdiAlertOctagram, mdiCheckBold } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../ha-circular-progress";
|
||||
import "../ha-svg-icon";
|
||||
@@ -27,7 +34,7 @@ export class HaProgressButton extends LitElement {
|
||||
<slot></slot>
|
||||
</mwc-button>
|
||||
${!overlay
|
||||
? ""
|
||||
? nothing
|
||||
: html`
|
||||
<div class="progress">
|
||||
${this._result === "success"
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
formatDate,
|
||||
formatDateMonth,
|
||||
formatDateMonthYear,
|
||||
formatDateShort,
|
||||
formatDateVeryShort,
|
||||
formatDateWeekdayDay,
|
||||
formatDateYear,
|
||||
} from "../../common/datetime/format_date";
|
||||
@@ -128,13 +128,13 @@ _adapters._date.override({
|
||||
this.options.config
|
||||
);
|
||||
case "day":
|
||||
return formatDateShort(
|
||||
return formatDateVeryShort(
|
||||
new Date(time),
|
||||
this.options.locale,
|
||||
this.options.config
|
||||
);
|
||||
case "week":
|
||||
return formatDate(
|
||||
return formatDateVeryShort(
|
||||
new Date(time),
|
||||
this.options.locale,
|
||||
this.options.config
|
||||
|
||||
@@ -12,6 +12,7 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import { clamp } from "../../common/number/clamp";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
|
||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
||||
|
||||
@@ -52,6 +53,12 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
@state() private _hiddenDatasets: Set<number> = new Set();
|
||||
|
||||
private _paddingUpdateCount = 0;
|
||||
|
||||
private _paddingUpdateLock = false;
|
||||
|
||||
private _paddingYAxisInternal = 0;
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._releaseCanvas();
|
||||
@@ -104,9 +111,44 @@ export class HaChartBase extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
public shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (
|
||||
this._paddingUpdateLock &&
|
||||
changedProps.size === 1 &&
|
||||
changedProps.has("paddingYAxis")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _debouncedClearUpdates = debounce(
|
||||
() => {
|
||||
this._paddingUpdateCount = 0;
|
||||
},
|
||||
2000,
|
||||
false
|
||||
);
|
||||
|
||||
public willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (!this._paddingUpdateLock) {
|
||||
this._paddingYAxisInternal = this.paddingYAxis;
|
||||
if (changedProps.size === 1 && changedProps.has("paddingYAxis")) {
|
||||
this._paddingUpdateCount++;
|
||||
if (this._paddingUpdateCount > 300) {
|
||||
this._paddingUpdateLock = true;
|
||||
// eslint-disable-next-line
|
||||
console.error(
|
||||
"Detected excessive chart padding updates, possibly an infinite loop. Disabling axis padding."
|
||||
);
|
||||
} else {
|
||||
this._debouncedClearUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.hasUpdated || !this.chart) {
|
||||
return;
|
||||
}
|
||||
@@ -171,10 +213,10 @@ export class HaChartBase extends LitElement {
|
||||
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
||||
}px`,
|
||||
"padding-left": `${
|
||||
computeRTL(this.hass) ? 0 : this.paddingYAxis
|
||||
computeRTL(this.hass) ? 0 : this._paddingYAxisInternal
|
||||
}px`,
|
||||
"padding-right": `${
|
||||
computeRTL(this.hass) ? this.paddingYAxis : 0
|
||||
computeRTL(this.hass) ? this._paddingYAxisInternal : 0
|
||||
}px`,
|
||||
})}
|
||||
>
|
||||
@@ -324,7 +366,7 @@ export class HaChartBase extends LitElement {
|
||||
clamp(
|
||||
context.tooltip.caretX,
|
||||
100,
|
||||
this.clientWidth - 100 - this.paddingYAxis
|
||||
this.clientWidth - 100 - this._paddingYAxisInternal
|
||||
) -
|
||||
100 +
|
||||
"px",
|
||||
|
||||
@@ -141,16 +141,10 @@ export class StateHistoryChartLine extends LitElement {
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
this.hass.locale,
|
||||
this.data[context.datasetIndex]?.entity_id
|
||||
? getNumberFormatOptions(
|
||||
this.hass.states[
|
||||
this.data[context.datasetIndex].entity_id
|
||||
],
|
||||
this.hass.entities[
|
||||
this.data[context.datasetIndex].entity_id
|
||||
]
|
||||
)
|
||||
: undefined
|
||||
getNumberFormatOptions(
|
||||
undefined,
|
||||
this.hass.entities[this._entityIds[context.datasetIndex]]
|
||||
)
|
||||
)} ${this.unit}`,
|
||||
},
|
||||
},
|
||||
@@ -180,7 +174,9 @@ export class StateHistoryChartLine extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const points = e.chart.getElementsAtEventForMode(
|
||||
const chart = e.chart;
|
||||
|
||||
const points = chart.getElementsAtEventForMode(
|
||||
e,
|
||||
"nearest",
|
||||
{ intersect: true },
|
||||
@@ -192,6 +188,7 @@ export class StateHistoryChartLine extends LitElement {
|
||||
fireEvent(this, "hass-more-info", {
|
||||
entityId: this._entityIds[firstPoint.datasetIndex],
|
||||
});
|
||||
chart.canvas.dispatchEvent(new Event("mouseout")); // to hide tooltip
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -238,6 +238,7 @@ export class StateHistoryChartTimeline extends LitElement {
|
||||
// @ts-ignore
|
||||
entityId: this._chartData?.datasets[index]?.label,
|
||||
});
|
||||
chart.canvas.dispatchEvent(new Event("mouseout")); // to hide tooltip
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ export class StateHistoryCharts extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public isLoadingData = false;
|
||||
|
||||
@state() private _computedStartTime!: Date;
|
||||
private _computedStartTime!: Date;
|
||||
|
||||
@state() private _computedEndTime!: Date;
|
||||
private _computedEndTime!: Date;
|
||||
|
||||
@state() private _maxYWidth = 0;
|
||||
|
||||
@@ -114,31 +114,6 @@ export class StateHistoryCharts extends LitElement {
|
||||
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
|
||||
this._computedEndTime =
|
||||
this.upToNow || !this.endTime || this.endTime > now ? now : this.endTime;
|
||||
|
||||
if (this.startTime) {
|
||||
this._computedStartTime = this.startTime;
|
||||
} else if (this.hoursToShow) {
|
||||
this._computedStartTime = new Date(
|
||||
new Date().getTime() - 60 * 60 * this.hoursToShow * 1000
|
||||
);
|
||||
} else {
|
||||
this._computedStartTime = new Date(
|
||||
this.historyData.timeline.reduce(
|
||||
(minTime, stateInfo) =>
|
||||
Math.min(
|
||||
minTime,
|
||||
new Date(stateInfo.data[0].last_changed).getTime()
|
||||
),
|
||||
new Date().getTime()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const combinedItems = this.historyData.timeline.length
|
||||
? (this.virtualize
|
||||
? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
|
||||
@@ -220,10 +195,45 @@ export class StateHistoryCharts extends LitElement {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected willUpdate() {
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
if (!this.hasUpdated) {
|
||||
loadVirtualizer();
|
||||
}
|
||||
if (
|
||||
[...changedProps.keys()].some(
|
||||
(prop) =>
|
||||
!(
|
||||
["_maxYWidth", "_childYWidths", "_chartCount"] as PropertyKey[]
|
||||
).includes(prop)
|
||||
)
|
||||
) {
|
||||
// Don't recompute times when we just want to update layout
|
||||
const now = new Date();
|
||||
|
||||
this._computedEndTime =
|
||||
this.upToNow || !this.endTime || this.endTime > now
|
||||
? now
|
||||
: this.endTime;
|
||||
|
||||
if (this.startTime) {
|
||||
this._computedStartTime = this.startTime;
|
||||
} else if (this.hoursToShow) {
|
||||
this._computedStartTime = new Date(
|
||||
new Date().getTime() - 60 * 60 * this.hoursToShow * 1000
|
||||
);
|
||||
} else {
|
||||
this._computedStartTime = new Date(
|
||||
(this.historyData?.timeline ?? []).reduce(
|
||||
(minTime, stateInfo) =>
|
||||
Math.min(
|
||||
minTime,
|
||||
new Date(stateInfo.data[0].last_changed).getTime()
|
||||
),
|
||||
new Date().getTime()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
getNumberFormatOptions,
|
||||
} from "../../common/number/format_number";
|
||||
import {
|
||||
getDisplayUnit,
|
||||
@@ -72,8 +73,12 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public isLoadingData = false;
|
||||
|
||||
@property() public period?: string;
|
||||
|
||||
@state() private _chartData: ChartData = { datasets: [] };
|
||||
|
||||
@state() private _statisticIds: string[] = [];
|
||||
|
||||
@state() private _chartOptions?: ChartOptions;
|
||||
|
||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||
@@ -89,7 +94,12 @@ export class StatisticsChart extends LitElement {
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
if (!this.hasUpdated || changedProps.has("unit")) {
|
||||
if (
|
||||
!this.hasUpdated ||
|
||||
changedProps.has("unit") ||
|
||||
changedProps.has("period") ||
|
||||
changedProps.has("chartType")
|
||||
) {
|
||||
this._createOptions();
|
||||
}
|
||||
if (
|
||||
@@ -157,6 +167,7 @@ export class StatisticsChart extends LitElement {
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
source: this.chartType === "bar" ? "data" : undefined,
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
@@ -170,6 +181,12 @@ export class StatisticsChart extends LitElement {
|
||||
},
|
||||
time: {
|
||||
tooltipFormat: "datetime",
|
||||
unit:
|
||||
this.chartType === "bar" &&
|
||||
this.period &&
|
||||
["hour", "day", "week", "month"].includes(this.period)
|
||||
? this.period
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
@@ -189,7 +206,11 @@ export class StatisticsChart extends LitElement {
|
||||
label: (context) =>
|
||||
`${context.dataset.label}: ${formatNumber(
|
||||
context.parsed.y,
|
||||
this.hass.locale
|
||||
this.hass.locale,
|
||||
getNumberFormatOptions(
|
||||
undefined,
|
||||
this.hass.entities[this._statisticIds[context.datasetIndex]]
|
||||
)
|
||||
)} ${
|
||||
// @ts-ignore
|
||||
context.dataset.unit || ""
|
||||
@@ -248,6 +269,7 @@ export class StatisticsChart extends LitElement {
|
||||
let colorIndex = 0;
|
||||
const statisticsData = Object.entries(this.statisticsData);
|
||||
const totalDataSets: ChartDataset<"line">[] = [];
|
||||
const statisticIds: string[] = [];
|
||||
let endTime: Date;
|
||||
|
||||
if (statisticsData.length === 0) {
|
||||
@@ -386,6 +408,7 @@ export class StatisticsChart extends LitElement {
|
||||
unit: meta?.unit_of_measurement,
|
||||
band,
|
||||
});
|
||||
statisticIds.push(statistic_id);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -411,11 +434,7 @@ export class StatisticsChart extends LitElement {
|
||||
} else {
|
||||
val = stat[type];
|
||||
}
|
||||
dataValues.push(
|
||||
val !== null && val !== undefined
|
||||
? Math.round(val * 100) / 100
|
||||
: null
|
||||
);
|
||||
dataValues.push(val ?? null);
|
||||
});
|
||||
pushData(startDate, new Date(stat.end), dataValues);
|
||||
});
|
||||
@@ -431,6 +450,7 @@ export class StatisticsChart extends LitElement {
|
||||
this._chartData = {
|
||||
datasets: totalDataSets,
|
||||
};
|
||||
this._statisticIds = statisticIds;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
||||
53
src/components/chips/ha-assist-chip.ts
Normal file
53
src/components/chips/ha-assist-chip.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { MdAssistChip } from "@material/web/chips/assist-chip";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-assist-chip")
|
||||
export class HaAssistChip extends MdAssistChip {
|
||||
@property({ type: Boolean, reflect: true }) filled = false;
|
||||
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
--md-sys-color-on-surface: var(--primary-text-color);
|
||||
--md-assist-chip-container-shape: 16px;
|
||||
--md-assist-chip-outline-color: var(--outline-color);
|
||||
--md-assist-chip-label-text-weight: 400;
|
||||
--ha-assist-chip-filled-container-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
/** Material 3 doesn't have a filled chip, so we have to make our own **/
|
||||
.filled {
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
border-radius: inherit;
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
background-color: var(--ha-assist-chip-filled-container-color);
|
||||
}
|
||||
/** Set the size of mdc icons **/
|
||||
::slotted([slot="icon"]) {
|
||||
display: flex;
|
||||
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
protected override renderOutline() {
|
||||
if (this.filled) {
|
||||
return html`<span class="filled"></span>`;
|
||||
}
|
||||
|
||||
return super.renderOutline();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-assist-chip": HaAssistChip;
|
||||
}
|
||||
}
|
||||
11
src/components/chips/ha-chip-set.ts
Normal file
11
src/components/chips/ha-chip-set.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { MdChipSet } from "@material/web/chips/chip-set";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-chip-set")
|
||||
export class HaChipSet extends MdChipSet {}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-chip-set": HaChipSet;
|
||||
}
|
||||
}
|
||||
41
src/components/chips/ha-filter-chip.ts
Normal file
41
src/components/chips/ha-filter-chip.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MdFilterChip } from "@material/web/chips/filter-chip";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-filter-chip")
|
||||
export class HaFilterChip extends MdFilterChip {
|
||||
@property({ type: Boolean, reflect: true, attribute: "no-leading-icon" })
|
||||
noLeadingIcon = false;
|
||||
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
--md-sys-color-on-surface: var(--primary-text-color);
|
||||
--md-sys-color-on-surface-variant: var(--primary-text-color);
|
||||
--md-sys-color-on-secondary-container: var(--primary-text-color);
|
||||
--md-filter-chip-container-shape: 16px;
|
||||
--md-filter-chip-outline-color: var(--outline-color);
|
||||
--md-filter-chip-selected-container-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
protected renderLeadingIcon() {
|
||||
if (this.noLeadingIcon) {
|
||||
// eslint-disable-next-line lit/prefer-nothing
|
||||
return html``;
|
||||
}
|
||||
return super.renderLeadingIcon();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-filter-chip": HaFilterChip;
|
||||
}
|
||||
}
|
||||
35
src/components/chips/ha-input-chip.ts
Normal file
35
src/components/chips/ha-input-chip.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { MdInputChip } from "@material/web/chips/input-chip";
|
||||
import { css } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-input-chip")
|
||||
export class HaInputChip extends MdInputChip {
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
--md-sys-color-on-surface: var(--primary-text-color);
|
||||
--md-sys-color-on-surface-variant: var(--primary-text-color);
|
||||
--md-sys-color-on-secondary-container: var(--primary-text-color);
|
||||
--md-input-chip-container-shape: 16px;
|
||||
--md-input-chip-outline-color: var(--outline-color);
|
||||
--md-input-chip-selected-container-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
/** Set the size of mdc icons **/
|
||||
::slotted([slot="icon"]) {
|
||||
display: flex;
|
||||
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-input-chip": HaInputChip;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,10 @@ const Component = Vue.extend({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
openingDirection: {
|
||||
type: String,
|
||||
default: "right",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -66,7 +70,7 @@ const Component = Vue.extend({
|
||||
props: {
|
||||
"time-picker": this.timePicker,
|
||||
"auto-apply": this.autoApply,
|
||||
opens: "right",
|
||||
opens: this.openingDirection,
|
||||
"show-dropdowns": false,
|
||||
"time-picker24-hour": this.twentyfourHours,
|
||||
disabled: this.disabled,
|
||||
@@ -126,9 +130,9 @@ class DateRangePickerElement extends WrappedElement {
|
||||
${dateRangePickerStyles}
|
||||
.calendars {
|
||||
display: flex;
|
||||
flex-wrap: nowrap !important;
|
||||
}
|
||||
.daterangepicker {
|
||||
left: 0px !important;
|
||||
top: auto;
|
||||
box-shadow: var(--ha-card-box-shadow, none);
|
||||
background-color: var(--card-background-color);
|
||||
@@ -252,6 +256,10 @@ class DateRangePickerElement extends WrappedElement {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
.vue-daterange-picker{
|
||||
min-width: unset !important;
|
||||
display: block !important;
|
||||
}
|
||||
`;
|
||||
const shadowRoot = this.shadowRoot!;
|
||||
shadowRoot.appendChild(style);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user