mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-08 19:54:24 +00:00
Compare commits
349 Commits
log-classi
...
remove-app
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ecb41accd | ||
|
|
68cbaf6481 | ||
|
|
b9b249a317 | ||
|
|
df6578dfdf | ||
|
|
b4a07f504c | ||
|
|
24b8078a9c | ||
|
|
2bd8a657b8 | ||
|
|
61724a52ba | ||
|
|
67d73261a4 | ||
|
|
419ba64bed | ||
|
|
e87155bba3 | ||
|
|
d8a3939e22 | ||
|
|
5141b6aea9 | ||
|
|
9aaf1bdd07 | ||
|
|
a0e7c8e9bb | ||
|
|
8f482776b9 | ||
|
|
2c80183ded | ||
|
|
9bd5e25ee3 | ||
|
|
994193465f | ||
|
|
530b7ed457 | ||
|
|
24332e8288 | ||
|
|
980aaa2bac | ||
|
|
344856cbc4 | ||
|
|
06877cbaaa | ||
|
|
e1ac7e98b0 | ||
|
|
4b2426ab77 | ||
|
|
f9471d6b4c | ||
|
|
0645484258 | ||
|
|
2da3efb812 | ||
|
|
e3674e550f | ||
|
|
cff038fea4 | ||
|
|
bed39fd8e9 | ||
|
|
b8346d0286 | ||
|
|
5f80b74322 | ||
|
|
73e0f1e18d | ||
|
|
0b16b735e8 | ||
|
|
c88dcf10b9 | ||
|
|
08bc23e2e7 | ||
|
|
97d49f06b1 | ||
|
|
50b727393d | ||
|
|
5c6dd2a697 | ||
|
|
87758cc228 | ||
|
|
60e8b8b505 | ||
|
|
3c012c30ac | ||
|
|
84d234a330 | ||
|
|
a12543fe74 | ||
|
|
cc53f977a2 | ||
|
|
71541625d7 | ||
|
|
43da700ccc | ||
|
|
efbbdbf3e8 | ||
|
|
eee6f79639 | ||
|
|
9381bbd656 | ||
|
|
2724087290 | ||
|
|
6bdf1ccd8c | ||
|
|
79743c0afa | ||
|
|
15b1df5a58 | ||
|
|
8222d9796c | ||
|
|
3337b414d7 | ||
|
|
ce90d83c92 | ||
|
|
1f6d0d2e63 | ||
|
|
0327b02d0b | ||
|
|
780db9b066 | ||
|
|
89755f274d | ||
|
|
6ea15f507a | ||
|
|
c506fa8990 | ||
|
|
9470863808 | ||
|
|
4bdac1f385 | ||
|
|
5bbfa36228 | ||
|
|
a8070b322c | ||
|
|
9cbc44123e | ||
|
|
c8f4c892f9 | ||
|
|
40b9f9dccb | ||
|
|
823c222a55 | ||
|
|
02acd2996c | ||
|
|
c462fc0639 | ||
|
|
903553dab9 | ||
|
|
25a1c14523 | ||
|
|
f03eee6cb2 | ||
|
|
a2a38e1da7 | ||
|
|
88c063ba2a | ||
|
|
984b50bac7 | ||
|
|
09e4355451 | ||
|
|
7ee76538ae | ||
|
|
eb8b2a9d17 | ||
|
|
10e8c2a148 | ||
|
|
e1a8616ab0 | ||
|
|
ccdd71dd64 | ||
|
|
d3e1d55686 | ||
|
|
4f916abcbf | ||
|
|
4548f9daae | ||
|
|
4020bcec42 | ||
|
|
22c0035e60 | ||
|
|
6b6ad8dd2c | ||
|
|
3bbc3403d6 | ||
|
|
9979bb13ea | ||
|
|
8ac831679d | ||
|
|
db05b07997 | ||
|
|
dba8cefa67 | ||
|
|
6935c55c3c | ||
|
|
635a1185a3 | ||
|
|
585c894c5a | ||
|
|
f9d052a818 | ||
|
|
a29132441d | ||
|
|
479d52bf1d | ||
|
|
d96d78d6f6 | ||
|
|
f80cba341f | ||
|
|
77ee966442 | ||
|
|
2fec5a497e | ||
|
|
ed75d96d3c | ||
|
|
0fac47992b | ||
|
|
91a608c4c5 | ||
|
|
df61953ed4 | ||
|
|
2cda46b4bb | ||
|
|
037190a393 | ||
|
|
ebe0154e32 | ||
|
|
efa73067f6 | ||
|
|
fdb40c9d01 | ||
|
|
5a7ddb4972 | ||
|
|
dbe46d3b3f | ||
|
|
eb43d85439 | ||
|
|
1bbfb79ddb | ||
|
|
cf50db350f | ||
|
|
e04a0ec7dc | ||
|
|
e08576a6dc | ||
|
|
a7831f86ee | ||
|
|
c66e5b379b | ||
|
|
e819c30151 | ||
|
|
e278e33375 | ||
|
|
8313be8e7e | ||
|
|
6d95a59ca0 | ||
|
|
a498ad3d06 | ||
|
|
c4a2229baa | ||
|
|
15245af52d | ||
|
|
c697735e46 | ||
|
|
ddec792ae3 | ||
|
|
cfd0e72609 | ||
|
|
8a5bcd67ab | ||
|
|
a794a80228 | ||
|
|
41ed7d2877 | ||
|
|
b0b86e7ba8 | ||
|
|
e67c4842d4 | ||
|
|
d9c39640e0 | ||
|
|
a8478ab346 | ||
|
|
3ac2434b6f | ||
|
|
f2f1044992 | ||
|
|
53bc66883a | ||
|
|
d795bd1f61 | ||
|
|
869e1d32b3 | ||
|
|
3370bfa9dd | ||
|
|
b1921d1b66 | ||
|
|
c2a2b382e9 | ||
|
|
7d95c2b6cb | ||
|
|
67536a8a64 | ||
|
|
3d89ad4f91 | ||
|
|
36e08367d9 | ||
|
|
6e3cf3e42f | ||
|
|
fe53656c7e | ||
|
|
14615191f4 | ||
|
|
86ca8ebf71 | ||
|
|
16c1db5346 | ||
|
|
b9568c079e | ||
|
|
0f9d90c217 | ||
|
|
ad092df9e0 | ||
|
|
484473080e | ||
|
|
97dcc62ae7 | ||
|
|
d12d4811b4 | ||
|
|
3747abe1a9 | ||
|
|
8541494b3d | ||
|
|
2d1e211034 | ||
|
|
147a21db27 | ||
|
|
e2e5feb8d5 | ||
|
|
619c75ac8b | ||
|
|
5b09101903 | ||
|
|
408efd2e2c | ||
|
|
fd0b503a21 | ||
|
|
1e9a1eaa04 | ||
|
|
b42d52b854 | ||
|
|
21a5e643ac | ||
|
|
52ac052baf | ||
|
|
a13ffa3c35 | ||
|
|
c51535eb79 | ||
|
|
5ea05768a3 | ||
|
|
d10376e9d8 | ||
|
|
e21f7baa93 | ||
|
|
14098e0d7e | ||
|
|
bd8c407690 | ||
|
|
c452acf6cf | ||
|
|
814106e4aa | ||
|
|
a2254eb048 | ||
|
|
fa5e0bb0c7 | ||
|
|
e8e62a0aa0 | ||
|
|
4893b631a5 | ||
|
|
d851a5bdf3 | ||
|
|
148d01b05b | ||
|
|
171875c65c | ||
|
|
6a61186507 | ||
|
|
1690991726 | ||
|
|
4904566460 | ||
|
|
b3b8f1a38a | ||
|
|
3bb870f52a | ||
|
|
95a7b91ea5 | ||
|
|
9c4aacdb1f | ||
|
|
3feb40a8f4 | ||
|
|
7a310812e0 | ||
|
|
ee77619da3 | ||
|
|
cfa8eb5370 | ||
|
|
d9d2d6aa03 | ||
|
|
1f46f477c7 | ||
|
|
52667b3266 | ||
|
|
c790d2356c | ||
|
|
f24c009dd7 | ||
|
|
8d42395938 | ||
|
|
1a6d46a7ff | ||
|
|
b286b07cfd | ||
|
|
1859d35f7b | ||
|
|
29b3303f94 | ||
|
|
5dc2c51251 | ||
|
|
571fccf4a0 | ||
|
|
8f958147dd | ||
|
|
41753e48bf | ||
|
|
d496b2a6fe | ||
|
|
3260c48130 | ||
|
|
a356c153f8 | ||
|
|
b60dc94196 | ||
|
|
c5cb196aa1 | ||
|
|
5ddb31ce7b | ||
|
|
7465c9874c | ||
|
|
bbe81ca8f1 | ||
|
|
51ecdb0851 | ||
|
|
268c0a60f0 | ||
|
|
8745cfa1ed | ||
|
|
8bcccb5792 | ||
|
|
0844d9c2c0 | ||
|
|
9d3aafe219 | ||
|
|
0cf3e34956 | ||
|
|
7d20b04469 | ||
|
|
6f6ba71e61 | ||
|
|
d61de4d2df | ||
|
|
f8dcee6867 | ||
|
|
0f975acca1 | ||
|
|
9e57dacb3c | ||
|
|
2650c1c0b2 | ||
|
|
6957e1f4d9 | ||
|
|
32be26320d | ||
|
|
cdbaa85beb | ||
|
|
b58fc68ea8 | ||
|
|
4792659c61 | ||
|
|
eeae66e026 | ||
|
|
60b4b1c4e2 | ||
|
|
877c5e8d16 | ||
|
|
11fd10a011 | ||
|
|
623baea59d | ||
|
|
052909ea01 | ||
|
|
5e3c80ef6e | ||
|
|
6e2fdbb396 | ||
|
|
5b981062f6 | ||
|
|
5a2d7f100a | ||
|
|
b62e6c9328 | ||
|
|
537ab2debb | ||
|
|
04d31e4ea9 | ||
|
|
3d0c7b6aab | ||
|
|
87d3d06e79 | ||
|
|
872d1c684f | ||
|
|
d9d2ebfb9d | ||
|
|
e26315c4d2 | ||
|
|
d9a2e1cbee | ||
|
|
b7d6652e5c | ||
|
|
b188da0b5c | ||
|
|
9f87257f41 | ||
|
|
f4e409f157 | ||
|
|
44d727763c | ||
|
|
2f87614c4c | ||
|
|
697a945056 | ||
|
|
67dbacce52 | ||
|
|
e891ae6bdd | ||
|
|
a11d1cfecf | ||
|
|
933bb4a2cc | ||
|
|
1ca1ef3c32 | ||
|
|
f529a349b9 | ||
|
|
b3b35e4299 | ||
|
|
59364d05ec | ||
|
|
6444c0df15 | ||
|
|
8f704d8c0a | ||
|
|
aeddeb3bd7 | ||
|
|
1377e3a363 | ||
|
|
e47bf22798 | ||
|
|
b3f1ba9433 | ||
|
|
829c5ca4df | ||
|
|
fbd4fc6d5f | ||
|
|
7612ec1979 | ||
|
|
71a5a1d3b4 | ||
|
|
ad91d360f8 | ||
|
|
a88f492c05 | ||
|
|
0fc85a52a0 | ||
|
|
e01dc89a48 | ||
|
|
52c36db5d4 | ||
|
|
f217dab8d4 | ||
|
|
5709af57de | ||
|
|
bb16cc8c00 | ||
|
|
17c6dc52a8 | ||
|
|
1b8211db6d | ||
|
|
2b2bb77a2b | ||
|
|
64749350ef | ||
|
|
043d4eed85 | ||
|
|
2f2e64bb1d | ||
|
|
b74b02c09f | ||
|
|
ab4c3a4316 | ||
|
|
b89b02e044 | ||
|
|
daff323606 | ||
|
|
356bceb1cc | ||
|
|
acb5af2f41 | ||
|
|
17314fe62b | ||
|
|
3826a69465 | ||
|
|
a68acdf9dd | ||
|
|
bb56bbb1ba | ||
|
|
76aef60c05 | ||
|
|
15de137591 | ||
|
|
465c10b945 | ||
|
|
457c51cf58 | ||
|
|
640f2b9245 | ||
|
|
852caa32be | ||
|
|
67ccfa0f6e | ||
|
|
c3cc566fe3 | ||
|
|
38d02a3f30 | ||
|
|
ad1d1e2260 | ||
|
|
b2eb8ec968 | ||
|
|
7b8884f0fd | ||
|
|
aff1fedc9d | ||
|
|
8f5059c24a | ||
|
|
1e72ad1411 | ||
|
|
c9f96bbe69 | ||
|
|
616c3d4657 | ||
|
|
b1ceece224 | ||
|
|
d695c4c845 | ||
|
|
fdbeb12622 | ||
|
|
29ede122a1 | ||
|
|
519d3d0e53 | ||
|
|
030a9a492c | ||
|
|
2685a007e7 | ||
|
|
9ca1cfbf4a | ||
|
|
0793af6846 | ||
|
|
bb7f441d8d | ||
|
|
2813ed7938 | ||
|
|
9ebfa4029b | ||
|
|
6190ba18ea | ||
|
|
81feea1109 | ||
|
|
be430931cc | ||
|
|
e07194027a | ||
|
|
17d9cd192f |
9
.github/copilot-instructions.md
vendored
9
.github/copilot-instructions.md
vendored
@@ -464,7 +464,14 @@ this.hass.localize("ui.panel.config.updates.update_available", {
|
||||
|
||||
### Pull Requests
|
||||
|
||||
When creating a pull request, you **must** use the PR template located at `.github/PULL_REQUEST_TEMPLATE.md`. Read the template file and use its full content as the PR body, filling in each section appropriately. Do not omit, reorder, or rewrite the template sections. Do not check the checklist items on behalf of the user — those are the user's responsibility to review and check. If the PR includes UI changes, remind the user to add screenshots or a short video to the PR after creating it.
|
||||
When creating a pull request, you **must** use the PR template located at `.github/PULL_REQUEST_TEMPLATE.md`. Read the template file and use its full content as the PR body, filling in each section appropriately.
|
||||
|
||||
- Do not omit, reorder, or rewrite the template sections
|
||||
- Check the appropriate "Type of change" box based on the changes
|
||||
- Do not check the checklist items on behalf of the user — those are the user's responsibility to review and check
|
||||
- If the PR includes UI changes, remind the user to add screenshots or a short video to the PR after creating it
|
||||
- Be simple and user friendly — explain what the change does, not implementation details
|
||||
- Use markdown so the user can copy it
|
||||
|
||||
### Text and Copy Guidelines
|
||||
|
||||
|
||||
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
@@ -5,6 +5,9 @@ updates:
|
||||
schedule:
|
||||
interval: weekly
|
||||
time: "06:00"
|
||||
cooldown:
|
||||
default-days-before-reopen: 30
|
||||
default-days: 7
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- Dependencies
|
||||
|
||||
9
.github/workflows/cast_deployment.yaml
vendored
9
.github/workflows/cast_deployment.yaml
vendored
@@ -8,6 +8,9 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
@@ -24,9 +27,10 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -59,9 +63,10 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: master
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
17
.github/workflows/ci.yaml
vendored
17
.github/workflows/ci.yaml
vendored
@@ -18,6 +18,9 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint and check format
|
||||
@@ -25,8 +28,10 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -37,7 +42,7 @@ jobs:
|
||||
- name: Build resources
|
||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
||||
- name: Setup lint cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: |
|
||||
node_modules/.cache/prettier
|
||||
@@ -59,8 +64,10 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -77,8 +84,10 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [dev]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
@@ -28,6 +32,7 @@ jobs:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
persist-credentials: false
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
@@ -36,14 +41,14 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -57,4 +62,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
|
||||
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
|
||||
9
.github/workflows/demo_deployment.yaml
vendored
9
.github/workflows/demo_deployment.yaml
vendored
@@ -9,6 +9,9 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
@@ -25,9 +28,10 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -60,9 +64,10 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: master
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
7
.github/workflows/design_deployment.yaml
vendored
7
.github/workflows/design_deployment.yaml
vendored
@@ -5,6 +5,9 @@ on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
@@ -17,9 +20,11 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
7
.github/workflows/design_preview.yaml
vendored
7
.github/workflows/design_preview.yaml
vendored
@@ -10,6 +10,9 @@ on:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
@@ -22,9 +25,11 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
2
.github/workflows/labeler.yaml
vendored
2
.github/workflows/labeler.yaml
vendored
@@ -1,6 +1,6 @@
|
||||
name: "Pull Request Labeler"
|
||||
|
||||
on: pull_request_target
|
||||
on: pull_request_target # zizmor: ignore[dangerous-triggers] -- safe: only runs actions/labeler, no PR code checkout
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
|
||||
4
.github/workflows/lock.yml
vendored
4
.github/workflows/lock.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/nightly.yaml
vendored
4
.github/workflows/nightly.yaml
vendored
@@ -21,6 +21,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
@@ -28,7 +30,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
32
.github/workflows/relative-ci.yaml
vendored
32
.github/workflows/relative-ci.yaml
vendored
@@ -1,25 +1,39 @@
|
||||
name: RelativeCI
|
||||
|
||||
on:
|
||||
# zizmor: ignore[dangerous-triggers] -- safe: only downloads artifacts, no PR code checkout
|
||||
workflow_run:
|
||||
workflows: [CI]
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload stats
|
||||
upload-frontend-modern:
|
||||
name: Upload stats (frontend/modern)
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
strategy:
|
||||
matrix:
|
||||
bundle: [frontend]
|
||||
build: [modern, legacy]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send bundle stats and build information to RelativeCI
|
||||
uses: relative-ci/agent-action@3c681926017930047fc03acaa35cd6a44efcbfc3 # v3.2.2
|
||||
with:
|
||||
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
||||
key: ${{ secrets.RELATIVE_CI_KEY_frontend_modern }}
|
||||
token: ${{ github.token }}
|
||||
artifactName: ${{ format('{0}-bundle-stats', matrix.bundle) }}
|
||||
webpackStatsFile: ${{ format('{0}-{1}.json', matrix.bundle, matrix.build) }}
|
||||
artifactName: frontend-bundle-stats
|
||||
webpackStatsFile: frontend-modern.json
|
||||
|
||||
upload-frontend-legacy:
|
||||
name: Upload stats (frontend/legacy)
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send bundle stats and build information to RelativeCI
|
||||
uses: relative-ci/agent-action@3c681926017930047fc03acaa35cd6a44efcbfc3 # v3.2.2
|
||||
with:
|
||||
key: ${{ secrets.RELATIVE_CI_KEY_frontend_legacy }}
|
||||
token: ${{ github.token }}
|
||||
artifactName: frontend-bundle-stats
|
||||
webpackStatsFile: frontend-legacy.json
|
||||
|
||||
2
.github/workflows/release-drafter.yaml
vendored
2
.github/workflows/release-drafter.yaml
vendored
@@ -18,6 +18,6 @@ jobs:
|
||||
pull-requests: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0
|
||||
- uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
38
.github/workflows/release.yaml
vendored
38
.github/workflows/release.yaml
vendored
@@ -27,6 +27,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
@@ -34,13 +36,12 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Verify version
|
||||
uses: home-assistant/actions/helpers/verify-version@master
|
||||
uses: home-assistant/actions/helpers/verify-version@d56d093b9ab8d2105bc0cb6ee9bcc0ef4ec8b96d # master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
@@ -62,11 +63,10 @@ jobs:
|
||||
skip-existing: true
|
||||
|
||||
- name: Upload release assets
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
files: |
|
||||
dist/*.whl
|
||||
dist/*.tar.gz
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG_NAME" dist/*.whl dist/*.tar.gz --clobber
|
||||
|
||||
wheels-init:
|
||||
name: Init wheels build
|
||||
@@ -74,15 +74,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate requirements.txt
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: |
|
||||
# Sleep to give pypi time to populate the new version across mirrors
|
||||
sleep 240
|
||||
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
||||
version=$(echo "$GITHUB_REF" | awk -F"/" '{print $NF}' )
|
||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||
|
||||
# home-assistant/wheels doesn't support SHA pinning
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2025.12.0
|
||||
uses: home-assistant/wheels@e5742a69d69f0e274e2689c998900c7d19652c21 # 2025.12.0
|
||||
with:
|
||||
abi: cp314
|
||||
tag: musllinux_1_2
|
||||
@@ -99,11 +101,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Download Translations
|
||||
@@ -113,8 +116,11 @@ jobs:
|
||||
- name: Build landing-page
|
||||
run: landing-page/script/build_landing_page
|
||||
- name: Tar folder
|
||||
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
||||
env:
|
||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: tar -czf "landing-page/home_assistant_frontend_landingpage-${TAG_NAME}.tar.gz" -C landing-page/dist .
|
||||
- name: Upload release asset
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG_NAME" "landing-page/home_assistant_frontend_landingpage-${TAG_NAME}.tar.gz" --clobber
|
||||
|
||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
5
.github/workflows/translations.yaml
vendored
5
.github/workflows/translations.yaml
vendored
@@ -8,6 +8,9 @@ on:
|
||||
paths:
|
||||
- src/translations/en.json
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Upload
|
||||
@@ -15,6 +18,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
|
||||
@@ -31,7 +31,7 @@ index 8795ddcaa77aea7b0356417e4bc4b19e2b3f860c..fcdc68342d9ac53936c9ed40a9ccfc2f
|
||||
@@ -129,7 +129,10 @@ export async function injectManifest(
|
||||
searchString: options.injectionPoint!,
|
||||
});
|
||||
|
||||
|
||||
- filesToWrite[options.swDest] = source;
|
||||
+ filesToWrite[options.swDest] = source.replace(
|
||||
+ url!,
|
||||
942
.yarn/releases/yarn-4.12.0.cjs
vendored
942
.yarn/releases/yarn-4.12.0.cjs
vendored
File diff suppressed because one or more lines are too long
940
.yarn/releases/yarn-4.13.0.cjs
vendored
Executable file
940
.yarn/releases/yarn-4.13.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
@@ -8,4 +8,4 @@ enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.13.0.cjs
|
||||
|
||||
@@ -40,18 +40,24 @@ const convertToJSON = async (
|
||||
throw e;
|
||||
}
|
||||
// Convert to JSON
|
||||
const parts = localeData.split("} else {");
|
||||
const firstBlock = parts[0];
|
||||
const obj = INTL_POLYFILLS[pkg];
|
||||
const dataRegex = new RegExp(
|
||||
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
|
||||
"s"
|
||||
);
|
||||
localeData = localeData.match(dataRegex)?.groups?.data;
|
||||
localeData = firstBlock.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(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||
try {
|
||||
localeData = JSON.stringify(JSON.parse(localeData));
|
||||
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
|
||||
} catch (e) {
|
||||
throw Error(`Failed to parse JSON for language ${lang} from ${pkg}: ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
|
||||
|
||||
@@ -57,6 +57,12 @@ const runDevServer = async ({
|
||||
directory: contentBase,
|
||||
watch: true,
|
||||
},
|
||||
client: {
|
||||
overlay: {
|
||||
runtimeErrors: (error) =>
|
||||
!error?.message?.includes("ResizeObserver loop"),
|
||||
},
|
||||
},
|
||||
proxy,
|
||||
},
|
||||
compiler
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { Entity } from "../../../../src/fake_data/entity";
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { EntityInput } from "../../../../src/fake_data/entities/types";
|
||||
|
||||
export const castDemoEntities: () => Entity[] = () =>
|
||||
convertEntities({
|
||||
export const castDemoEntities: () => EntityInput[] = () =>
|
||||
Object.values({
|
||||
"light.reading_light": {
|
||||
entity_id: "light.reading_light",
|
||||
state: "on",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"cover.living_room_garden_shutter": {
|
||||
entity_id: "cover.living_room_garden_shutter",
|
||||
state: "open",
|
||||
@@ -142,7 +141,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"device_tracker.car": {
|
||||
entity_id: "sensor.outdoor_humidity",
|
||||
entity_id: "device_tracker.car",
|
||||
state: "not_home",
|
||||
attributes: {
|
||||
friendly_name: "Car",
|
||||
@@ -200,7 +199,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"binary_sensor.kitchen_motion": {
|
||||
entity_id: "light.kitchen_motion",
|
||||
entity_id: "binary_sensor.kitchen_motion",
|
||||
state: "on",
|
||||
attributes: {
|
||||
device_class: "motion",
|
||||
@@ -336,7 +335,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"sensor.rain": {
|
||||
entity_id: "sensor.moon_phase",
|
||||
entity_id: "sensor.rain",
|
||||
state: "7.2",
|
||||
attributes: {
|
||||
state_class: "total_increasing",
|
||||
@@ -566,7 +565,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
||||
},
|
||||
},
|
||||
"update.home_assistant_core_update": {
|
||||
entity_id: "update.home_assistant_supervisor_update",
|
||||
entity_id: "update.home_assistant_core_update",
|
||||
state: "off",
|
||||
attributes: {
|
||||
auto_update: false,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { convertEntities } from "../../../../src/fake_data/entity";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"todo.shopping_list": {
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import type { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import type { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||
import type { Entity } from "../../../src/fake_data/entity";
|
||||
import type { EntityInput } from "../../../src/fake_data/entities/types";
|
||||
|
||||
export interface DemoConfig {
|
||||
index?: number;
|
||||
@@ -12,6 +12,6 @@ export interface DemoConfig {
|
||||
| string
|
||||
| ((localize: LocalizeFunc) => string | TemplateResult<1>);
|
||||
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
|
||||
entities: (localize: LocalizeFunc) => Entity[];
|
||||
entities: (localize: LocalizeFunc) => EntityInput[];
|
||||
theme: () => Record<string, string> | null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const mapEntities = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"zone.home": {
|
||||
entity_id: "zone.home",
|
||||
state: "zoning",
|
||||
@@ -51,7 +49,7 @@ export const mapEntities = () =>
|
||||
});
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
Object.values({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
|
||||
@@ -1,175 +1,253 @@
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
|
||||
export const createMediaPlayerEntities = () => [
|
||||
getEntity("media_player", "music_paused", "paused", {
|
||||
friendly_name: "Pausing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover_2.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 50,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
source: "AirPlay",
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
}),
|
||||
getEntity("media_player", "music_playing", "playing", {
|
||||
friendly_name: "Playing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
|
||||
supported_features: 784959,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
group_members: ["media_player.playing", "media_player.stream_playing"],
|
||||
}),
|
||||
getEntity("media_player", "stream_playing", "playing", {
|
||||
friendly_name: "Playing the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play + Browse Media
|
||||
supported_features: 147489,
|
||||
}),
|
||||
getEntity("media_player", "stream_paused", "paused", {
|
||||
friendly_name: "Paused the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play
|
||||
supported_features: 16417,
|
||||
}),
|
||||
getEntity("media_player", "stream_playing_previous", "playing", {
|
||||
friendly_name: 'Playing the Stream (with "previous" support)',
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Previous Track + Play
|
||||
supported_features: 16401,
|
||||
}),
|
||||
getEntity("media_player", "tv_playing", "playing", {
|
||||
friendly_name: "Playing non-skip TV Show",
|
||||
media_content_type: "tvshow",
|
||||
media_title: "Chapter 1",
|
||||
media_series_title: "House of Cards",
|
||||
app_name: "Netflix",
|
||||
entity_picture: "/images/netflix.jpg",
|
||||
// Pause
|
||||
supported_features: 1,
|
||||
}),
|
||||
getEntity("media_player", "sonos_idle", "idle", {
|
||||
friendly_name: "Sonos Idle",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
volume_level: 0.33,
|
||||
is_volume_muted: true,
|
||||
}),
|
||||
getEntity("media_player", "idle_browse_media", "idle", {
|
||||
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
|
||||
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Play + Shuffle Set + Browse Media
|
||||
supported_features: 182839,
|
||||
volume_level: 0.79,
|
||||
}),
|
||||
getEntity("media_player", "theater_off", "off", {
|
||||
friendly_name: "TV Off",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
}),
|
||||
getEntity("media_player", "theater_on", "on", {
|
||||
friendly_name: "TV On",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
}),
|
||||
getEntity("media_player", "theater_off_static", "off", {
|
||||
friendly_name: "TV Off (cannot be switched on)",
|
||||
// Off + Next + Pause
|
||||
supported_features: 289,
|
||||
}),
|
||||
getEntity("media_player", "theater_on_static", "on", {
|
||||
friendly_name: "TV On (cannot be switched off)",
|
||||
// On + Next + Pause
|
||||
supported_features: 161,
|
||||
}),
|
||||
getEntity("media_player", "android_cast", "playing", {
|
||||
friendly_name: "Casting App (no supported features)",
|
||||
media_title: "Android Screen Casting",
|
||||
app_name: "Screen Mirroring",
|
||||
}),
|
||||
getEntity("media_player", "image_display", "playing", {
|
||||
friendly_name: "Digital Picture Frame",
|
||||
media_content_type: "image",
|
||||
media_title: "Famous Painting",
|
||||
media_artist: "Famous Artist",
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
// On + Off + Browse Media
|
||||
supported_features: 131456,
|
||||
}),
|
||||
getEntity("media_player", "unavailable", "unavailable", {
|
||||
friendly_name: "Player Unavailable",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "unknown", "unknown", {
|
||||
friendly_name: "Player Unknown",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "playing", "playing", {
|
||||
friendly_name: "Player Playing (no Pause support)",
|
||||
// Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21436,
|
||||
volume_level: 1,
|
||||
}),
|
||||
getEntity("media_player", "idle", "idle", {
|
||||
friendly_name: "Player Idle",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
volume_level: 0,
|
||||
}),
|
||||
getEntity("media_player", "receiver_on", "on", {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
volume_level: 0.63,
|
||||
is_volume_muted: false,
|
||||
source: "TV",
|
||||
sound_mode: "Movie",
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
}),
|
||||
getEntity("media_player", "receiver_off", "off", {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
}),
|
||||
{
|
||||
entity_id: "media_player.music_paused",
|
||||
state: "paused",
|
||||
attributes: {
|
||||
friendly_name: "Pausing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover_2.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 50,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
source: "AirPlay",
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.music_playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Playing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
|
||||
supported_features: 784959,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
volume_level: 0.5,
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
sound_mode: "Music",
|
||||
group_members: ["media_player.playing", "media_player.stream_playing"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.stream_playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Playing the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play + Browse Media
|
||||
supported_features: 147489,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.stream_paused",
|
||||
state: "paused",
|
||||
attributes: {
|
||||
friendly_name: "Paused the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Next Track + Play
|
||||
supported_features: 16417,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.stream_playing_previous",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: 'Playing the Stream (with "previous" support)',
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
entity_picture: "/images/frenck.jpg",
|
||||
// Pause + Previous Track + Play
|
||||
supported_features: 16401,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.tv_playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Playing non-skip TV Show",
|
||||
media_content_type: "tvshow",
|
||||
media_title: "Chapter 1",
|
||||
media_series_title: "House of Cards",
|
||||
app_name: "Netflix",
|
||||
entity_picture: "/images/netflix.jpg",
|
||||
// Pause
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.sonos_idle",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Sonos Idle",
|
||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Stop + Clear + Play + Shuffle Set
|
||||
supported_features: 64063,
|
||||
volume_level: 0.33,
|
||||
is_volume_muted: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.idle_browse_media",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
|
||||
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
|
||||
// Select Source + Play + Shuffle Set + Browse Media
|
||||
supported_features: 182839,
|
||||
volume_level: 0.79,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_off",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "TV Off",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_on",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "TV On",
|
||||
// On + Off + Play + Next + Pause
|
||||
supported_features: 16801,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_off_static",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "TV Off (cannot be switched on)",
|
||||
// Off + Next + Pause
|
||||
supported_features: 289,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.theater_on_static",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "TV On (cannot be switched off)",
|
||||
// On + Next + Pause
|
||||
supported_features: 161,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.android_cast",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Casting App (no supported features)",
|
||||
media_title: "Android Screen Casting",
|
||||
app_name: "Screen Mirroring",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.image_display",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Digital Picture Frame",
|
||||
media_content_type: "image",
|
||||
media_title: "Famous Painting",
|
||||
media_artist: "Famous Artist",
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
// On + Off + Browse Media
|
||||
supported_features: 131456,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Player Unavailable",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.unknown",
|
||||
state: "unknown",
|
||||
attributes: {
|
||||
friendly_name: "Player Unknown",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.playing",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Player Playing (no Pause support)",
|
||||
// Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21436,
|
||||
volume_level: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.idle",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Player Idle",
|
||||
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
|
||||
// Play Media + Stop + Play
|
||||
supported_features: 21437,
|
||||
volume_level: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.receiver_on",
|
||||
state: "on",
|
||||
attributes: {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
volume_level: 0.63,
|
||||
is_volume_muted: false,
|
||||
source: "TV",
|
||||
sound_mode: "Movie",
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.receiver_off",
|
||||
state: "off",
|
||||
attributes: {
|
||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
||||
friendly_name: "Receiver (selectable sources)",
|
||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||
supported_features: 84364,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,72 +1,82 @@
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
|
||||
export const createPlantEntities = () => [
|
||||
getEntity("plant", "lemon_tree", "ok", {
|
||||
problem: "none",
|
||||
sensors: {
|
||||
moisture: "sensor.lemon_tree_moisture",
|
||||
battery: "sensor.lemon_tree_battery",
|
||||
temperature: "sensor.lemon_tree_temperature",
|
||||
conductivity: "sensor.lemon_tree_conductivity",
|
||||
brightness: "sensor.lemon_tree_brightness",
|
||||
{
|
||||
entity_id: "plant.lemon_tree",
|
||||
state: "ok",
|
||||
attributes: {
|
||||
problem: "none",
|
||||
sensors: {
|
||||
moisture: "sensor.lemon_tree_moisture",
|
||||
battery: "sensor.lemon_tree_battery",
|
||||
temperature: "sensor.lemon_tree_temperature",
|
||||
conductivity: "sensor.lemon_tree_conductivity",
|
||||
brightness: "sensor.lemon_tree_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 95,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 12,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Lemon Tree",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
{
|
||||
entity_id: "plant.apple_tree",
|
||||
state: "ok",
|
||||
attributes: {
|
||||
problem: "brightness",
|
||||
sensors: {
|
||||
moisture: "sensor.apple_tree_moisture",
|
||||
battery: "sensor.apple_tree_battery",
|
||||
temperature: "sensor.apple_tree_temperature",
|
||||
conductivity: "sensor.apple_tree_conductivity",
|
||||
brightness: "sensor.apple_tree_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 2,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Apple Tree",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 95,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 12,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Lemon Tree",
|
||||
}),
|
||||
getEntity("plant", "apple_tree", "ok", {
|
||||
problem: "brightness",
|
||||
sensors: {
|
||||
moisture: "sensor.apple_tree_moisture",
|
||||
battery: "sensor.apple_tree_battery",
|
||||
temperature: "sensor.apple_tree_temperature",
|
||||
conductivity: "sensor.apple_tree_conductivity",
|
||||
brightness: "sensor.apple_tree_brightness",
|
||||
},
|
||||
{
|
||||
entity_id: "plant.sunflowers",
|
||||
state: "ok",
|
||||
attributes: {
|
||||
problem: "moisture, temperature, conductivity",
|
||||
sensors: {
|
||||
moisture: "sensor.sunflowers_moisture",
|
||||
temperature: "sensor.sunflowers_temperature",
|
||||
conductivity: "sensor.sunflowers_conductivity",
|
||||
brightness: "sensor.sunflowers_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
battery: "%",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
battery: 2,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
max_brightness: 20,
|
||||
friendly_name: "Apple Tree",
|
||||
}),
|
||||
getEntity("plant", "sunflowers", "ok", {
|
||||
problem: "moisture, temperature, conductivity",
|
||||
sensors: {
|
||||
moisture: "sensor.sunflowers_moisture",
|
||||
temperature: "sensor.sunflowers_temperature",
|
||||
conductivity: "sensor.sunflowers_conductivity",
|
||||
brightness: "sensor.sunflowers_brightness",
|
||||
},
|
||||
unit_of_measurement_dict: {
|
||||
temperature: "°C",
|
||||
moisture: "%",
|
||||
brightness: "lx",
|
||||
conductivity: "μS/cm",
|
||||
},
|
||||
moisture: 54,
|
||||
temperature: 15.6,
|
||||
conductivity: 1,
|
||||
brightness: 25,
|
||||
entity_picture: "/images/sunflowers.jpg",
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -5,17 +5,24 @@ import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { Action } from "../../../../src/data/script";
|
||||
import { describeAction } from "../../../../src/data/script_i18n";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("scene", "kitchen_morning", "scening", {
|
||||
friendly_name: "Kitchen Morning",
|
||||
}),
|
||||
getEntity("media_player", "kitchen", "playing", {
|
||||
friendly_name: "Sonos Kitchen",
|
||||
}),
|
||||
{
|
||||
entity_id: "scene.kitchen_morning",
|
||||
state: "scening",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Morning",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.kitchen",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Sonos Kitchen",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const ACTIONS = [
|
||||
|
||||
@@ -5,20 +5,31 @@ import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { Condition } from "../../../../src/data/automation";
|
||||
import { describeCondition } from "../../../../src/data/automation_i18n";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen", "on", {
|
||||
friendly_name: "Kitchen Light",
|
||||
}),
|
||||
getEntity("device_tracker", "person", "home", {
|
||||
friendly_name: "Person",
|
||||
}),
|
||||
getEntity("zone", "home", "", {
|
||||
friendly_name: "Home",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.person",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Person",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "",
|
||||
attributes: {
|
||||
friendly_name: "Home",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const conditions: Condition[] = [
|
||||
|
||||
@@ -5,20 +5,31 @@ import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-yaml-editor";
|
||||
import type { LegacyTrigger } from "../../../../src/data/automation";
|
||||
import { describeTrigger } from "../../../../src/data/automation_i18n";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen", "on", {
|
||||
friendly_name: "Kitchen Light",
|
||||
}),
|
||||
getEntity("person", "person", "", {
|
||||
friendly_name: "Person",
|
||||
}),
|
||||
getEntity("zone", "home", "", {
|
||||
friendly_name: "Home",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.person",
|
||||
state: "",
|
||||
attributes: {
|
||||
friendly_name: "Person",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "",
|
||||
attributes: {
|
||||
friendly_name: "Home",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const triggers = [
|
||||
|
||||
@@ -12,34 +12,53 @@ import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("media_player", "livingroom", "playing", {
|
||||
friendly_name: "Livingroom",
|
||||
media_content_type: "music",
|
||||
device_class: "tv",
|
||||
}),
|
||||
getEntity("media_player", "lounge", "idle", {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
device_class: "speaker",
|
||||
}),
|
||||
getEntity("light", "bedroom", "on", {
|
||||
friendly_name: "Bedroom",
|
||||
effect: "colorloop",
|
||||
effect_list: ["colorloop", "random"],
|
||||
}),
|
||||
getEntity("switch", "coffee", "off", {
|
||||
friendly_name: "Coffee",
|
||||
device_class: "switch",
|
||||
}),
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.livingroom",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Livingroom",
|
||||
media_content_type: "music",
|
||||
device_class: "tv",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.lounge",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
device_class: "speaker",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bedroom",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bedroom",
|
||||
effect: "colorloop",
|
||||
effect_list: ["colorloop", "random"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.coffee",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Coffee",
|
||||
device_class: "switch",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const DEVICES: DeviceRegistryEntry[] = [
|
||||
@@ -461,6 +480,12 @@ const SCHEMAS: {
|
||||
},
|
||||
{ type: "string", name: "path", default: "/" },
|
||||
{ type: "boolean", name: "ssl", default: false },
|
||||
{
|
||||
type: "string",
|
||||
name: "comments",
|
||||
default: "disabled field",
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
82
gallery/src/pages/components/ha-input.markdown
Normal file
82
gallery/src/pages/components/ha-input.markdown
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: Input
|
||||
---
|
||||
|
||||
# Input `<ha-input>`
|
||||
|
||||
A text input component supporting Home Assistant theming and validation, based on webawesome input.
|
||||
Supports multiple input types including text, number, password, email, search, and more.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Example usage
|
||||
|
||||
```html
|
||||
<ha-input label="Name" value="Hello"></ha-input>
|
||||
|
||||
<ha-input label="Email" type="email" placeholder="you@example.com"></ha-input>
|
||||
|
||||
<ha-input label="Password" type="password" password-toggle></ha-input>
|
||||
|
||||
<ha-input label="Required" required></ha-input>
|
||||
|
||||
<ha-input label="Disabled" disabled value="Can't touch this"></ha-input>
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
This component is based on the webawesome input component.
|
||||
|
||||
**Slots**
|
||||
|
||||
- `start`: Content placed before the input (usually for icons or prefixes).
|
||||
- `end`: Content placed after the input (usually for icons or suffixes).
|
||||
- `label`: Custom label content. Overrides the `label` property.
|
||||
- `hint`: Custom hint content. Overrides the `hint` property.
|
||||
- `clear-icon`: Custom clear icon.
|
||||
- `show-password-icon`: Custom show password icon.
|
||||
- `hide-password-icon`: Custom hide password icon.
|
||||
|
||||
**Properties/Attributes**
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| appearance | "material"/"outlined" | "material" | Sets the input appearance style. "material" is the default filled style, "outlined" uses a bordered style. |
|
||||
| type | "text"/"number"/"password"/"email"/"search"/"tel"/"url"/"date"/"datetime-local"/"time"/"color" | "text" | Sets the input type. |
|
||||
| value | String | - | The current value of the input. |
|
||||
| label | String | "" | The input's label text. |
|
||||
| hint | String | "" | The input's hint/helper text. |
|
||||
| placeholder | String | "" | Placeholder text shown when the input is empty. |
|
||||
| with-clear | Boolean | false | Adds a clear button when the input is not empty. |
|
||||
| readonly | Boolean | false | Makes the input readonly. |
|
||||
| disabled | Boolean | false | Disables the input and prevents user interaction. |
|
||||
| required | Boolean | false | Makes the input a required field. |
|
||||
| password-toggle | Boolean | false | Adds a button to toggle the password visibility. |
|
||||
| without-spin-buttons | Boolean | false | Hides the browser's built-in spin buttons for number inputs. |
|
||||
| auto-validate | Boolean | false | Validates the input on blur instead of on form submit. |
|
||||
| invalid | Boolean | false | Marks the input as invalid. |
|
||||
| inset-label | Boolean | false | Uses an inset label style where the label stays inside the input. |
|
||||
| validation-message | String | "" | Custom validation message shown when the input is invalid. |
|
||||
| pattern | String | - | A regular expression pattern to validate input against. |
|
||||
| minlength | Number | - | The minimum length of input that will be considered valid. |
|
||||
| maxlength | Number | - | The maximum length of input that will be considered valid. |
|
||||
| min | Number/String | - | The input's minimum value. Only applies to date and number input types. |
|
||||
| max | Number/String | - | The input's maximum value. Only applies to date and number input types. |
|
||||
| step | Number/"any" | - | Specifies the granularity that the value must adhere to. |
|
||||
|
||||
**CSS Custom Properties**
|
||||
|
||||
- `--ha-input-padding-top` - Padding above the input.
|
||||
- `--ha-input-padding-bottom` - Padding below the input. Defaults to `var(--ha-space-2)`.
|
||||
- `--ha-input-text-align` - Text alignment of the input. Defaults to `start`.
|
||||
- `--ha-input-required-marker` - The marker shown after the label for required fields. Defaults to `"*"`.
|
||||
|
||||
---
|
||||
|
||||
## Derivatives
|
||||
|
||||
The following components extend or wrap `ha-input` for specific use cases:
|
||||
|
||||
- **`<ha-input-search>`** — A pre-configured search input with a magnify icon, clear button, and localized "Search" placeholder. Extends `ha-input`.
|
||||
- **`<ha-input-copy>`** — A read-only input with a copy-to-clipboard button. Supports optional value masking with a reveal toggle.
|
||||
- **`<ha-input-multi>`** — A dynamic list of text inputs for managing arrays of strings. Supports adding, removing, and drag-and-drop reordering.
|
||||
232
gallery/src/pages/components/ha-input.ts
Normal file
232
gallery/src/pages/components/ha-input.ts
Normal file
@@ -0,0 +1,232 @@
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import { mdiMagnify } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import "../../../../src/components/input/ha-input";
|
||||
import "../../../../src/components/input/ha-input-copy";
|
||||
import "../../../../src/components/input/ha-input-multi";
|
||||
import "../../../../src/components/input/ha-input-search";
|
||||
import { localizeContext } from "../../../../src/data/context";
|
||||
|
||||
const LOCALIZE_KEYS: Record<string, string> = {
|
||||
"ui.common.copy": "Copy",
|
||||
"ui.common.show": "Show",
|
||||
"ui.common.hide": "Hide",
|
||||
"ui.common.add": "Add",
|
||||
"ui.common.remove": "Remove",
|
||||
"ui.common.search": "Search",
|
||||
"ui.common.copied_clipboard": "Copied to clipboard",
|
||||
};
|
||||
|
||||
@customElement("demo-components-ha-input")
|
||||
export class DemoHaInput extends LitElement {
|
||||
constructor() {
|
||||
super();
|
||||
// Provides localizeContext for ha-input-copy, ha-input-multi and ha-input-search
|
||||
// eslint-disable-next-line no-new
|
||||
new ContextProvider(this, {
|
||||
context: localizeContext,
|
||||
initialValue: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${["light", "dark"].map(
|
||||
(mode) => html`
|
||||
<div class=${mode}>
|
||||
<ha-card header="ha-input in ${mode}">
|
||||
<div class="card-content">
|
||||
<h3>Basic</h3>
|
||||
<div class="row">
|
||||
<ha-input label="Default"></ha-input>
|
||||
<ha-input label="With value" value="Hello"></ha-input>
|
||||
<ha-input
|
||||
label="With placeholder"
|
||||
placeholder="Type here..."
|
||||
></ha-input>
|
||||
</div>
|
||||
|
||||
<h3>Input types</h3>
|
||||
<div class="row">
|
||||
<ha-input label="Text" type="text" value="Text"></ha-input>
|
||||
<ha-input label="Number" type="number" value="42"></ha-input>
|
||||
<ha-input
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
></ha-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
label="Password"
|
||||
type="password"
|
||||
value="secret"
|
||||
password-toggle
|
||||
></ha-input>
|
||||
<ha-input label="URL" type="url" placeholder="https://...">
|
||||
</ha-input>
|
||||
<ha-input label="Date" type="date"></ha-input>
|
||||
</div>
|
||||
|
||||
<h3>States</h3>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
label="Disabled"
|
||||
disabled
|
||||
value="Disabled"
|
||||
></ha-input>
|
||||
<ha-input
|
||||
label="Readonly"
|
||||
readonly
|
||||
value="Readonly"
|
||||
></ha-input>
|
||||
<ha-input label="Required" required></ha-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
label="Invalid"
|
||||
invalid
|
||||
validation-message="This field is required"
|
||||
value=""
|
||||
></ha-input>
|
||||
<ha-input label="With hint" hint="This is a hint"></ha-input>
|
||||
<ha-input
|
||||
label="With clear"
|
||||
with-clear
|
||||
value="Clear me"
|
||||
></ha-input>
|
||||
</div>
|
||||
|
||||
<h3>With slots</h3>
|
||||
<div class="row">
|
||||
<ha-input label="With prefix">
|
||||
<span slot="start">$</span>
|
||||
</ha-input>
|
||||
<ha-input label="With suffix">
|
||||
<span slot="end">kg</span>
|
||||
</ha-input>
|
||||
<ha-input label="With icon">
|
||||
<ha-svg-icon .path=${mdiMagnify} slot="start"></ha-svg-icon>
|
||||
</ha-input>
|
||||
</div>
|
||||
|
||||
<h3>Appearance: outlined</h3>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
label="Outlined"
|
||||
value="Hello"
|
||||
></ha-input>
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
label="Outlined disabled"
|
||||
disabled
|
||||
value="Disabled"
|
||||
></ha-input>
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
label="Outlined invalid"
|
||||
invalid
|
||||
validation-message="Required"
|
||||
></ha-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<ha-input
|
||||
appearance="outlined"
|
||||
placeholder="Placeholder only"
|
||||
></ha-input>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="Derivatives in ${mode}">
|
||||
<div class="card-content">
|
||||
<h3>ha-input-search</h3>
|
||||
<ha-input-search label="Search label"></ha-input-search>
|
||||
<ha-input-search appearance="outlined"></ha-input-search>
|
||||
|
||||
<h3>ha-input-copy</h3>
|
||||
<ha-input-copy
|
||||
value="my-api-token-123"
|
||||
masked-value="••••••••••••••••••"
|
||||
masked-toggle
|
||||
></ha-input-copy>
|
||||
|
||||
<h3>ha-input-multi</h3>
|
||||
<ha-input-multi
|
||||
label="URL"
|
||||
add-label="Add URL"
|
||||
.value=${["https://example.com"]}
|
||||
></ha-input-multi>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
applyThemesOnElement(
|
||||
this.shadowRoot!.querySelector(".dark"),
|
||||
{
|
||||
default_theme: "default",
|
||||
default_dark_theme: "default",
|
||||
themes: {},
|
||||
darkMode: true,
|
||||
theme: "default",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.dark,
|
||||
.light {
|
||||
display: block;
|
||||
background-color: var(--primary-background-color);
|
||||
padding: 0 50px;
|
||||
}
|
||||
ha-card {
|
||||
margin: 24px auto;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-2);
|
||||
}
|
||||
h3 {
|
||||
margin: var(--ha-space-4) 0 var(--ha-space-1) 0;
|
||||
font-size: var(--ha-font-size-l);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
gap: var(--ha-space-4);
|
||||
}
|
||||
.row > * {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-components-ha-input": DemoHaInput;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
|
||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
|
||||
import type { HASSDomEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-selector/ha-selector";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
@@ -16,33 +17,59 @@ import type { BlueprintInput } from "../../../../src/data/blueprint";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
|
||||
import type { LabelRegistryEntry } from "../../../../src/data/label/label_registry";
|
||||
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import {
|
||||
showDialog,
|
||||
type ShowDialogParams,
|
||||
} from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/demo-black-white-row";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("media_player", "livingroom", "playing", {
|
||||
friendly_name: "Livingroom",
|
||||
}),
|
||||
getEntity("media_player", "lounge", "idle", {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
}),
|
||||
getEntity("light", "bedroom", "on", {
|
||||
friendly_name: "Bedroom",
|
||||
}),
|
||||
getEntity("switch", "coffee", "off", {
|
||||
friendly_name: "Coffee",
|
||||
}),
|
||||
getEntity("number", "number", 5, {
|
||||
friendly_name: "Number",
|
||||
}),
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.livingroom",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Livingroom",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.lounge",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Lounge",
|
||||
supported_features: 444983,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bedroom",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bedroom",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.coffee",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Coffee",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.number",
|
||||
state: "5",
|
||||
attributes: {
|
||||
friendly_name: "Number",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const DEVICES: DeviceRegistryEntry[] = [
|
||||
@@ -611,14 +638,15 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||
};
|
||||
};
|
||||
|
||||
private _dialogManager = (e) => {
|
||||
const { dialogTag, dialogImport, dialogParams, addHistory } = e.detail;
|
||||
private _dialogManager = (e: HASSDomEvent<ShowDialogParams<unknown>>) => {
|
||||
const { dialogTag, dialogImport, dialogParams, addHistory, parentElement } =
|
||||
e.detail;
|
||||
showDialog(
|
||||
this,
|
||||
this.shadowRoot!,
|
||||
dialogTag,
|
||||
dialogParams,
|
||||
dialogImport,
|
||||
parentElement,
|
||||
addHistory
|
||||
);
|
||||
};
|
||||
|
||||
@@ -28,9 +28,12 @@ This element is based on webawesome `wa-tooltip` it only sets some css tokens an
|
||||
|
||||
In your theme settings use this without the prefixed `--`.
|
||||
|
||||
- `--ha-tooltip-border-radius` (Default: 4px)
|
||||
- `--ha-tooltip-arrow-size` (Default: 8px)
|
||||
- `--wa-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
|
||||
- `--ha-tooltip-background-color` (Default: `var(--secondary-background-color)`)
|
||||
- `--ha-tooltip-text-color` (Default: `var(--primary-text-color)`)
|
||||
- `--ha-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
|
||||
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
|
||||
- `--wa-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
|
||||
- `--wa-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
|
||||
- `--ha-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
|
||||
- `--ha-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
|
||||
- `--ha-tooltip-padding` (Default: 8px)
|
||||
- `--ha-tooltip-border-radius` (Default: `var(--ha-border-radius-sm)`)
|
||||
- `--ha-tooltip-arrow-size` (Default: 8px)
|
||||
|
||||
@@ -19,7 +19,7 @@ The Home Assistant interface is based on Material Design. It's a design system c
|
||||
We want to make it as easy for designers to contribute as it is for developers. There’s a lot a designer can contribute to:
|
||||
|
||||
- Meet us at <a href="https://www.home-assistant.io/join-chat-design" rel="noopener noreferrer" target="_blank">Discord #designers channel</a>. If you can't see the channel, make sure you set the correct role in Channels & Roles.
|
||||
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
|
||||
- Start designing with our <a href="https://www.figma.com/design/2WGI8IDGyxINjSV6NRvPur/Home-Assistant-Design-Kit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
|
||||
- Find the latest UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
|
||||
|
||||
## Developers
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("alarm_control_panel", "alarm_armed", "armed_home", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("alarm_control_panel", "unavailable", "unavailable", {
|
||||
friendly_name: "Alarm",
|
||||
}),
|
||||
getEntity("alarm_control_panel", "alarm_code", "disarmed", {
|
||||
friendly_name: "Alarm",
|
||||
code_format: "number",
|
||||
}),
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm_armed",
|
||||
state: "armed_home",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.alarm_code",
|
||||
state: "disarmed",
|
||||
attributes: {
|
||||
friendly_name: "Alarm",
|
||||
code_format: "number",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,44 +1,79 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("switch", "bed_ac", "on", {
|
||||
friendly_name: "Ecobee",
|
||||
}),
|
||||
getEntity("sensor", "bed_temp", "72", {
|
||||
friendly_name: "Bedroom Temp",
|
||||
device_class: "temperature",
|
||||
unit_of_measurement: "°F",
|
||||
}),
|
||||
getEntity("light", "living_room_light", "off", {
|
||||
friendly_name: "Living Room Light",
|
||||
}),
|
||||
getEntity("fan", "living_room", "on", {
|
||||
friendly_name: "Living Room Fan",
|
||||
}),
|
||||
getEntity("sensor", "office_humidity", "73", {
|
||||
friendly_name: "Office Humidity",
|
||||
device_class: "humidity",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("light", "office", "on", {
|
||||
friendly_name: "Office Light",
|
||||
}),
|
||||
getEntity("fan", "kitchen", "on", {
|
||||
friendly_name: "Kitchen Fan",
|
||||
}),
|
||||
getEntity("binary_sensor", "kitchen_door", "on", {
|
||||
friendly_name: "Office Door",
|
||||
device_class: "door",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.bed_ac",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Ecobee",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.bed_temp",
|
||||
state: "72",
|
||||
attributes: {
|
||||
friendly_name: "Bedroom Temp",
|
||||
device_class: "temperature",
|
||||
unit_of_measurement: "°F",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.living_room_light",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Living Room Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.living_room",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Living Room Fan",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.office_humidity",
|
||||
state: "73",
|
||||
attributes: {
|
||||
friendly_name: "Office Humidity",
|
||||
device_class: "humidity",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.office",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Office Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Fan",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.kitchen_door",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Office Door",
|
||||
device_class: "door",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// TODO: Update image here
|
||||
|
||||
@@ -1,24 +1,39 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "controller_1", "on", {
|
||||
friendly_name: "Controller 1",
|
||||
}),
|
||||
getEntity("light", "controller_2", "on", {
|
||||
friendly_name: "Controller 2",
|
||||
}),
|
||||
getEntity("light", "floor", "off", {
|
||||
friendly_name: "Floor light",
|
||||
}),
|
||||
getEntity("light", "kitchen", "on", {
|
||||
friendly_name: "Kitchen light",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.controller_1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Controller 1",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.controller_2",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Controller 2",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.floor",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Floor light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen light",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,150 +1,257 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("group", "kitchen", "on", {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "Kitchen Group",
|
||||
}),
|
||||
getEntity("lock", "kitchen_door", "locked", {
|
||||
friendly_name: "Kitchen Lock",
|
||||
}),
|
||||
getEntity("cover", "kitchen_window", "open", {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
}),
|
||||
getEntity("scene", "romantic_lights", "scening", {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_paulus", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("climate", "ecobee", "auto", {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
operation_mode: "auto",
|
||||
operation_list: ["heat", "cool", "auto", "off"],
|
||||
hold_mode: "home",
|
||||
swing_mode: "Auto",
|
||||
swing_list: ["Auto", "1", "2", "3", "Off"],
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
}),
|
||||
getEntity("input_number", "number", 5, {
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Number",
|
||||
icon: "mdi:bell-ring",
|
||||
}),
|
||||
getEntity("input_boolean", "toggle", "on", {
|
||||
friendly_name: "Toggle",
|
||||
}),
|
||||
getEntity("input_datetime", "date_and_time", "2022-01-10 00:00:00", {
|
||||
has_date: true,
|
||||
has_time: true,
|
||||
editable: true,
|
||||
year: 2022,
|
||||
month: 1,
|
||||
day: 10,
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
timestamp: 1641801600,
|
||||
friendly_name: "Date and Time",
|
||||
}),
|
||||
getEntity("sensor", "humidity", "23.2", {
|
||||
friendly_name: "Humidity",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("input_select", "dropdown", "Soda", {
|
||||
friendly_name: "Dropdown",
|
||||
options: ["Soda", "Beer", "Wine"],
|
||||
}),
|
||||
getEntity("input_text", "text", "Inspiration", {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
}),
|
||||
getEntity("timer", "timer", "idle", {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
}),
|
||||
getEntity("counter", "counter", "3", {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
step: 1,
|
||||
minimum: 0,
|
||||
maximum: 10,
|
||||
}),
|
||||
getEntity("text", "message", "Hello!", {
|
||||
friendly_name: "Message",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "group.kitchen",
|
||||
state: "on",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "Kitchen Group",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.kitchen_door",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lock",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.kitchen_window",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "scene.romantic_lights",
|
||||
state: "scening",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.ecobee",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
operation_mode: "auto",
|
||||
operation_list: ["heat", "cool", "auto", "off"],
|
||||
hold_mode: "home",
|
||||
swing_mode: "Auto",
|
||||
swing_list: ["Auto", "1", "2", "3", "Off"],
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.number",
|
||||
state: "5",
|
||||
attributes: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Number",
|
||||
icon: "mdi:bell-ring",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_boolean.toggle",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Toggle",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_datetime.date_and_time",
|
||||
state: "2022-01-10 00:00:00",
|
||||
attributes: {
|
||||
has_date: true,
|
||||
has_time: true,
|
||||
editable: true,
|
||||
year: 2022,
|
||||
month: 1,
|
||||
day: 10,
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0,
|
||||
timestamp: 1641801600,
|
||||
friendly_name: "Date and Time",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.humidity",
|
||||
state: "23.2",
|
||||
attributes: {
|
||||
friendly_name: "Humidity",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_select.dropdown",
|
||||
state: "Soda",
|
||||
attributes: {
|
||||
friendly_name: "Dropdown",
|
||||
options: ["Soda", "Beer", "Wine"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_text.text",
|
||||
state: "Inspiration",
|
||||
attributes: {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "timer.timer",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "counter.counter",
|
||||
state: "3",
|
||||
attributes: {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
step: 1,
|
||||
minimum: 0,
|
||||
maximum: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "text.message",
|
||||
state: "Hello!",
|
||||
attributes: {
|
||||
friendly_name: "Message",
|
||||
},
|
||||
},
|
||||
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("lock", "unavailable", "unavailable", {
|
||||
friendly_name: "Kitchen Door",
|
||||
}),
|
||||
getEntity("cover", "unavailable", "unavailable", {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
}),
|
||||
getEntity("scene", "unavailable", "unavailable", {
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("device_tracker", "unavailable", "unavailable", {
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("climate", "unavailable", "unavailable", {
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
}),
|
||||
getEntity("input_number", "unavailable", "unavailable", {
|
||||
friendly_name: "Allowed Noise",
|
||||
icon: "mdi:bell-ring",
|
||||
}),
|
||||
getEntity("input_select", "unavailable", "unavailable", {
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Who cooks",
|
||||
icon: "mdi:cheff",
|
||||
}),
|
||||
getEntity("text", "unavailable", "unavailable", {
|
||||
friendly_name: "Message",
|
||||
}),
|
||||
getEntity("event", "unavailable", "unavailable", {
|
||||
friendly_name: "Empty remote",
|
||||
}),
|
||||
getEntity("event", "doorbell", "2023-07-17T21:26:11.615+00:00", {
|
||||
friendly_name: "Doorbell",
|
||||
device_class: "doorbell",
|
||||
event_type: "Ding-Dong",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Door",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "scene.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
unit_of_measurement: "°F",
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Allowed Noise",
|
||||
icon: "mdi:bell-ring",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_select.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
unit_of_measurement: "dB",
|
||||
friendly_name: "Who cooks",
|
||||
icon: "mdi:cheff",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "text.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Message",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "event.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Empty remote",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "event.doorbell",
|
||||
state: "2023-07-17T21:26:11.615+00:00",
|
||||
attributes: {
|
||||
friendly_name: "Doorbell",
|
||||
device_class: "doorbell",
|
||||
event_type: "Ding-Dong",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,74 +1,117 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "work", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 25,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_anne_therese", "school", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 50,
|
||||
friendly_name: "Anne Therese",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_home_boy", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 75,
|
||||
friendly_name: "Home Boy",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "ceiling_lights", "off", {
|
||||
friendly_name: "Ceiling Lights",
|
||||
}),
|
||||
getEntity("sensor", "battery_1", 20, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 1",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "battery_2", 35, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 2",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "battery_3", 40, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 3",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "battery_4", 80, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 4",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("input_number", "min_battery_level", 30, {
|
||||
mode: "slider",
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
icon: "mdi:battery-alert-variant",
|
||||
friendly_name: "Minimum Battery Level",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "work",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 25,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_anne_therese",
|
||||
state: "school",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 50,
|
||||
friendly_name: "Anne Therese",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_home_boy",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 75,
|
||||
friendly_name: "Home Boy",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.ceiling_lights",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_1",
|
||||
state: "20",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 1",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_2",
|
||||
state: "35",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 2",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_3",
|
||||
state: "40",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 3",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery_4",
|
||||
state: "80",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery 4",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.min_battery_level",
|
||||
state: "30",
|
||||
attributes: {
|
||||
mode: "slider",
|
||||
step: 10,
|
||||
min: 0,
|
||||
max: 100,
|
||||
icon: "mdi:battery-alert-variant",
|
||||
friendly_name: "Minimum Battery Level",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("sensor", "brightness", "12", {}),
|
||||
getEntity("sensor", "brightness_medium", "53", {}),
|
||||
getEntity("sensor", "brightness_high", "87", {}),
|
||||
getEntity("plant", "bonsai", "ok", {}),
|
||||
getEntity("sensor", "not_working", "unavailable", {}),
|
||||
getEntity("sensor", "outside_humidity", "54", {
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
getEntity("sensor", "outside_temperature", "15.6", {
|
||||
unit_of_measurement: "°C",
|
||||
}),
|
||||
{ entity_id: "sensor.brightness", state: "12", attributes: {} },
|
||||
{ entity_id: "sensor.brightness_medium", state: "53", attributes: {} },
|
||||
{ entity_id: "sensor.brightness_high", state: "87", attributes: {} },
|
||||
{ entity_id: "plant.bonsai", state: "ok", attributes: {} },
|
||||
{ entity_id: "sensor.not_working", state: "unavailable", attributes: {} },
|
||||
{
|
||||
entity_id: "sensor.outside_humidity",
|
||||
state: "54",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.outside_temperature",
|
||||
state: "15.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "°C",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
@@ -47,6 +54,19 @@ const CONFIGS = [
|
||||
needle: true
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Rendering needle and severity levels",
|
||||
config: `
|
||||
- type: gauge
|
||||
entity: sensor.brightness_high
|
||||
name: Brightness High
|
||||
needle: true
|
||||
severity:
|
||||
red: 75
|
||||
green: 0
|
||||
yellow: 50
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Setting severity levels",
|
||||
config: `
|
||||
|
||||
@@ -1,62 +1,89 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("media_player", "living_room", "playing", {
|
||||
volume_level: 1,
|
||||
is_volume_muted: false,
|
||||
media_content_id: "eyU3bRy2x44",
|
||||
media_content_type: "movie",
|
||||
media_duration: 300,
|
||||
media_position: 45.017773,
|
||||
media_position_updated_at: "2018-07-19T10:44:45.919514+00:00",
|
||||
media_title: "♥♥ The Best Fireplace Video (3 hours)",
|
||||
app_name: "YouTube",
|
||||
sound_mode: "Dummy Music",
|
||||
sound_mode_list: ["Dummy Music", "Dummy Movie"],
|
||||
shuffle: false,
|
||||
friendly_name: "Living Room",
|
||||
entity_picture:
|
||||
"/api/media_player_proxy/media_player.living_room?token=e925f8db7f7bd1f317e4524dcb8333d60f6019219a3799a22604b5787f243567&cache=bc2ffb49c4f67034",
|
||||
supported_features: 115597,
|
||||
}),
|
||||
getEntity("sun", "sun", "below_horizon", {
|
||||
next_dawn: "2018-07-19T20:48:47+00:00",
|
||||
next_dusk: "2018-07-20T11:46:06+00:00",
|
||||
next_midnight: "2018-07-19T16:17:28+00:00",
|
||||
next_noon: "2018-07-20T04:17:26+00:00",
|
||||
next_rising: "2018-07-19T21:16:31+00:00",
|
||||
next_setting: "2018-07-20T11:18:22+00:00",
|
||||
elevation: 67.69,
|
||||
azimuth: 338.55,
|
||||
friendly_name: "Sun",
|
||||
}),
|
||||
getEntity("cover", "kitchen_window", "open", {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
}),
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "ceiling_lights", "off", {
|
||||
friendly_name: "Ceiling Lights",
|
||||
}),
|
||||
getEntity("lock", "kitchen_door", "locked", {
|
||||
friendly_name: "Kitchen Door",
|
||||
}),
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.living_room",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
volume_level: 1,
|
||||
is_volume_muted: false,
|
||||
media_content_id: "eyU3bRy2x44",
|
||||
media_content_type: "movie",
|
||||
media_duration: 300,
|
||||
media_position: 45.017773,
|
||||
media_position_updated_at: "2018-07-19T10:44:45.919514+00:00",
|
||||
media_title: "♥♥ The Best Fireplace Video (3 hours)",
|
||||
app_name: "YouTube",
|
||||
sound_mode: "Dummy Music",
|
||||
sound_mode_list: ["Dummy Music", "Dummy Movie"],
|
||||
shuffle: false,
|
||||
friendly_name: "Living Room",
|
||||
entity_picture:
|
||||
"/api/media_player_proxy/media_player.living_room?token=e925f8db7f7bd1f317e4524dcb8333d60f6019219a3799a22604b5787f243567&cache=bc2ffb49c4f67034",
|
||||
supported_features: 115597,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sun.sun",
|
||||
state: "below_horizon",
|
||||
attributes: {
|
||||
next_dawn: "2018-07-19T20:48:47+00:00",
|
||||
next_dusk: "2018-07-20T11:46:06+00:00",
|
||||
next_midnight: "2018-07-19T16:17:28+00:00",
|
||||
next_noon: "2018-07-20T04:17:26+00:00",
|
||||
next_rising: "2018-07-19T21:16:31+00:00",
|
||||
next_setting: "2018-07-20T11:18:22+00:00",
|
||||
elevation: 67.69,
|
||||
azimuth: 338.55,
|
||||
friendly_name: "Sun",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.kitchen_window",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Window",
|
||||
supported_features: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.ceiling_lights",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.kitchen_door",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Door",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -2,46 +2,69 @@ import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Lights",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_paulus", "work", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_anne_therese", "school", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Anne Therese",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_home_boy", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Home Boy",
|
||||
}),
|
||||
getEntity("sensor", "illumination", "23", {
|
||||
friendly_name: "Illumination",
|
||||
unit_of_measurement: "lx",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "work",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_anne_therese",
|
||||
state: "school",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Anne Therese",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_home_boy",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Home Boy",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.illumination",
|
||||
state: "23",
|
||||
attributes: {
|
||||
friendly_name: "Illumination",
|
||||
unit_of_measurement: "lx",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,29 +1,44 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
brightness: 255,
|
||||
}),
|
||||
getEntity("light", "dim_on", "on", {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
brightness: 100,
|
||||
}),
|
||||
getEntity("light", "dim_off", "off", {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
}),
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Lost Light",
|
||||
supported_features: 1,
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
brightness: 255,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.dim_on",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
brightness: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.dim_off",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Dining Room",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Lost Light",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,59 +1,86 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "not_home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("device_tracker", "demo_home_boy", "home", {
|
||||
source_type: "gps",
|
||||
latitude: 32.87334,
|
||||
longitude: 117.22745,
|
||||
gps_accuracy: 20,
|
||||
battery: 53,
|
||||
friendly_name: "Home Boy",
|
||||
}),
|
||||
getEntity("zone", "home", "zoning", {
|
||||
latitude: 32.87354,
|
||||
longitude: 117.22765,
|
||||
radius: 100,
|
||||
friendly_name: "Home",
|
||||
icon: "mdi:home",
|
||||
}),
|
||||
getEntity("zone", "bushfire", "zoning", {
|
||||
latitude: -33.8611,
|
||||
longitude: 151.203,
|
||||
radius: 35000,
|
||||
friendly_name: "Bushfire Zone",
|
||||
icon: "mdi:home",
|
||||
}),
|
||||
getEntity("geo_location", "nelsons_creek", "15", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -34.07792,
|
||||
longitude: 151.03219,
|
||||
friendly_name: "Nelsons Creek",
|
||||
}),
|
||||
getEntity("geo_location", "forest_rd_nowra_hill", "8", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.69452,
|
||||
longitude: 151.19577,
|
||||
friendly_name: "Forest Rd, Nowra Hill",
|
||||
}),
|
||||
getEntity("geo_location", "stoney_ridge_rd_kremnos", "20", {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.66584,
|
||||
longitude: 150.97209,
|
||||
friendly_name: "Stoney Ridge Rd, Kremnos",
|
||||
}),
|
||||
{
|
||||
entity_id: "device_tracker.demo_paulus",
|
||||
state: "not_home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.877105,
|
||||
longitude: 117.232185,
|
||||
gps_accuracy: 91,
|
||||
battery: 71,
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "device_tracker.demo_home_boy",
|
||||
state: "home",
|
||||
attributes: {
|
||||
source_type: "gps",
|
||||
latitude: 32.87334,
|
||||
longitude: 117.22745,
|
||||
gps_accuracy: 20,
|
||||
battery: 53,
|
||||
friendly_name: "Home Boy",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "zoning",
|
||||
attributes: {
|
||||
latitude: 32.87354,
|
||||
longitude: 117.22765,
|
||||
radius: 100,
|
||||
friendly_name: "Home",
|
||||
icon: "mdi:home",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "zone.bushfire",
|
||||
state: "zoning",
|
||||
attributes: {
|
||||
latitude: -33.8611,
|
||||
longitude: 151.203,
|
||||
radius: 35000,
|
||||
friendly_name: "Bushfire Zone",
|
||||
icon: "mdi:home",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "geo_location.nelsons_creek",
|
||||
state: "15",
|
||||
attributes: {
|
||||
source: "bushfire_demo",
|
||||
latitude: -34.07792,
|
||||
longitude: 151.03219,
|
||||
friendly_name: "Nelsons Creek",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "geo_location.forest_rd_nowra_hill",
|
||||
state: "8",
|
||||
attributes: {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.69452,
|
||||
longitude: 151.19577,
|
||||
friendly_name: "Forest Rd, Nowra Hill",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "geo_location.stoney_ridge_rd_kremnos",
|
||||
state: "20",
|
||||
attributes: {
|
||||
source: "bushfire_demo",
|
||||
latitude: -33.66584,
|
||||
longitude: 150.97209,
|
||||
friendly_name: "Stoney Ridge Rd, Kremnos",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,40 +1,63 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("group", "all_lights", "on", {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "All Lights",
|
||||
}),
|
||||
getEntity("camera", "demo_camera", "idle", {
|
||||
access_token:
|
||||
"2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
friendly_name: "Demo camera",
|
||||
entity_picture:
|
||||
"/api/camera_proxy/camera.demo_camera?token=2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
}),
|
||||
getEntity("binary_sensor", "movement_backyard", "on", {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "motion",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
getEntity("sensor", "battery", 35, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "group.all_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light"],
|
||||
order: 8,
|
||||
friendly_name: "All Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "camera.demo_camera",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
access_token:
|
||||
"2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
friendly_name: "Demo camera",
|
||||
entity_picture:
|
||||
"/api/camera_proxy/camera.demo_camera?token=2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.movement_backyard",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "motion",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery",
|
||||
state: "35",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
friendly_name: "Kitchen Lights",
|
||||
}),
|
||||
getEntity("light", "bed_light", "off", {
|
||||
friendly_name: "Bed Light",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
{
|
||||
entity_id: "light.kitchen_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,35 +1,58 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("switch", "decorative_lights", "on", {
|
||||
friendly_name: "Decorative Lights",
|
||||
}),
|
||||
getEntity("light", "ceiling_lights", "on", {
|
||||
friendly_name: "Ceiling Lights",
|
||||
}),
|
||||
getEntity("binary_sensor", "movement_backyard", "on", {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "moving",
|
||||
}),
|
||||
getEntity("binary_sensor", "basement_floor_wet", "off", {
|
||||
friendly_name: "Basement Floor Wet",
|
||||
device_class: "moisture",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
}),
|
||||
getEntity("sensor", "battery", 35, {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
}),
|
||||
{
|
||||
entity_id: "switch.decorative_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Decorative Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.ceiling_lights",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling Lights",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.movement_backyard",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Movement Backyard",
|
||||
device_class: "moving",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.basement_floor_wet",
|
||||
state: "off",
|
||||
attributes: {
|
||||
friendly_name: "Basement Floor Wet",
|
||||
device_class: "moisture",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
entity_picture: "/images/paulus.jpg",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.battery",
|
||||
state: "35",
|
||||
attributes: {
|
||||
device_class: "battery",
|
||||
friendly_name: "Battery",
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -1,100 +1,123 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("climate", "ecobee", "auto", {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 59,
|
||||
preset_mode: "eco",
|
||||
preset_modes: ["away", "eco"],
|
||||
}),
|
||||
getEntity("climate", "nest", "heat", {
|
||||
current_temperature: 17,
|
||||
min_temp: 15,
|
||||
max_temp: 25,
|
||||
temperature: 19,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Nest",
|
||||
supported_features: 43,
|
||||
}),
|
||||
getEntity("climate", "overkiz_radiator", "heat", {
|
||||
current_temperature: 18,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
temperature: 20,
|
||||
hvac_modes: ["heat", "auto", "off"],
|
||||
friendly_name: "Overkiz radiator",
|
||||
supported_features: 17,
|
||||
preset_mode: "comfort",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
"auto",
|
||||
"boost",
|
||||
"external",
|
||||
"prog",
|
||||
],
|
||||
}),
|
||||
getEntity("climate", "overkiz_towel_dryer", "heat", {
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
hvac_modes: ["heat", "off"],
|
||||
friendly_name: "Overkiz towel dryer",
|
||||
supported_features: 16,
|
||||
preset_mode: "eco",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
}),
|
||||
getEntity("climate", "sensibo", "fan_only", {
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
friendly_name: "Sensibo purifier",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
swing_modes: ["both", "rangefull", "off"],
|
||||
swing_mode: "rangefull",
|
||||
swing_horizontal_modes: ["both", "rangefull", "off"],
|
||||
swing_horizontal_mode: "both",
|
||||
supported_features: 553,
|
||||
}),
|
||||
getEntity("climate", "unavailable", "unavailable", {
|
||||
supported_features: 43,
|
||||
}),
|
||||
{
|
||||
entity_id: "climate.ecobee",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: null,
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 59,
|
||||
preset_mode: "eco",
|
||||
preset_modes: ["away", "eco"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.nest",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: 17,
|
||||
min_temp: 15,
|
||||
max_temp: 25,
|
||||
temperature: 19,
|
||||
fan_mode: "Auto Low",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Nest",
|
||||
supported_features: 43,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.overkiz_radiator",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: 18,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
temperature: 20,
|
||||
hvac_modes: ["heat", "auto", "off"],
|
||||
friendly_name: "Overkiz radiator",
|
||||
supported_features: 17,
|
||||
preset_mode: "comfort",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
"auto",
|
||||
"boost",
|
||||
"external",
|
||||
"prog",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.overkiz_towel_dryer",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
hvac_modes: ["heat", "off"],
|
||||
friendly_name: "Overkiz towel dryer",
|
||||
supported_features: 16,
|
||||
preset_mode: "eco",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.sensibo",
|
||||
state: "fan_only",
|
||||
attributes: {
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
friendly_name: "Sensibo purifier",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
swing_modes: ["both", "rangefull", "off"],
|
||||
swing_mode: "rangefull",
|
||||
swing_horizontal_modes: ["both", "rangefull", "off"],
|
||||
swing_horizontal_mode: "both",
|
||||
supported_features: 553,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
supported_features: 43,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -6,7 +6,6 @@ import { LightColorMode } from "../../../../src/data/light";
|
||||
import { LockEntityFeature } from "../../../../src/data/lock";
|
||||
import { MediaPlayerEntityFeature } from "../../../../src/data/media-player";
|
||||
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
@@ -14,102 +13,154 @@ import { ClimateEntityFeature } from "../../../../src/data/climate";
|
||||
import { FanEntityFeature } from "../../../../src/data/fan";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("switch", "tv_outlet", "on", {
|
||||
friendly_name: "TV outlet",
|
||||
device_class: "outlet",
|
||||
}),
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Bed Light",
|
||||
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
|
||||
}),
|
||||
getEntity("light", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable entity",
|
||||
}),
|
||||
getEntity("lock", "front_door", "locked", {
|
||||
friendly_name: "Front Door Lock",
|
||||
device_class: "lock",
|
||||
supported_features: LockEntityFeature.OPEN,
|
||||
}),
|
||||
getEntity("media_player", "living_room", "playing", {
|
||||
friendly_name: "Living room speaker",
|
||||
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
|
||||
}),
|
||||
getEntity("climate", "thermostat", "heat", {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: 80,
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
friendly_name: "Thermostat",
|
||||
hvac_action: "heating",
|
||||
}),
|
||||
getEntity("person", "paulus", "home", {
|
||||
friendly_name: "Paulus",
|
||||
}),
|
||||
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
}),
|
||||
getEntity("cover", "kitchen_shutter", "open", {
|
||||
friendly_name: "Kitchen shutter",
|
||||
device_class: "shutter",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP,
|
||||
}),
|
||||
getEntity("cover", "pergola_roof", "open", {
|
||||
friendly_name: "Pergola Roof",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT,
|
||||
}),
|
||||
getEntity("input_number", "counter", "1.0", {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
}),
|
||||
getEntity("climate", "dual_thermostat", "heat/cool", {
|
||||
friendly_name: "Dual thermostat",
|
||||
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
|
||||
preset_modes: ["home", "eco", "away"],
|
||||
swing_modes: ["auto", "1", "2", "3", "off"],
|
||||
switch_horizontal_modes: ["auto", "4", "5", "6", "off"],
|
||||
current_temperature: 23,
|
||||
target_temp_high: 24,
|
||||
target_temp_low: 21,
|
||||
fan_mode: "auto_low",
|
||||
preset_mode: "home",
|
||||
swing_mode: "auto",
|
||||
swing_horizontal_mode: "off",
|
||||
supported_features:
|
||||
ClimateEntityFeature.TURN_ON +
|
||||
ClimateEntityFeature.TURN_OFF +
|
||||
ClimateEntityFeature.SWING_MODE +
|
||||
ClimateEntityFeature.SWING_HORIZONTAL_MODE +
|
||||
ClimateEntityFeature.PRESET_MODE +
|
||||
ClimateEntityFeature.FAN_MODE +
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
}),
|
||||
getEntity("fan", "fan_demo", "on", {
|
||||
friendly_name: "Ceiling fan",
|
||||
device_class: "fan",
|
||||
direction: "reverse",
|
||||
supported_features:
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED +
|
||||
FanEntityFeature.OSCILLATE,
|
||||
}),
|
||||
{
|
||||
entity_id: "switch.tv_outlet",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "TV outlet",
|
||||
device_class: "outlet",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Bed Light",
|
||||
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable entity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.front_door",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Front Door Lock",
|
||||
device_class: "lock",
|
||||
supported_features: LockEntityFeature.OPEN,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.living_room",
|
||||
state: "playing",
|
||||
attributes: {
|
||||
friendly_name: "Living room speaker",
|
||||
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.thermostat",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
current_temperature: 73,
|
||||
min_temp: 45,
|
||||
max_temp: 95,
|
||||
temperature: 80,
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
friendly_name: "Thermostat",
|
||||
hvac_action: "heating",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "person.paulus",
|
||||
state: "home",
|
||||
attributes: {
|
||||
friendly_name: "Paulus",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "vacuum.first_floor_vacuum",
|
||||
state: "docked",
|
||||
attributes: {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.kitchen_shutter",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Kitchen shutter",
|
||||
device_class: "shutter",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.pergola_roof",
|
||||
state: "open",
|
||||
attributes: {
|
||||
friendly_name: "Pergola Roof",
|
||||
supported_features:
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.counter",
|
||||
state: "1.0",
|
||||
attributes: {
|
||||
friendly_name: "Counter",
|
||||
initial: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
mode: "slider",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.dual_thermostat",
|
||||
state: "heat/cool",
|
||||
attributes: {
|
||||
friendly_name: "Dual thermostat",
|
||||
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
|
||||
preset_modes: ["home", "eco", "away"],
|
||||
swing_modes: ["auto", "1", "2", "3", "off"],
|
||||
switch_horizontal_modes: ["auto", "4", "5", "6", "off"],
|
||||
current_temperature: 23,
|
||||
target_temp_high: 24,
|
||||
target_temp_low: 21,
|
||||
fan_mode: "auto_low",
|
||||
preset_mode: "home",
|
||||
swing_mode: "auto",
|
||||
swing_horizontal_mode: "off",
|
||||
supported_features:
|
||||
ClimateEntityFeature.TURN_ON +
|
||||
ClimateEntityFeature.TURN_OFF +
|
||||
ClimateEntityFeature.SWING_MODE +
|
||||
ClimateEntityFeature.SWING_HORIZONTAL_MODE +
|
||||
ClimateEntityFeature.PRESET_MODE +
|
||||
ClimateEntityFeature.FAN_MODE +
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.fan_demo",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Ceiling fan",
|
||||
device_class: "fan",
|
||||
direction: "reverse",
|
||||
supported_features:
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED +
|
||||
FanEntityFeature.OSCILLATE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -3,18 +3,25 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
import { mockTodo } from "../../../../demo/src/stubs/todo";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("todo", "shopping_list", "2", {
|
||||
friendly_name: "Shopping List",
|
||||
supported_features: 15,
|
||||
}),
|
||||
getEntity("todo", "read_only", "2", {
|
||||
friendly_name: "Read only",
|
||||
}),
|
||||
{
|
||||
entity_id: "todo.shopping_list",
|
||||
state: "2",
|
||||
attributes: {
|
||||
friendly_name: "Shopping List",
|
||||
supported_features: 15,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "todo.read_only",
|
||||
state: "2",
|
||||
attributes: {
|
||||
friendly_name: "Read only",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CONFIGS = [
|
||||
|
||||
@@ -3,108 +3,135 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { ClimateEntityFeature } from "../../../../src/data/climate";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("climate", "radiator", "heat", {
|
||||
friendly_name: "Basic heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
}),
|
||||
getEntity("climate", "ac", "cool", {
|
||||
friendly_name: "Basic air conditioning",
|
||||
hvac_modes: ["cool", "off"],
|
||||
hvac_mode: "cool",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
}),
|
||||
getEntity("climate", "fan", "fan_only", {
|
||||
friendly_name: "Basic fan",
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
hvac_mode: "fan_only",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
|
||||
}),
|
||||
getEntity("climate", "hvac", "auto", {
|
||||
friendly_name: "Basic hvac",
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
}),
|
||||
getEntity("climate", "advanced", "auto", {
|
||||
friendly_name: "Advanced hvac",
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE |
|
||||
ClimateEntityFeature.TARGET_HUMIDITY |
|
||||
ClimateEntityFeature.PRESET_MODE,
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
preset_modes: ["eco", "comfort", "boost"],
|
||||
preset_mode: "eco",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
current_humidity: 40,
|
||||
min_humidity: 0,
|
||||
max_humidity: 100,
|
||||
humidity: 50,
|
||||
}),
|
||||
getEntity("climate", "towel_dryer", "heat", {
|
||||
friendly_name: "Preset only heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
preset_mode: "eco",
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
supported_features: ClimateEntityFeature.PRESET_MODE,
|
||||
}),
|
||||
getEntity("climate", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
}),
|
||||
{
|
||||
entity_id: "climate.radiator",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
friendly_name: "Basic heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.ac",
|
||||
state: "cool",
|
||||
attributes: {
|
||||
friendly_name: "Basic air conditioning",
|
||||
hvac_modes: ["cool", "off"],
|
||||
hvac_mode: "cool",
|
||||
current_temperature: 18,
|
||||
temperature: 20,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.fan",
|
||||
state: "fan_only",
|
||||
attributes: {
|
||||
friendly_name: "Basic fan",
|
||||
hvac_modes: ["fan_only", "off"],
|
||||
hvac_mode: "fan_only",
|
||||
fan_modes: ["low", "high"],
|
||||
fan_mode: "low",
|
||||
current_temperature: null,
|
||||
temperature: null,
|
||||
min_temp: 0,
|
||||
max_temp: 1,
|
||||
target_temp_step: 1,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.hvac",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
friendly_name: "Basic hvac",
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.advanced",
|
||||
state: "auto",
|
||||
attributes: {
|
||||
friendly_name: "Advanced hvac",
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE |
|
||||
ClimateEntityFeature.TARGET_HUMIDITY |
|
||||
ClimateEntityFeature.PRESET_MODE,
|
||||
hvac_modes: ["auto", "off"],
|
||||
hvac_mode: "auto",
|
||||
preset_modes: ["eco", "comfort", "boost"],
|
||||
preset_mode: "eco",
|
||||
current_temperature: 18,
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
target_temp_step: 1,
|
||||
target_temp_low: 20,
|
||||
target_temp_high: 25,
|
||||
current_humidity: 40,
|
||||
min_humidity: 0,
|
||||
max_humidity: 100,
|
||||
humidity: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.towel_dryer",
|
||||
state: "heat",
|
||||
attributes: {
|
||||
friendly_name: "Preset only heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
preset_modes: [
|
||||
"none",
|
||||
"frost_protection",
|
||||
"eco",
|
||||
"comfort",
|
||||
"comfort-1",
|
||||
"comfort-2",
|
||||
],
|
||||
preset_mode: "eco",
|
||||
current_temperature: null,
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
supported_features: ClimateEntityFeature.PRESET_MODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable heater",
|
||||
hvac_modes: ["heat", "off"],
|
||||
hvac_mode: "heat",
|
||||
min_temp: 10,
|
||||
max_temp: 30,
|
||||
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-climate")
|
||||
@@ -117,7 +144,7 @@ class DemoMoreInfoClimate extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,138 +4,189 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { CoverEntityFeature } from "../../../../src/data/cover";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("cover", "position_buttons", "on", {
|
||||
friendly_name: "Position Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE,
|
||||
}),
|
||||
getEntity("cover", "position_slider_half", "on", {
|
||||
friendly_name: "Position Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 50,
|
||||
}),
|
||||
getEntity("cover", "position_slider_open", "on", {
|
||||
friendly_name: "Position Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 100,
|
||||
}),
|
||||
getEntity("cover", "position_slider_closed", "on", {
|
||||
friendly_name: "Position Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 0,
|
||||
}),
|
||||
getEntity("cover", "tilt_buttons", "on", {
|
||||
friendly_name: "Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
}),
|
||||
getEntity("cover", "tilt_slider_half", "on", {
|
||||
friendly_name: "Tilt Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 50,
|
||||
}),
|
||||
getEntity("cover", "tilt_slider_open", "on", {
|
||||
friendly_name: "Tilt Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 100,
|
||||
}),
|
||||
getEntity("cover", "tilt_slider_closed", "on", {
|
||||
friendly_name: "Tilt Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 0,
|
||||
}),
|
||||
getEntity("cover", "position_slider_tilt_slider", "on", {
|
||||
friendly_name: "Both Sliders",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
}),
|
||||
getEntity("cover", "position_tilt_slider", "on", {
|
||||
friendly_name: "Position & Tilt Slider",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 70,
|
||||
}),
|
||||
getEntity("cover", "position_slider_tilt", "on", {
|
||||
friendly_name: "Position Slider & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
}),
|
||||
getEntity("cover", "position_slider_only_tilt_slider", "on", {
|
||||
friendly_name: "Position Slider Only & Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
}),
|
||||
getEntity("cover", "position_slider_only_tilt", "on", {
|
||||
friendly_name: "Position Slider Only & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
}),
|
||||
{
|
||||
entity_id: "cover.position_buttons",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_half",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_open",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_closed",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION,
|
||||
current_position: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_buttons",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_slider_half",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Half-Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_slider_open",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Open",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.tilt_slider_closed",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Tilt Closed",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_tilt_slider",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Both Sliders",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_tilt_slider",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position & Tilt Slider",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_tilt_position: 70,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_tilt",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Slider & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.OPEN +
|
||||
CoverEntityFeature.STOP +
|
||||
CoverEntityFeature.CLOSE +
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_only_tilt_slider",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Slider Only & Tilt Buttons",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT,
|
||||
current_position: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.position_slider_only_tilt",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Position Slider Only & Tilt",
|
||||
supported_features:
|
||||
CoverEntityFeature.SET_POSITION +
|
||||
CoverEntityFeature.OPEN_TILT +
|
||||
CoverEntityFeature.STOP_TILT +
|
||||
CoverEntityFeature.CLOSE_TILT +
|
||||
CoverEntityFeature.SET_TILT_POSITION,
|
||||
current_position: 30,
|
||||
current_tilt_position: 70,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-cover")
|
||||
@@ -148,7 +199,7 @@ class DemoMoreInfoCover extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,21 +3,24 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { FanEntityFeature } from "../../../../src/data/fan";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("fan", "fan", "on", {
|
||||
friendly_name: "Fan",
|
||||
device_class: "fan",
|
||||
supported_features:
|
||||
FanEntityFeature.OSCILLATE +
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED,
|
||||
}),
|
||||
{
|
||||
entity_id: "fan.fan",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Fan",
|
||||
device_class: "fan",
|
||||
supported_features:
|
||||
FanEntityFeature.OSCILLATE +
|
||||
FanEntityFeature.DIRECTION +
|
||||
FanEntityFeature.SET_SPEED,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-fan")
|
||||
@@ -30,7 +33,7 @@ class DemoMoreInfoFan extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,27 +3,38 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("humidifier", "humidifier", "on", {
|
||||
friendly_name: "Humidifier",
|
||||
device_class: "humidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
}),
|
||||
getEntity("humidifier", "dehumidifier", "on", {
|
||||
friendly_name: "Dehumidifier",
|
||||
device_class: "dehumidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
}),
|
||||
getEntity("humidifier", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable humidifier",
|
||||
}),
|
||||
{
|
||||
entity_id: "humidifier.humidifier",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Humidifier",
|
||||
device_class: "humidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "humidifier.dehumidifier",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Dehumidifier",
|
||||
device_class: "dehumidifier",
|
||||
current_humidity: 50,
|
||||
humidity: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "humidifier.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable humidifier",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-humidifier")
|
||||
@@ -36,7 +47,7 @@ class DemoMoreInfoHumidifier extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,30 +3,37 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("input_number", "box1", 0, {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("input_number", "slider1", 0, {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
{
|
||||
entity_id: "input_number.box1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "input_number.slider1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-input-number")
|
||||
@@ -39,7 +46,7 @@ class DemoMoreInfoInputNumber extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("input_text", "text", "Inspiration", {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
}),
|
||||
{
|
||||
entity_id: "input_text.text",
|
||||
state: "Inspiration",
|
||||
attributes: {
|
||||
friendly_name: "Text",
|
||||
mode: "text",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-input-text")
|
||||
@@ -25,7 +28,7 @@ class DemoMoreInfoInputText extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,137 +4,172 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { LightColorMode, LightEntityFeature } from "../../../../src/data/light";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
friendly_name: "Basic Light",
|
||||
}),
|
||||
getEntity("light", "kitchen_light", "on", {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS],
|
||||
color_mode: LightColorMode.BRIGHTNESS,
|
||||
}),
|
||||
getEntity("light", "color_temperature_light", "on", {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
brightness: 128,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
}),
|
||||
getEntity("light", "color_hs_light", "on", {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.HS,
|
||||
],
|
||||
color_mode: LightColorMode.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgb_ct_light", "on", {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGB,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_RGB_light", "on", {
|
||||
friendly_name: "Color Effects Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
|
||||
color_mode: LightColorMode.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbw_light", "on", {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_rgbww_light", "on", {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBWW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
getEntity("light", "color_xy_light", "on", {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.XY,
|
||||
],
|
||||
color_mode: LightColorMode.XY,
|
||||
effect_list: ["random", "colorloop"],
|
||||
}),
|
||||
{
|
||||
entity_id: "light.bed_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Basic Light",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Brightness Light",
|
||||
brightness: 200,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS],
|
||||
color_mode: LightColorMode.BRIGHTNESS,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_temperature_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "White Color Temperature Light",
|
||||
brightness: 128,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_hs_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color HS Light",
|
||||
brightness: 255,
|
||||
hs_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.HS,
|
||||
],
|
||||
color_mode: LightColorMode.HS,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_rgb_ct_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color RGB + CT Light",
|
||||
brightness: 255,
|
||||
color_temp: 75,
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGB,
|
||||
],
|
||||
color_mode: LightColorMode.COLOR_TEMP,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_RGB_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color Effects Light",
|
||||
brightness: 255,
|
||||
rgb_color: [30, 100, 255],
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
|
||||
color_mode: LightColorMode.RGB,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_rgbw_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color RGBW Light",
|
||||
brightness: 255,
|
||||
rgbw_color: [30, 100, 255, 125],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_rgbww_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color RGBWW Light",
|
||||
brightness: 255,
|
||||
rgbww_color: [30, 100, 255, 125, 10],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.RGBWW,
|
||||
],
|
||||
color_mode: LightColorMode.RGBWW,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.color_xy_light",
|
||||
state: "on",
|
||||
attributes: {
|
||||
friendly_name: "Color XY Light",
|
||||
brightness: 255,
|
||||
xy_color: [30, 100],
|
||||
rgb_color: [30, 100, 255],
|
||||
min_mireds: 30,
|
||||
max_mireds: 150,
|
||||
supported_features:
|
||||
LightEntityFeature.EFFECT +
|
||||
LightEntityFeature.FLASH +
|
||||
LightEntityFeature.TRANSITION,
|
||||
supported_color_modes: [
|
||||
LightColorMode.BRIGHTNESS,
|
||||
LightColorMode.COLOR_TEMP,
|
||||
LightColorMode.XY,
|
||||
],
|
||||
color_mode: LightColorMode.XY,
|
||||
effect_list: ["random", "colorloop"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-light")
|
||||
@@ -147,7 +182,7 @@ class DemoMoreInfoLight extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,26 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("lock", "lock", "locked", {
|
||||
friendly_name: "Lock",
|
||||
device_class: "lock",
|
||||
}),
|
||||
getEntity("lock", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable lock",
|
||||
}),
|
||||
{
|
||||
entity_id: "lock.lock",
|
||||
state: "locked",
|
||||
attributes: {
|
||||
friendly_name: "Lock",
|
||||
device_class: "lock",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable lock",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-lock")
|
||||
@@ -28,7 +35,7 @@ class DemoMoreInfoLock extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class DemoMoreInfoMediaPlayer extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,48 +3,63 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("number", "box1", 0, {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "slider1", 0, {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "auto1", 0, {
|
||||
friendly_name: "Auto1",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
getEntity("number", "auto2", 0, {
|
||||
friendly_name: "Auto2",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
}),
|
||||
{
|
||||
entity_id: "number.box1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Box1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "box",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.slider1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Slider1",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "slider",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.auto1",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Auto1",
|
||||
min: 0,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "number.auto2",
|
||||
state: "0",
|
||||
attributes: {
|
||||
friendly_name: "Auto2",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
initial: 0,
|
||||
mode: "auto",
|
||||
unit_of_measurement: "items",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-number")
|
||||
@@ -57,7 +72,7 @@ class DemoMoreInfoNumber extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,26 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("scene", "romantic_lights", "scening", {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
getEntity("scene", "unavailable", "unavailable", {
|
||||
friendly_name: "Romantic Scene",
|
||||
}),
|
||||
{
|
||||
entity_id: "scene.romantic_lights",
|
||||
state: "scening",
|
||||
attributes: {
|
||||
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "scene.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Romantic Scene",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-scene")
|
||||
@@ -28,7 +35,7 @@ class DemoMoreInfoScene extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("timer", "timer", "idle", {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
}),
|
||||
{
|
||||
entity_id: "timer.timer",
|
||||
state: "idle",
|
||||
attributes: {
|
||||
friendly_name: "Timer",
|
||||
duration: "0:05:00",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-timer")
|
||||
@@ -25,7 +28,7 @@ class DemoMoreInfoTimer extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
@@ -23,124 +22,208 @@ const base_attributes = {
|
||||
};
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("update", "update1", "on", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update",
|
||||
}),
|
||||
getEntity("update", "update2", "on", {
|
||||
...base_attributes,
|
||||
title: null,
|
||||
friendly_name: "Update without title",
|
||||
}),
|
||||
getEntity("update", "update3", "on", {
|
||||
...base_attributes,
|
||||
release_url: null,
|
||||
friendly_name: "Update without release_url",
|
||||
}),
|
||||
getEntity("update", "update4", "on", {
|
||||
...base_attributes,
|
||||
release_summary: null,
|
||||
friendly_name: "Update without release_summary",
|
||||
}),
|
||||
getEntity("update", "update5", "off", {
|
||||
...base_attributes,
|
||||
installed_version: "1.2.3",
|
||||
friendly_name: "No update",
|
||||
}),
|
||||
getEntity("update", "update6", "off", {
|
||||
...base_attributes,
|
||||
skipped_version: "1.2.3",
|
||||
friendly_name: "Skipped version",
|
||||
}),
|
||||
getEntity("update", "update7", "on", {
|
||||
...base_attributes,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
|
||||
friendly_name: "With backup support",
|
||||
}),
|
||||
getEntity("update", "update8", "on", {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name: "With true in_progress",
|
||||
}),
|
||||
getEntity("update", "update9", "on", {
|
||||
...base_attributes,
|
||||
in_progress: 25,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 25 in_progress",
|
||||
}),
|
||||
getEntity("update", "update10", "on", {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 50 in_progress",
|
||||
}),
|
||||
getEntity("update", "update11", "on", {
|
||||
...base_attributes,
|
||||
in_progress: 75,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 75 in_progress",
|
||||
}),
|
||||
getEntity("update", "update12", "unavailable", {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
friendly_name: "Unavailable",
|
||||
}),
|
||||
getEntity("update", "update13", "on", {
|
||||
...base_attributes,
|
||||
supported_features: 0,
|
||||
friendly_name: "No install support",
|
||||
}),
|
||||
getEntity("update", "update14", "off", {
|
||||
...base_attributes,
|
||||
installed_version: null,
|
||||
friendly_name: "Update without installed_version",
|
||||
}),
|
||||
getEntity("update", "update15", "off", {
|
||||
...base_attributes,
|
||||
latest_version: null,
|
||||
friendly_name: "Update without latest_version",
|
||||
}),
|
||||
getEntity("update", "update16", "off", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
}),
|
||||
getEntity("update", "update17", "off", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes error",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
}),
|
||||
getEntity("update", "update18", "off", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes loading",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
}),
|
||||
getEntity("update", "update19", "on", {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with auto update",
|
||||
auto_update: true,
|
||||
}),
|
||||
getEntity("update", "update20", "on", {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
title: undefined,
|
||||
friendly_name: "Installing without title",
|
||||
}),
|
||||
getEntity("update", "update21", "on", {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name:
|
||||
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
}),
|
||||
{
|
||||
entity_id: "update.update1",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update2",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
title: null,
|
||||
friendly_name: "Update without title",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update3",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
release_url: null,
|
||||
friendly_name: "Update without release_url",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update4",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
release_summary: null,
|
||||
friendly_name: "Update without release_summary",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update5",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
installed_version: "1.2.3",
|
||||
friendly_name: "No update",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update6",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
skipped_version: "1.2.3",
|
||||
friendly_name: "Skipped version",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update7",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
|
||||
friendly_name: "With backup support",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update8",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name: "With true in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update9",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 25,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 25 in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update10",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 50 in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update11",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 75,
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
friendly_name: "With 75 in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update12",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: 50,
|
||||
friendly_name: "Unavailable",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update13",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
supported_features: 0,
|
||||
friendly_name: "No install support",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update14",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
installed_version: null,
|
||||
friendly_name: "Update without installed_version",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update15",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
latest_version: null,
|
||||
friendly_name: "Update without latest_version",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update16",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update17",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes error",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update18",
|
||||
state: "off",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with release notes loading",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update19",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
friendly_name: "Update with auto update",
|
||||
auto_update: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update20",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
title: undefined,
|
||||
friendly_name: "Installing without title",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "update.update21",
|
||||
state: "on",
|
||||
attributes: {
|
||||
...base_attributes,
|
||||
in_progress: true,
|
||||
friendly_name:
|
||||
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
|
||||
supported_features:
|
||||
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-update")
|
||||
@@ -153,7 +236,7 @@ class DemoMoreInfoUpdate extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -3,20 +3,23 @@ import { html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
}),
|
||||
{
|
||||
entity_id: "vacuum.first_floor_vacuum",
|
||||
state: "docked",
|
||||
attributes: {
|
||||
friendly_name: "First floor vacuum",
|
||||
supported_features:
|
||||
VacuumEntityFeature.START +
|
||||
VacuumEntityFeature.STOP +
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-vacuum")
|
||||
@@ -29,7 +32,7 @@ class DemoMoreInfoVacuum extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,39 +4,46 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { WaterHeaterEntityFeature } from "../../../../src/data/water_heater";
|
||||
import "../../../../src/dialogs/more-info/more-info-content";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-more-infos";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("water_heater", "basic", "eco", {
|
||||
friendly_name: "Basic heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "eco",
|
||||
away_mode: "off",
|
||||
target_temp_step: 1,
|
||||
current_temperature: 55,
|
||||
temperature: 60,
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE |
|
||||
WaterHeaterEntityFeature.AWAY_MODE,
|
||||
}),
|
||||
getEntity("water_heater", "unavailable", "unavailable", {
|
||||
friendly_name: "Unavailable heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "off",
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE,
|
||||
}),
|
||||
{
|
||||
entity_id: "water_heater.basic",
|
||||
state: "eco",
|
||||
attributes: {
|
||||
friendly_name: "Basic heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "eco",
|
||||
away_mode: "off",
|
||||
target_temp_step: 1,
|
||||
current_temperature: 55,
|
||||
temperature: 60,
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE |
|
||||
WaterHeaterEntityFeature.AWAY_MODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "water_heater.unavailable",
|
||||
state: "unavailable",
|
||||
attributes: {
|
||||
friendly_name: "Unavailable heater",
|
||||
operation_list: ["heat_pump", "eco", "performance", "off"],
|
||||
operation_mode: "off",
|
||||
min_temp: 20,
|
||||
max_temp: 70,
|
||||
supported_features:
|
||||
// eslint-disable-next-line no-bitwise
|
||||
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
|
||||
WaterHeaterEntityFeature.OPERATION_MODE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-more-info-water-heater")
|
||||
@@ -49,7 +56,7 @@ class DemoMoreInfoWaterHeater extends LitElement {
|
||||
return html`
|
||||
<demo-more-infos
|
||||
.hass=${this.hass}
|
||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||
.entities=${ENTITIES.map((ent) => ent.entity_id)}
|
||||
></demo-more-infos>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class HaLandingPage extends LandingPageBaseElement {
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
makeDialogManager(this, this.shadowRoot!);
|
||||
makeDialogManager(this);
|
||||
|
||||
if (window.innerWidth > 450) {
|
||||
import("../../src/resources/particles");
|
||||
|
||||
93
package.json
93
package.json
@@ -26,33 +26,33 @@
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.6",
|
||||
"@babel/runtime": "7.29.2",
|
||||
"@braintree/sanitize-url": "7.1.2",
|
||||
"@codemirror/autocomplete": "6.20.0",
|
||||
"@codemirror/commands": "6.10.2",
|
||||
"@codemirror/language": "6.12.2",
|
||||
"@codemirror/autocomplete": "6.20.1",
|
||||
"@codemirror/commands": "6.10.3",
|
||||
"@codemirror/language": "6.12.3",
|
||||
"@codemirror/legacy-modes": "6.5.2",
|
||||
"@codemirror/search": "6.6.0",
|
||||
"@codemirror/state": "6.5.4",
|
||||
"@codemirror/view": "6.39.15",
|
||||
"@codemirror/state": "6.6.0",
|
||||
"@codemirror/view": "6.40.0",
|
||||
"@date-fns/tz": "1.4.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "7.2.2",
|
||||
"@formatjs/intl-displaynames": "7.2.1",
|
||||
"@formatjs/intl-durationformat": "0.10.1",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.1",
|
||||
"@formatjs/intl-listformat": "8.2.1",
|
||||
"@formatjs/intl-locale": "5.2.1",
|
||||
"@formatjs/intl-numberformat": "9.2.2",
|
||||
"@formatjs/intl-pluralrules": "6.2.2",
|
||||
"@formatjs/intl-relativetimeformat": "12.2.2",
|
||||
"@formatjs/intl-datetimeformat": "7.3.1",
|
||||
"@formatjs/intl-displaynames": "7.3.1",
|
||||
"@formatjs/intl-durationformat": "0.10.3",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.2",
|
||||
"@formatjs/intl-listformat": "8.3.1",
|
||||
"@formatjs/intl-locale": "5.3.1",
|
||||
"@formatjs/intl-numberformat": "9.3.1",
|
||||
"@formatjs/intl-pluralrules": "6.3.1",
|
||||
"@formatjs/intl-relativetimeformat": "12.3.1",
|
||||
"@fullcalendar/core": "6.1.20",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"@fullcalendar/interaction": "6.1.20",
|
||||
"@fullcalendar/list": "6.1.20",
|
||||
"@fullcalendar/luxon3": "6.1.20",
|
||||
"@fullcalendar/timegrid": "6.1.20",
|
||||
"@home-assistant/webawesome": "3.2.1-ha.3",
|
||||
"@home-assistant/webawesome": "3.3.1-ha.0",
|
||||
"@lezer/highlight": "1.2.3",
|
||||
"@lit-labs/motion": "1.1.0",
|
||||
"@lit-labs/observers": "2.1.0",
|
||||
@@ -72,7 +72,6 @@
|
||||
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"@material/mwc-radio": "0.27.0",
|
||||
"@material/mwc-select": "0.27.0",
|
||||
"@material/mwc-snackbar": "0.27.0",
|
||||
"@material/mwc-switch": "0.27.0",
|
||||
"@material/mwc-textarea": "0.27.0",
|
||||
"@material/mwc-textfield": "0.27.0",
|
||||
@@ -88,14 +87,13 @@
|
||||
"@tsparticles/engine": "3.9.1",
|
||||
"@tsparticles/preset-links": "3.2.0",
|
||||
"@vibrant/color": "4.0.4",
|
||||
"@vue/web-component-wrapper": "1.3.0",
|
||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"app-datepicker": "5.1.1",
|
||||
"barcode-detector": "3.1.0",
|
||||
"barcode-detector": "3.1.1",
|
||||
"cally": "0.9.2",
|
||||
"color-name": "2.1.0",
|
||||
"comlink": "4.4.2",
|
||||
"core-js": "3.48.0",
|
||||
"core-js": "3.49.0",
|
||||
"cropperjs": "1.6.2",
|
||||
"culori": "4.0.2",
|
||||
"date-fns": "4.1.0",
|
||||
@@ -110,7 +108,7 @@
|
||||
"hls.js": "1.6.15",
|
||||
"home-assistant-js-websocket": "9.6.0",
|
||||
"idb-keyval": "6.2.2",
|
||||
"intl-messageformat": "11.1.2",
|
||||
"intl-messageformat": "11.2.0",
|
||||
"js-yaml": "4.1.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
|
||||
@@ -118,7 +116,7 @@
|
||||
"lit": "3.3.2",
|
||||
"lit-html": "3.3.2",
|
||||
"luxon": "3.7.2",
|
||||
"marked": "17.0.3",
|
||||
"marked": "17.0.5",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "4.0.4",
|
||||
"object-hash": "3.0.0",
|
||||
@@ -131,9 +129,6 @@
|
||||
"stacktrace-js": "2.0.2",
|
||||
"superstruct": "2.0.2",
|
||||
"tinykeys": "3.0.0",
|
||||
"ua-parser-js": "2.0.9",
|
||||
"vue": "2.7.16",
|
||||
"vue2-daterange-picker": "0.6.8",
|
||||
"weekstart": "2.0.0",
|
||||
"workbox-cacheable-response": "7.4.0",
|
||||
"workbox-core": "7.4.0",
|
||||
@@ -145,17 +140,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.29.0",
|
||||
"@babel/helper-define-polyfill-provider": "0.6.6",
|
||||
"@babel/helper-define-polyfill-provider": "0.6.8",
|
||||
"@babel/plugin-transform-runtime": "7.29.0",
|
||||
"@babel/preset-env": "7.29.0",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.21.10",
|
||||
"@html-eslint/eslint-plugin": "0.57.1",
|
||||
"@babel/preset-env": "7.29.2",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.22.0",
|
||||
"@html-eslint/eslint-plugin": "0.58.1",
|
||||
"@lokalise/node-api": "15.6.1",
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
"@octokit/rest": "22.0.1",
|
||||
"@rsdoctor/rspack-plugin": "1.5.2",
|
||||
"@rspack/core": "1.7.6",
|
||||
"@rsdoctor/rspack-plugin": "1.5.5",
|
||||
"@rspack/core": "1.7.10",
|
||||
"@rspack/dev-server": "1.2.1",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.25",
|
||||
@@ -173,14 +168,13 @@
|
||||
"@types/qrcode": "1.5.6",
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@types/tar": "7.0.87",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@vitest/coverage-v8": "4.0.18",
|
||||
"babel-loader": "10.0.0",
|
||||
"@vitest/coverage-v8": "4.1.1",
|
||||
"babel-loader": "10.1.1",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.3",
|
||||
"del": "8.0.1",
|
||||
"eslint": "9.39.3",
|
||||
"eslint": "9.39.4",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-import-resolver-webpack": "0.13.10",
|
||||
@@ -190,7 +184,7 @@
|
||||
"eslint-plugin-unused-imports": "4.4.1",
|
||||
"eslint-plugin-wc": "3.1.0",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.3.3",
|
||||
"fs-extra": "11.3.4",
|
||||
"glob": "13.0.6",
|
||||
"gulp": "5.0.1",
|
||||
"gulp-brotli": "3.0.0",
|
||||
@@ -198,9 +192,9 @@
|
||||
"gulp-rename": "2.1.0",
|
||||
"html-minifier-terser": "7.2.0",
|
||||
"husky": "9.1.7",
|
||||
"jsdom": "28.1.0",
|
||||
"jsdom": "29.0.1",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "16.3.0",
|
||||
"lint-staged": "16.4.0",
|
||||
"lit-analyzer": "2.0.3",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.template": "4.5.0",
|
||||
@@ -208,33 +202,32 @@
|
||||
"pinst": "3.0.0",
|
||||
"prettier": "3.8.1",
|
||||
"rspack-manifest-plugin": "5.2.1",
|
||||
"serve": "14.2.5",
|
||||
"sinon": "21.0.1",
|
||||
"tar": "7.5.9",
|
||||
"terser-webpack-plugin": "5.3.16",
|
||||
"serve": "14.2.6",
|
||||
"sinon": "21.0.3",
|
||||
"tar": "7.5.13",
|
||||
"terser-webpack-plugin": "5.4.0",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.56.1",
|
||||
"typescript-eslint": "8.57.2",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.0.18",
|
||||
"vitest": "4.1.1",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.4.0#~/.yarn/patches/workbox-build-npm-7.4.0-c84561662c.patch"
|
||||
},
|
||||
"resolutions": {
|
||||
"@material/mwc-button@^0.25.3": "^0.27.0",
|
||||
"lit": "3.3.2",
|
||||
"lit-html": "3.3.2",
|
||||
"clean-css": "5.3.3",
|
||||
"@lit/reactive-element": "2.1.2",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"globals": "17.3.0",
|
||||
"globals": "17.4.0",
|
||||
"tslib": "2.8.1",
|
||||
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"glob@^10.2.2": "^10.5.0"
|
||||
},
|
||||
"packageManager": "yarn@4.12.0",
|
||||
"packageManager": "yarn@4.13.0",
|
||||
"volta": {
|
||||
"node": "24.14.0"
|
||||
"node": "24.14.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20260128.0"
|
||||
version = "20260325.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
"extends": ["monorepo:material-components-web"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"description": "Vue is only used by date range which is only v2",
|
||||
"matchPackageNames": ["vue"],
|
||||
"allowedVersions": "< 3"
|
||||
},
|
||||
{
|
||||
"description": "Group MDI packages",
|
||||
"groupName": "Material Design Icons",
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { HaFormString } from "../components/ha-form/ha-form-string";
|
||||
import "../components/ha-icon-button";
|
||||
import "./ha-auth-textfield";
|
||||
import "../components/input/ha-input";
|
||||
|
||||
@customElement("ha-auth-form-string")
|
||||
export class HaAuthFormString extends HaFormString {
|
||||
@@ -12,63 +9,9 @@ export class HaAuthFormString extends HaFormString {
|
||||
return this;
|
||||
}
|
||||
|
||||
public reportValidity(): boolean {
|
||||
return this.querySelector("ha-auth-textfield")?.reportValidity() ?? true;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
ha-auth-form-string {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
ha-auth-form-string[own-margin] {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
ha-auth-form-string ha-auth-textfield {
|
||||
display: block !important;
|
||||
}
|
||||
ha-auth-form-string ha-icon-button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 8px;
|
||||
--ha-icon-button-size: 40px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
direction: var(--direction);
|
||||
}
|
||||
</style>
|
||||
<ha-auth-textfield
|
||||
.type=${!this.isPassword
|
||||
? this.stringType
|
||||
: this.unmaskedPassword
|
||||
? "text"
|
||||
: "password"}
|
||||
.label=${this.label}
|
||||
.value=${this.data || ""}
|
||||
.helper=${this.helper}
|
||||
helperPersistent
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.schema.required}
|
||||
.autoValidate=${this.schema.required}
|
||||
.name=${this.schema.name}
|
||||
.autocomplete=${this.schema.autocomplete}
|
||||
?autofocus=${this.schema.autofocus}
|
||||
.suffix=${this.isPassword
|
||||
? // reserve some space for the icon.
|
||||
html`<div style="width: 24px"></div>`
|
||||
: this.schema.description?.suffix}
|
||||
.validationMessage=${this.schema.required
|
||||
? this.localize?.("ui.panel.page-authorize.form.error_required")
|
||||
: undefined}
|
||||
@input=${this._valueChanged}
|
||||
@change=${this._valueChanged}
|
||||
></ha-auth-textfield>
|
||||
${this.renderIcon()}
|
||||
`;
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.style.position = "relative";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import { HaForm } from "../components/ha-form/ha-form";
|
||||
import "./ha-auth-form-string";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
const localizeBaseKey = "ui.panel.page-authorize.form";
|
||||
|
||||
@@ -34,6 +34,9 @@ export class HaAuthForm extends HaForm {
|
||||
protected render() {
|
||||
return html`
|
||||
<style>
|
||||
ha-auth-form {
|
||||
--ha-input-required-marker: "";
|
||||
}
|
||||
ha-auth-form .root > * {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
/* eslint-disable lit/value-after-constraints */
|
||||
/* eslint-disable lit/prefer-static-styles */
|
||||
import { floatingLabel } from "@material/mwc-floating-label/mwc-floating-label-directive";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { live } from "lit/directives/live";
|
||||
import { HaTextField } from "../components/ha-textfield";
|
||||
|
||||
@customElement("ha-auth-textfield")
|
||||
export class HaAuthTextField extends HaTextField {
|
||||
protected renderLabel(): TemplateResult | string {
|
||||
return !this.label
|
||||
? ""
|
||||
: html`
|
||||
<span
|
||||
.floatingLabelFoundation=${floatingLabel(
|
||||
this.label
|
||||
) as unknown as any}
|
||||
.id=${this.name}
|
||||
>${this.label}</span
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
protected renderInput(shouldRenderHelperText: boolean): TemplateResult {
|
||||
const minOrUndef = this.minLength === -1 ? undefined : this.minLength;
|
||||
const maxOrUndef = this.maxLength === -1 ? undefined : this.maxLength;
|
||||
const autocapitalizeOrUndef = this.autocapitalize
|
||||
? (this.autocapitalize as
|
||||
| "off"
|
||||
| "none"
|
||||
| "on"
|
||||
| "sentences"
|
||||
| "words"
|
||||
| "characters")
|
||||
: undefined;
|
||||
const showValidationMessage = this.validationMessage && !this.isUiValid;
|
||||
const ariaLabelledbyOrUndef = this.label ? this.name : undefined;
|
||||
const ariaControlsOrUndef = shouldRenderHelperText
|
||||
? "helper-text"
|
||||
: undefined;
|
||||
const ariaDescribedbyOrUndef =
|
||||
this.focused || this.helperPersistent || showValidationMessage
|
||||
? "helper-text"
|
||||
: undefined;
|
||||
// TODO: live() directive needs casting for lit-analyzer
|
||||
// https://github.com/runem/lit-analyzer/pull/91/files
|
||||
// TODO: lit-analyzer labels min/max as (number|string) instead of string
|
||||
return html`<input
|
||||
aria-labelledby=${ifDefined(ariaLabelledbyOrUndef)}
|
||||
aria-controls=${ifDefined(ariaControlsOrUndef)}
|
||||
aria-describedby=${ifDefined(ariaDescribedbyOrUndef)}
|
||||
class="mdc-text-field__input"
|
||||
type=${this.type}
|
||||
.value=${live(this.value) as unknown as string}
|
||||
?disabled=${this.disabled}
|
||||
placeholder=${this.placeholder}
|
||||
?required=${this.required}
|
||||
?readonly=${this.readOnly}
|
||||
minlength=${ifDefined(minOrUndef)}
|
||||
maxlength=${ifDefined(maxOrUndef)}
|
||||
pattern=${ifDefined(this.pattern ? this.pattern : undefined)}
|
||||
min=${ifDefined(this.min === "" ? undefined : (this.min as number))}
|
||||
max=${ifDefined(this.max === "" ? undefined : (this.max as number))}
|
||||
step=${ifDefined(this.step === null ? undefined : (this.step as number))}
|
||||
size=${ifDefined(this.size === null ? undefined : this.size)}
|
||||
name=${ifDefined(this.name === "" ? undefined : this.name)}
|
||||
inputmode=${ifDefined(this.inputMode)}
|
||||
autocapitalize=${ifDefined(autocapitalizeOrUndef)}
|
||||
?autofocus=${this.autofocus}
|
||||
@input=${this.handleInputChange}
|
||||
@focus=${this.onInputFocus}
|
||||
@blur=${this.onInputBlur}
|
||||
/>`;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<style>
|
||||
ha-auth-textfield {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
outline: none;
|
||||
}
|
||||
ha-auth-textfield:not([disabled]):hover
|
||||
:not(.mdc-text-field--invalid):not(.mdc-text-field--focused)
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-outlined-hover-border-color,
|
||||
rgba(0, 0, 0, 0.87)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field:not(.mdc-text-field--outlined) {
|
||||
background-color: var(--mdc-text-field-fill-color, whitesmoke);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--invalid
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-error-color,
|
||||
var(--mdc-theme-error, #b00020)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--invalid
|
||||
+ .mdc-text-field-helper-line
|
||||
.mdc-text-field-character-counter,
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--invalid
|
||||
.mdc-text-field__icon {
|
||||
color: var(
|
||||
--mdc-text-field-error-color,
|
||||
var(--mdc-theme-error, #b00020)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label,
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label::after {
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-stroke-width: 2px;
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-focused-label-color,
|
||||
var(--mdc-theme-primary, rgba(98, 0, 238, 0.87))
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||
.mdc-floating-label {
|
||||
color: #6200ee;
|
||||
color: var(--mdc-theme-primary, #6200ee);
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field
|
||||
.mdc-text-field__input {
|
||||
color: var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87));
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field
|
||||
.mdc-text-field__input::placeholder {
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field-helper-line
|
||||
.mdc-text-field-helper-text:not(
|
||||
.mdc-text-field-helper-text--validation-msg
|
||||
),
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field-helper-line:not(.mdc-text-field--invalid)
|
||||
.mdc-text-field-character-counter {
|
||||
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field:not(.mdc-text-field--outlined) {
|
||||
background-color: var(--mdc-text-field-disabled-fill-color, #fafafa);
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field.mdc-text-field--outlined
|
||||
mwc-notched-outline {
|
||||
--mdc-notched-outline-border-color: var(
|
||||
--mdc-text-field-outlined-disabled-border-color,
|
||||
rgba(0, 0, 0, 0.06)
|
||||
);
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label,
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field:not(.mdc-text-field--invalid):not(
|
||||
.mdc-text-field--focused
|
||||
)
|
||||
.mdc-floating-label::after {
|
||||
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled] .mdc-text-field .mdc-text-field__input,
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field
|
||||
.mdc-text-field__input::placeholder {
|
||||
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||
}
|
||||
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field-helper-line
|
||||
.mdc-text-field-helper-text,
|
||||
ha-auth-textfield[disabled]
|
||||
.mdc-text-field-helper-line
|
||||
.mdc-text-field-character-counter {
|
||||
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
|
||||
}
|
||||
ha-auth-textfield:not([disabled])
|
||||
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
|
||||
.mdc-floating-label {
|
||||
color: var(--mdc-theme-primary, #6200ee);
|
||||
}
|
||||
ha-auth-textfield[no-spinner] input::-webkit-outer-spin-button,
|
||||
ha-auth-textfield[no-spinner] input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
ha-auth-textfield[no-spinner] input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
</style>
|
||||
${super.render()}
|
||||
`;
|
||||
}
|
||||
|
||||
protected createRenderRoot() {
|
||||
// add parent style to light dom
|
||||
const style = document.createElement("style");
|
||||
style.textContent = HaTextField.elementStyles as unknown as string;
|
||||
this.append(style);
|
||||
return this;
|
||||
}
|
||||
|
||||
public firstUpdated() {
|
||||
super.firstUpdated();
|
||||
|
||||
if (this.autofocus) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-auth-textfield": HaAuthTextField;
|
||||
}
|
||||
}
|
||||
@@ -58,9 +58,11 @@ export class CastManager {
|
||||
this._eventListeners[event].push(listener);
|
||||
|
||||
return () => {
|
||||
this._eventListeners[event].splice(
|
||||
this._eventListeners[event].indexOf(listener)
|
||||
);
|
||||
const listeners = this._eventListeners[event];
|
||||
const index = listeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,17 @@ const YAML_ONLY_THEMES_COLORS = new Set([
|
||||
"disabled",
|
||||
]);
|
||||
|
||||
export function computeCssColor(color: string): string {
|
||||
export function computeCssVariableName(color: string): string {
|
||||
if (THEME_COLORS.has(color) || YAML_ONLY_THEMES_COLORS.has(color)) {
|
||||
return `var(--${color}-color)`;
|
||||
return `--${color}-color`;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
export function computeCssColor(color: string): string {
|
||||
const cssVarName = computeCssVariableName(color);
|
||||
if (cssVarName !== color) {
|
||||
return `var(${cssVarName})`;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {
|
||||
addDays,
|
||||
subHours,
|
||||
endOfDay,
|
||||
endOfMonth,
|
||||
endOfQuarter,
|
||||
endOfWeek,
|
||||
endOfYear,
|
||||
startOfDay,
|
||||
startOfMonth,
|
||||
startOfQuarter,
|
||||
startOfWeek,
|
||||
startOfYear,
|
||||
startOfQuarter,
|
||||
endOfQuarter,
|
||||
subDays,
|
||||
subHours,
|
||||
subMonths,
|
||||
} from "date-fns";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
@@ -33,83 +33,89 @@ export type DateRange =
|
||||
| "now-24h";
|
||||
|
||||
export const calcDateRange = (
|
||||
hass: HomeAssistant,
|
||||
locale: HomeAssistant["locale"],
|
||||
hassConfig: HomeAssistant["config"],
|
||||
range: DateRange
|
||||
): [Date, Date] => {
|
||||
const today = new Date();
|
||||
const weekStartsOn = firstWeekdayIndex(hass.locale);
|
||||
const weekStartsOn = firstWeekdayIndex(locale);
|
||||
switch (range) {
|
||||
case "today":
|
||||
return [
|
||||
calcDate(today, startOfDay, hass.locale, hass.config, {
|
||||
calcDate(today, startOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
calcDate(today, endOfDay, hass.locale, hass.config, {
|
||||
calcDate(today, endOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
];
|
||||
case "yesterday":
|
||||
return [
|
||||
calcDate(addDays(today, -1), startOfDay, hass.locale, hass.config, {
|
||||
calcDate(addDays(today, -1), startOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
calcDate(addDays(today, -1), endOfDay, hass.locale, hass.config, {
|
||||
calcDate(addDays(today, -1), endOfDay, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
];
|
||||
case "this_week":
|
||||
return [
|
||||
calcDate(today, startOfWeek, hass.locale, hass.config, {
|
||||
calcDate(today, startOfWeek, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
calcDate(today, endOfWeek, hass.locale, hass.config, {
|
||||
calcDate(today, endOfWeek, locale, hassConfig, {
|
||||
weekStartsOn,
|
||||
}),
|
||||
];
|
||||
case "this_month":
|
||||
return [
|
||||
calcDate(today, startOfMonth, hass.locale, hass.config),
|
||||
calcDate(today, endOfMonth, hass.locale, hass.config),
|
||||
calcDate(today, startOfMonth, locale, hassConfig),
|
||||
calcDate(today, endOfMonth, locale, hassConfig),
|
||||
];
|
||||
case "this_quarter":
|
||||
return [
|
||||
calcDate(today, startOfQuarter, hass.locale, hass.config),
|
||||
calcDate(today, endOfQuarter, hass.locale, hass.config),
|
||||
calcDate(today, startOfQuarter, locale, hassConfig),
|
||||
calcDate(today, endOfQuarter, locale, hassConfig),
|
||||
];
|
||||
case "this_year":
|
||||
return [
|
||||
calcDate(today, startOfYear, hass.locale, hass.config),
|
||||
calcDate(today, endOfYear, hass.locale, hass.config),
|
||||
calcDate(today, startOfYear, locale, hassConfig),
|
||||
calcDate(today, endOfYear, locale, hassConfig),
|
||||
];
|
||||
case "now-7d":
|
||||
return [
|
||||
calcDate(today, subDays, hass.locale, hass.config, 7),
|
||||
calcDate(today, subDays, hass.locale, hass.config, 0),
|
||||
calcDate(today, subDays, locale, hassConfig, 7),
|
||||
calcDate(today, subDays, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-30d":
|
||||
return [
|
||||
calcDate(today, subDays, hass.locale, hass.config, 30),
|
||||
calcDate(today, subDays, hass.locale, hass.config, 0),
|
||||
calcDate(today, subDays, locale, hassConfig, 30),
|
||||
calcDate(today, subDays, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-12m":
|
||||
return [
|
||||
calcDate(today, subMonths, hass.locale, hass.config, 12),
|
||||
calcDate(today, subMonths, hass.locale, hass.config, 0),
|
||||
calcDate(
|
||||
today,
|
||||
(date) => subMonths(startOfMonth(date), 11),
|
||||
locale,
|
||||
hassConfig
|
||||
),
|
||||
calcDate(today, endOfMonth, locale, hassConfig),
|
||||
];
|
||||
case "now-1h":
|
||||
return [
|
||||
calcDate(today, subHours, hass.locale, hass.config, 1),
|
||||
calcDate(today, subHours, hass.locale, hass.config, 0),
|
||||
calcDate(today, subHours, locale, hassConfig, 1),
|
||||
calcDate(today, subHours, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-12h":
|
||||
return [
|
||||
calcDate(today, subHours, hass.locale, hass.config, 12),
|
||||
calcDate(today, subHours, hass.locale, hass.config, 0),
|
||||
calcDate(today, subHours, locale, hassConfig, 12),
|
||||
calcDate(today, subHours, locale, hassConfig, 0),
|
||||
];
|
||||
case "now-24h":
|
||||
return [
|
||||
calcDate(today, subHours, hass.locale, hass.config, 24),
|
||||
calcDate(today, subHours, hass.locale, hass.config, 0),
|
||||
calcDate(today, subHours, locale, hassConfig, 24),
|
||||
calcDate(today, subHours, locale, hassConfig, 0),
|
||||
];
|
||||
}
|
||||
return [today, today];
|
||||
|
||||
@@ -166,6 +166,21 @@ const formatDateMonthMem = memoizeOne(
|
||||
})
|
||||
);
|
||||
|
||||
// Aug
|
||||
export const formatDateMonthShort = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => formatDateMonthShortMem(locale, config.time_zone).format(dateObj);
|
||||
|
||||
const formatDateMonthShortMem = memoizeOne(
|
||||
(locale: FrontendLocaleData, serverTimeZone: string) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
month: "short",
|
||||
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
|
||||
})
|
||||
);
|
||||
|
||||
// 2021
|
||||
export const formatDateYear = (
|
||||
dateObj: Date,
|
||||
@@ -246,3 +261,36 @@ const formatDateWeekdayShortDateMem = memoizeOne(
|
||||
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Format a date as YYYY-MM-DD. Uses "en-CA" because it's the only
|
||||
* Intl locale that natively outputs ISO 8601 date format.
|
||||
* Locale/config are only used to resolve the time zone.
|
||||
*/
|
||||
export const formatISODateOnly = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => {
|
||||
const timeZone = resolveTimeZone(locale.time_zone, config.time_zone);
|
||||
const formatter = new Intl.DateTimeFormat("en-CA", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
timeZone,
|
||||
});
|
||||
return formatter.format(dateObj);
|
||||
};
|
||||
|
||||
// 2026-08-10/2026-08-15
|
||||
export const formatCallyDateRange = (
|
||||
start: Date,
|
||||
end: Date,
|
||||
locale: FrontendLocaleData,
|
||||
config: HassConfig
|
||||
) => {
|
||||
const startDate = formatISODateOnly(start, locale, config);
|
||||
const endDate = formatISODateOnly(end, locale, config);
|
||||
|
||||
return `${startDate}/${endDate}`;
|
||||
};
|
||||
|
||||
@@ -11,11 +11,11 @@ export const canOverrideAlphanumericInput = (composedPath: EventTarget[]) => {
|
||||
|
||||
const el = composedPath[0] as Element;
|
||||
|
||||
if (el.tagName === "TEXTAREA") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (el.parentElement?.tagName === "HA-SELECT") {
|
||||
if (
|
||||
el.tagName === "TEXTAREA" ||
|
||||
el.parentElement?.tagName === "HA-SELECT" ||
|
||||
el.parentElement?.tagName === "HA-DROPDOWN"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { computeAreaName } from "./compute_area_name";
|
||||
import { computeDeviceName } from "./compute_device_name";
|
||||
import { computeEntityName, entityUseDeviceName } from "./compute_entity_name";
|
||||
import { computeFloorName } from "./compute_floor_name";
|
||||
import { computeStateName } from "./compute_state_name";
|
||||
import { getEntityContext } from "./context/get_entity_context";
|
||||
|
||||
const DEFAULT_SEPARATOR = " ";
|
||||
@@ -29,14 +30,23 @@ export interface EntityNameOptions {
|
||||
|
||||
export const computeEntityNameDisplay = (
|
||||
stateObj: HassEntity,
|
||||
name: EntityNameItem | EntityNameItem[] | undefined,
|
||||
name: string | EntityNameItem | EntityNameItem[] | undefined,
|
||||
entities: HomeAssistant["entities"],
|
||||
devices: HomeAssistant["devices"],
|
||||
areas: HomeAssistant["areas"],
|
||||
floors: HomeAssistant["floors"],
|
||||
options?: EntityNameOptions
|
||||
) => {
|
||||
let items = ensureArray(name || DEFAULT_ENTITY_NAME);
|
||||
if (typeof name === "string") {
|
||||
return name;
|
||||
}
|
||||
|
||||
// If no name config is provided, fall back to the friendly name
|
||||
if (!name) {
|
||||
return computeStateName(stateObj);
|
||||
}
|
||||
|
||||
let items = ensureArray(name);
|
||||
|
||||
const separator = options?.separator ?? DEFAULT_SEPARATOR;
|
||||
|
||||
|
||||
@@ -142,6 +142,8 @@ const computeStateToPartsFromEntityAttributes = (
|
||||
group: "value",
|
||||
decimal: "value",
|
||||
fraction: "value",
|
||||
minusSign: "value",
|
||||
plusSign: "value",
|
||||
literal: "literal",
|
||||
currency: "unit",
|
||||
};
|
||||
@@ -152,7 +154,7 @@ const computeStateToPartsFromEntityAttributes = (
|
||||
const type = TYPE_MAP[part.type];
|
||||
if (!type) continue;
|
||||
const last = valueParts[valueParts.length - 1];
|
||||
// Merge consecutive numeric parts (e.g. "1" + "," + "234" + "." + "56" → "1,234.56")
|
||||
// Merge consecutive value parts (e.g. "-" + "12" + "." + "00" → "-12.00")
|
||||
if (type === "value" && last?.type === "value") {
|
||||
last.value += part.value;
|
||||
} else {
|
||||
@@ -253,6 +255,7 @@ const computeStateToPartsFromEntityAttributes = (
|
||||
"conversation",
|
||||
"event",
|
||||
"image",
|
||||
"infrared",
|
||||
"input_button",
|
||||
"notify",
|
||||
"scene",
|
||||
|
||||
@@ -29,6 +29,7 @@ export const FIXED_DOMAIN_STATES = {
|
||||
device_tracker: ["home", "not_home"],
|
||||
fan: ["on", "off"],
|
||||
humidifier: ["on", "off"],
|
||||
infrared: [],
|
||||
input_boolean: ["on", "off"],
|
||||
input_button: [],
|
||||
lawn_mower: ["error", "paused", "mowing", "returning", "docked"],
|
||||
@@ -270,6 +271,8 @@ export const getStates = (
|
||||
result.push(...state.attributes.preset_modes);
|
||||
} else if (attribute === "swing_mode") {
|
||||
result.push(...state.attributes.swing_modes);
|
||||
} else if (attribute === "swing_horizontal_mode") {
|
||||
result.push(...state.attributes.swing_horizontal_modes);
|
||||
}
|
||||
break;
|
||||
case "device_tracker":
|
||||
|
||||
@@ -6,7 +6,9 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
const compareState = state !== undefined ? state : stateObj?.state;
|
||||
|
||||
if (["button", "event", "input_button", "scene"].includes(domain)) {
|
||||
if (
|
||||
["button", "event", "infrared", "input_button", "scene"].includes(domain)
|
||||
) {
|
||||
return compareState !== UNAVAILABLE;
|
||||
}
|
||||
|
||||
|
||||
11
src/common/feature-detect/support-popover.ts
Normal file
11
src/common/feature-detect/support-popover.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Indicates whether the current browser supports the Popover API.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
|
||||
*/
|
||||
export const popoverSupported = globalThis?.HTMLElement?.prototype
|
||||
? Object.prototype.hasOwnProperty.call(
|
||||
globalThis.HTMLElement.prototype,
|
||||
"popover"
|
||||
)
|
||||
: false;
|
||||
@@ -71,13 +71,6 @@ export const formatNumberToParts = (
|
||||
? numberFormatToLocale(localeOptions)
|
||||
: undefined;
|
||||
|
||||
// Polyfill for Number.isNaN, which is more reliable than the global isNaN()
|
||||
Number.isNaN =
|
||||
Number.isNaN ||
|
||||
function isNaN(input) {
|
||||
return typeof input === "number" && isNaN(input);
|
||||
};
|
||||
|
||||
if (
|
||||
localeOptions?.number_format !== NumberFormat.none &&
|
||||
!Number.isNaN(Number(num))
|
||||
|
||||
@@ -1,24 +1,5 @@
|
||||
import { deepActiveElement } from "../dom/deep-active-element";
|
||||
|
||||
const getClipboardFallbackRoot = (): HTMLElement => {
|
||||
const activeElement = deepActiveElement();
|
||||
if (activeElement instanceof HTMLElement) {
|
||||
let root: Node = activeElement.getRootNode();
|
||||
let host: HTMLElement | null = null;
|
||||
|
||||
while (root instanceof ShadowRoot && root.host instanceof HTMLElement) {
|
||||
host = root.host;
|
||||
root = root.host.getRootNode();
|
||||
}
|
||||
|
||||
if (host) {
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
return document.body;
|
||||
};
|
||||
|
||||
export const copyToClipboard = async (str, rootEl?: HTMLElement) => {
|
||||
if (navigator.clipboard) {
|
||||
try {
|
||||
@@ -29,7 +10,7 @@ export const copyToClipboard = async (str, rootEl?: HTMLElement) => {
|
||||
}
|
||||
}
|
||||
|
||||
const root = rootEl || getClipboardFallbackRoot();
|
||||
const root = rootEl || deepActiveElement()?.getRootNode() || document.body;
|
||||
|
||||
const el = document.createElement("textarea");
|
||||
el.value = str;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// From: https://davidwalsh.name/javascript-debounce-function
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
||||
// leading edge and on the trailing.
|
||||
// leading edge. The trailing edge only fires if there were additional calls
|
||||
// during the wait period.
|
||||
|
||||
export const debounce = <T extends any[]>(
|
||||
func: (...args: T) => void,
|
||||
@@ -11,20 +10,35 @@ export const debounce = <T extends any[]>(
|
||||
immediate = false
|
||||
) => {
|
||||
let timeout: number | undefined;
|
||||
let trailingArgs: T | undefined;
|
||||
|
||||
const debouncedFunc = (...args: T): void => {
|
||||
const later = () => {
|
||||
timeout = undefined;
|
||||
func(...args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
const isLeading = immediate && !timeout;
|
||||
|
||||
if (timeout) {
|
||||
trailingArgs = args;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
timeout = window.setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
|
||||
timeout = window.setTimeout(() => {
|
||||
timeout = undefined;
|
||||
if (trailingArgs) {
|
||||
func(...trailingArgs);
|
||||
trailingArgs = undefined;
|
||||
} else if (!immediate) {
|
||||
func(...args);
|
||||
}
|
||||
}, wait);
|
||||
|
||||
if (isLeading) {
|
||||
func(...args);
|
||||
}
|
||||
};
|
||||
|
||||
debouncedFunc.cancel = () => {
|
||||
clearTimeout(timeout);
|
||||
trailingArgs = undefined;
|
||||
};
|
||||
|
||||
return debouncedFunc;
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@ export type CustomLegendOption = ECOption["legend"] & {
|
||||
id?: string;
|
||||
secondaryIds?: string[]; // Other dataset IDs that should be controlled by this legend item.
|
||||
name: string;
|
||||
value?: string; // Current value to display next to the name in the legend.
|
||||
itemStyle?: Record<string, any>;
|
||||
}[];
|
||||
};
|
||||
@@ -279,18 +280,23 @@ export class HaChartBase extends LitElement {
|
||||
<div class="chart"></div>
|
||||
</div>
|
||||
${this._renderLegend()}
|
||||
<div class="chart-controls ${classMap({ small: this.smallControls })}">
|
||||
${this._isZoomed && !this.hideResetButton
|
||||
? html`<ha-icon-button
|
||||
class="zoom-reset"
|
||||
.path=${mdiRestart}
|
||||
@click=${this._handleZoomReset}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.history_charts.zoom_reset"
|
||||
)}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
<slot name="button"></slot>
|
||||
<div class="top-controls ${classMap({ small: this.smallControls })}">
|
||||
<slot name="search"></slot>
|
||||
<div
|
||||
class="chart-controls ${classMap({ small: this.smallControls })}"
|
||||
>
|
||||
${this._isZoomed && !this.hideResetButton
|
||||
? html`<ha-icon-button
|
||||
class="zoom-reset"
|
||||
.path=${mdiRestart}
|
||||
@click=${this._handleZoomReset}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.history_charts.zoom_reset"
|
||||
)}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
<slot name="button"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -333,12 +339,14 @@ export class HaChartBase extends LitElement {
|
||||
let itemStyle: Record<string, any> = {};
|
||||
let name = "";
|
||||
let id = "";
|
||||
let value = "";
|
||||
if (typeof item === "string") {
|
||||
name = item;
|
||||
id = item;
|
||||
} else {
|
||||
name = item.name ?? "";
|
||||
id = item.id ?? name;
|
||||
value = item.value ?? "";
|
||||
itemStyle = item.itemStyle ?? {};
|
||||
}
|
||||
const dataset =
|
||||
@@ -365,6 +373,7 @@ export class HaChartBase extends LitElement {
|
||||
})}
|
||||
></div>
|
||||
<div class="label">${name}</div>
|
||||
${value ? html`<div class="value">${value}</div>` : nothing}
|
||||
</li>`;
|
||||
})}
|
||||
${items.length > overflowLimit
|
||||
@@ -578,7 +587,10 @@ export class HaChartBase extends LitElement {
|
||||
id: "dataZoom",
|
||||
type: "inside",
|
||||
orient: "horizontal",
|
||||
filterMode: "none",
|
||||
// "boundaryFilter" is a custom mode added via axis-proxy-patch.ts.
|
||||
// It rescales the Y-axis to the visible data while keeping one point
|
||||
// just outside each boundary to avoid line gaps at the zoom edges.
|
||||
filterMode: "boundaryFilter" as any,
|
||||
xAxisIndex: 0,
|
||||
moveOnMouseMove: !this._isTouchDevice || this._isZoomed,
|
||||
preventDefaultMouseMove: !this._isTouchDevice || this._isZoomed,
|
||||
@@ -877,10 +889,20 @@ export class HaChartBase extends LitElement {
|
||||
};
|
||||
}
|
||||
if (s.sampling === "minmax") {
|
||||
const minX =
|
||||
xAxis?.min && typeof xAxis.min === "number" ? xAxis.min : undefined;
|
||||
const maxX =
|
||||
xAxis?.max && typeof xAxis.max === "number" ? xAxis.max : undefined;
|
||||
const minX = xAxis?.min
|
||||
? xAxis.min instanceof Date
|
||||
? xAxis.min.getTime()
|
||||
: typeof xAxis.min === "number"
|
||||
? xAxis.min
|
||||
: undefined
|
||||
: undefined;
|
||||
const maxX = xAxis?.max
|
||||
? xAxis.max instanceof Date
|
||||
? xAxis.max.getTime()
|
||||
: typeof xAxis.max === "number"
|
||||
? xAxis.max
|
||||
: undefined
|
||||
: undefined;
|
||||
return {
|
||||
...s,
|
||||
sampling: undefined,
|
||||
@@ -1099,16 +1121,35 @@ export class HaChartBase extends LitElement {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.chart-controls {
|
||||
.top-controls {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 4px;
|
||||
top: var(--ha-space-4);
|
||||
inset-inline-start: var(--ha-space-4);
|
||||
inset-inline-end: var(--ha-space-1);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--ha-space-2);
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
::slotted([slot="search"]) {
|
||||
flex: 1 1 250px;
|
||||
min-width: 0;
|
||||
max-width: 250px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.chart-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-1);
|
||||
margin-inline-start: auto;
|
||||
flex-shrink: 0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.top-controls.small {
|
||||
top: 0;
|
||||
}
|
||||
.chart-controls.small {
|
||||
top: 0;
|
||||
flex-direction: row;
|
||||
}
|
||||
.chart-controls ha-icon-button,
|
||||
@@ -1156,6 +1197,9 @@ export class HaChartBase extends LitElement {
|
||||
.chart-legend.multiple-items li {
|
||||
max-width: 220px;
|
||||
}
|
||||
.chart-legend.multiple-items li:has(.value) {
|
||||
max-width: 300px;
|
||||
}
|
||||
.chart-legend .hidden {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
@@ -1164,6 +1208,12 @@ export class HaChartBase extends LitElement {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.chart-legend .value {
|
||||
color: var(--secondary-text-color);
|
||||
margin-inline-start: var(--ha-space-1);
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chart-legend .bullet {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { EChartsType } from "echarts/core";
|
||||
import type { GraphSeriesOption } from "echarts/charts";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
|
||||
import type {
|
||||
CallbackDataParams,
|
||||
TopLevelFormatterParams,
|
||||
@@ -76,8 +78,20 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
params: TopLevelFormatterParams
|
||||
) => string;
|
||||
|
||||
/**
|
||||
* Optional callback that returns additional searchable strings for a node.
|
||||
* These are matched against the search filter in addition to the node's name and context.
|
||||
*/
|
||||
@property({ attribute: false }) public searchableAttributes?: (
|
||||
nodeId: string
|
||||
) => string[];
|
||||
|
||||
@property({ attribute: false }) public searchFilter = "";
|
||||
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@state() private _highlightedNodes?: Set<string>;
|
||||
|
||||
@state() private _reducedMotion = false;
|
||||
|
||||
@state() private _physicsEnabled = true;
|
||||
@@ -117,6 +131,9 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||
).matches;
|
||||
|
||||
const hasHighlightedNodes =
|
||||
this._highlightedNodes && this._highlightedNodes.size > 0;
|
||||
|
||||
return html`<ha-chart-base
|
||||
.hass=${this.hass}
|
||||
.data=${this._getSeries(
|
||||
@@ -124,12 +141,14 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
this._physicsEnabled,
|
||||
this._reducedMotion,
|
||||
this._showLabels,
|
||||
isMobile
|
||||
isMobile,
|
||||
hasHighlightedNodes
|
||||
)}
|
||||
.options=${this._createOptions(this.data?.categories)}
|
||||
height="100%"
|
||||
.extraComponents=${[GraphChart]}
|
||||
>
|
||||
<slot name="search" slot="search"></slot>
|
||||
<slot name="button" slot="button"></slot>
|
||||
<ha-icon-button
|
||||
slot="button"
|
||||
@@ -165,7 +184,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
...category,
|
||||
icon: category.symbol,
|
||||
})),
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
},
|
||||
dataZoom: {
|
||||
type: "inside",
|
||||
@@ -175,13 +194,56 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
deepEqual
|
||||
);
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("searchFilter")) {
|
||||
const filter = this.searchFilter;
|
||||
if (!filter) {
|
||||
this._highlightedNodes = undefined;
|
||||
} else {
|
||||
const lowerFilter = filter.toLowerCase();
|
||||
const matchingIds = new Set<string>();
|
||||
for (const node of this.data.nodes) {
|
||||
if (this._nodeMatchesFilter(node, lowerFilter)) {
|
||||
matchingIds.add(node.id);
|
||||
}
|
||||
}
|
||||
this._highlightedNodes = matchingIds;
|
||||
}
|
||||
this._applyHighlighting();
|
||||
this._updateMouseoverHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private _nodeMatchesFilter(node: NetworkNode, lowerFilter: string): boolean {
|
||||
if (node.name?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
if (node.context?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
if (node.id?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
if (this.searchableAttributes) {
|
||||
const extraValues = this.searchableAttributes(node.id);
|
||||
for (const value of extraValues) {
|
||||
if (value?.toLowerCase().includes(lowerFilter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _getSeries = memoizeOne(
|
||||
(
|
||||
data: NetworkData,
|
||||
physicsEnabled: boolean,
|
||||
reducedMotion: boolean,
|
||||
showLabels: boolean,
|
||||
isMobile: boolean
|
||||
isMobile: boolean,
|
||||
hasHighlightedNodes?: boolean
|
||||
) => ({
|
||||
id: "network",
|
||||
type: "graph",
|
||||
@@ -214,7 +276,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
focus: isMobile ? "none" : "adjacency",
|
||||
focus: hasHighlightedNodes ? "self" : isMobile ? "none" : "adjacency",
|
||||
},
|
||||
force: {
|
||||
repulsion: [400, 600],
|
||||
@@ -362,6 +424,68 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
|
||||
});
|
||||
}
|
||||
|
||||
private _applyHighlighting() {
|
||||
const chart = this._baseChart?.chart;
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
// Reset all nodes to normal opacity first
|
||||
chart.dispatchAction({ type: "downplay" });
|
||||
|
||||
const highlighted = this._highlightedNodes;
|
||||
if (!highlighted || highlighted.size === 0) {
|
||||
return;
|
||||
}
|
||||
const dataIndices: number[] = [];
|
||||
this.data.nodes.forEach((node, index) => {
|
||||
if (highlighted.has(node.id)) {
|
||||
dataIndices.push(index);
|
||||
}
|
||||
});
|
||||
if (dataIndices.length > 0) {
|
||||
chart.dispatchAction({ type: "highlight", dataIndex: dataIndices });
|
||||
}
|
||||
}
|
||||
|
||||
private _emphasisGuardHandler?: () => void;
|
||||
|
||||
private _updateMouseoverHandler() {
|
||||
const chart = this._baseChart?.chart;
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When there are highlighted nodes, re-apply highlighting on hover
|
||||
// and mouseout to prevent hover from overriding the search state
|
||||
if (this._highlightedNodes && this._highlightedNodes.size > 0) {
|
||||
if (this._emphasisGuardHandler) {
|
||||
// Guard already set
|
||||
return;
|
||||
}
|
||||
this._emphasisGuardHandler = () => {
|
||||
this._applyHighlighting();
|
||||
};
|
||||
chart.on("mouseover", this._emphasisGuardHandler);
|
||||
chart.on("mouseout", this._emphasisGuardHandler);
|
||||
} else {
|
||||
if (!this._emphasisGuardHandler) {
|
||||
return;
|
||||
}
|
||||
chart.off("mouseover", this._emphasisGuardHandler);
|
||||
chart.off("mouseout", this._emphasisGuardHandler);
|
||||
this._emphasisGuardHandler = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
if (this._emphasisGuardHandler) {
|
||||
this._baseChart?.chart?.off("mouseover", this._emphasisGuardHandler);
|
||||
this._baseChart?.chart?.off("mouseout", this._emphasisGuardHandler);
|
||||
this._emphasisGuardHandler = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _togglePhysics() {
|
||||
this._saveNodePositions();
|
||||
this._physicsEnabled = !this._physicsEnabled;
|
||||
|
||||
@@ -28,6 +28,13 @@ const safeParseFloat = (value) => {
|
||||
return isFinite(parsed) ? parsed : null;
|
||||
};
|
||||
|
||||
const CLIMATE_MODE_CONFIGS = [
|
||||
{ mode: "heat", action: "heating", cssVar: "--state-climate-heat-color" },
|
||||
{ mode: "cool", action: "cooling", cssVar: "--state-climate-cool-color" },
|
||||
{ mode: "dry", action: "drying", cssVar: "--state-climate-dry-color" },
|
||||
{ mode: "fan_only", action: "fan", cssVar: "--state-climate-fan_only-color" },
|
||||
] as const;
|
||||
|
||||
@customElement("state-history-chart-line")
|
||||
export class StateHistoryChartLine extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -239,7 +246,9 @@ export class StateHistoryChartLine extends LitElement {
|
||||
changedProps.has("fitYData") ||
|
||||
changedProps.has("paddingYAxis") ||
|
||||
changedProps.has("_visualMap") ||
|
||||
changedProps.has("_yWidth")
|
||||
changedProps.has("_yWidth") ||
|
||||
(changedProps.has("hass") &&
|
||||
this._hasEntityStatesChanged(changedProps.get("hass")))
|
||||
) {
|
||||
const rtl = computeRTL(this.hass);
|
||||
let minYAxis: number | ((values: { min: number }) => number) | undefined =
|
||||
@@ -296,6 +305,19 @@ export class StateHistoryChartLine extends LitElement {
|
||||
legend: {
|
||||
type: "custom",
|
||||
show: this.showNames,
|
||||
data: this._chartData
|
||||
.map((d, i) => ({ dataset: d, entityId: this._entityIds[i] }))
|
||||
.filter((item) => !(item.dataset as LineSeriesOption).areaStyle)
|
||||
.map((item) => {
|
||||
const stateObj = this.hass.states[item.entityId];
|
||||
return {
|
||||
id: item.dataset.id as string,
|
||||
name: item.dataset.name as string,
|
||||
value: stateObj
|
||||
? this.hass.formatEntityState(stateObj)
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
},
|
||||
grid: {
|
||||
top: 15,
|
||||
@@ -316,6 +338,13 @@ export class StateHistoryChartLine extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _hasEntityStatesChanged(oldHass: HomeAssistant): boolean {
|
||||
return this._entityIds.some(
|
||||
(entityId) =>
|
||||
this.hass.states[entityId]?.state !== oldHass.states[entityId]?.state
|
||||
);
|
||||
}
|
||||
|
||||
private _generateData() {
|
||||
let colorIndex = 0;
|
||||
const computedStyles = getComputedStyle(this);
|
||||
@@ -407,23 +436,18 @@ export class StateHistoryChartLine extends LitElement {
|
||||
(entityState) => entityState.attributes?.hvac_action
|
||||
);
|
||||
|
||||
const isHeating =
|
||||
domain === "climate" && hasHvacAction
|
||||
? (entityState: LineChartState) =>
|
||||
CLIMATE_HVAC_ACTION_TO_MODE[
|
||||
entityState.attributes?.hvac_action
|
||||
] === "heat"
|
||||
: (entityState: LineChartState) => entityState.state === "heat";
|
||||
const isCooling =
|
||||
domain === "climate" && hasHvacAction
|
||||
? (entityState: LineChartState) =>
|
||||
CLIMATE_HVAC_ACTION_TO_MODE[
|
||||
entityState.attributes?.hvac_action
|
||||
] === "cool"
|
||||
: (entityState: LineChartState) => entityState.state === "cool";
|
||||
|
||||
const hasHeat = states.states.some(isHeating);
|
||||
const hasCool = states.states.some(isCooling);
|
||||
const activeModes = CLIMATE_MODE_CONFIGS.map(
|
||||
({ mode, action, cssVar }) => {
|
||||
const isActive =
|
||||
domain === "climate" && hasHvacAction
|
||||
? (entityState: LineChartState) =>
|
||||
CLIMATE_HVAC_ACTION_TO_MODE[
|
||||
entityState.attributes?.hvac_action
|
||||
] === mode
|
||||
: (entityState: LineChartState) => entityState.state === mode;
|
||||
return { action, cssVar, isActive };
|
||||
}
|
||||
).filter(({ isActive }) => states.states.some(isActive));
|
||||
// We differentiate between thermostats that have a target temperature
|
||||
// range versus ones that have just a target temperature
|
||||
|
||||
@@ -444,33 +468,19 @@ export class StateHistoryChartLine extends LitElement {
|
||||
"component.climate.entity_component._.state_attributes.current_temperature.name"
|
||||
)
|
||||
);
|
||||
if (hasHeat) {
|
||||
for (const { action, cssVar } of activeModes) {
|
||||
addDataSet(
|
||||
states.entity_id + "-heating",
|
||||
`${states.entity_id}-${action}`,
|
||||
this.showNames
|
||||
? this.hass.localize("ui.card.climate.heating", { name: name })
|
||||
? this.hass.localize(`ui.card.climate.${action}`, {
|
||||
name: name,
|
||||
})
|
||||
: this.hass.localize(
|
||||
"component.climate.entity_component._.state_attributes.hvac_action.state.heating"
|
||||
`component.climate.entity_component._.state_attributes.hvac_action.state.${action}`
|
||||
),
|
||||
computedStyles.getPropertyValue("--state-climate-heat-color"),
|
||||
computedStyles.getPropertyValue(cssVar),
|
||||
true
|
||||
);
|
||||
// The "heating" series uses steppedArea to shade the area below the current
|
||||
// temperature when the thermostat is calling for heat.
|
||||
}
|
||||
if (hasCool) {
|
||||
addDataSet(
|
||||
states.entity_id + "-cooling",
|
||||
this.showNames
|
||||
? this.hass.localize("ui.card.climate.cooling", { name: name })
|
||||
: this.hass.localize(
|
||||
"component.climate.entity_component._.state_attributes.hvac_action.state.cooling"
|
||||
),
|
||||
computedStyles.getPropertyValue("--state-climate-cool-color"),
|
||||
true
|
||||
);
|
||||
// The "cooling" series uses steppedArea to shade the area below the current
|
||||
// temperature when the thermostat is calling for heat.
|
||||
}
|
||||
|
||||
if (hasTargetRange) {
|
||||
@@ -518,11 +528,8 @@ export class StateHistoryChartLine extends LitElement {
|
||||
entityState.attributes.current_temperature
|
||||
);
|
||||
const series = [curTemp];
|
||||
if (hasHeat) {
|
||||
series.push(isHeating(entityState) ? curTemp : null);
|
||||
}
|
||||
if (hasCool) {
|
||||
series.push(isCooling(entityState) ? curTemp : null);
|
||||
for (const { isActive } of activeModes) {
|
||||
series.push(isActive(entityState) ? curTemp : null);
|
||||
}
|
||||
if (hasTargetRange) {
|
||||
const targetHigh = safeParseFloat(
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
getDisplayUnit,
|
||||
getStatisticLabel,
|
||||
getStatisticMetadata,
|
||||
isExternalStatistic,
|
||||
statisticsHaveType,
|
||||
} from "../../data/recorder";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
@@ -398,7 +399,31 @@ export class StatisticsChart extends LitElement {
|
||||
endTime = new Date();
|
||||
}
|
||||
|
||||
let unit: string | undefined | null;
|
||||
// Check if we need to display most recent data. Allow 10m of leeway for "now",
|
||||
// because stats are 5 minute aggregated.
|
||||
// Use same now point for all statistics even if processing time means the
|
||||
// state value is actually from a slightly later time. Otherwise the points
|
||||
// end up separated slightly and disappear from the tooltips.
|
||||
const now = new Date();
|
||||
const displayCurrentState = now.getTime() - endTime.getTime() <= 600000;
|
||||
|
||||
// Try to determine chart unit if it has not already been set explicitly
|
||||
if (!this.unit) {
|
||||
let unit: string | undefined | null;
|
||||
statisticsData.forEach(([statistic_id, _stats]) => {
|
||||
const meta = statisticsMetaData?.[statistic_id];
|
||||
const statisticUnit = getDisplayUnit(this.hass, statistic_id, meta);
|
||||
if (unit === undefined) {
|
||||
unit = statisticUnit;
|
||||
} else if (unit !== null && unit !== statisticUnit) {
|
||||
// Clear unit if not all statistics have same unit
|
||||
unit = null;
|
||||
}
|
||||
});
|
||||
if (unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
const names = this.names || {};
|
||||
statisticsData.forEach(([statistic_id, stats]) => {
|
||||
@@ -408,18 +433,6 @@ export class StatisticsChart extends LitElement {
|
||||
name = getStatisticLabel(this.hass, statistic_id, meta);
|
||||
}
|
||||
|
||||
if (!this.unit) {
|
||||
if (unit === undefined) {
|
||||
unit = getDisplayUnit(this.hass, statistic_id, meta);
|
||||
} else if (
|
||||
unit !== null &&
|
||||
unit !== getDisplayUnit(this.hass, statistic_id, meta)
|
||||
) {
|
||||
// Clear unit if not all statistics have same unit
|
||||
unit = null;
|
||||
}
|
||||
}
|
||||
|
||||
// array containing [value1, value2, etc]
|
||||
let prevValues: (number | null)[][] | null = null;
|
||||
let prevEndTime: Date | undefined;
|
||||
@@ -507,7 +520,6 @@ export class StatisticsChart extends LitElement {
|
||||
id: `${statistic_id}-${type}`,
|
||||
type: this.chartType,
|
||||
smooth: this.chartType === "line" ? 0.4 : false,
|
||||
smoothMonotone: "x",
|
||||
cursor: "default",
|
||||
data: [],
|
||||
name: name
|
||||
@@ -544,7 +556,7 @@ export class StatisticsChart extends LitElement {
|
||||
(series as LineSeriesOption).areaStyle = undefined;
|
||||
} else {
|
||||
series.stackOrder = "seriesAsc";
|
||||
if (drawBands && type === bandTop) {
|
||||
if (type === bandTop) {
|
||||
(series as LineSeriesOption).areaStyle = {
|
||||
color: color + "3F",
|
||||
};
|
||||
@@ -613,10 +625,10 @@ export class StatisticsChart extends LitElement {
|
||||
}
|
||||
});
|
||||
|
||||
// Close out the last stat segment at prevEndTime
|
||||
// For line charts, close out the last stat segment at prevEndTime
|
||||
const lastEndTime = prevEndTime;
|
||||
const lastValues = prevValues;
|
||||
if (lastEndTime && lastValues) {
|
||||
if (this.chartType === "line" && lastEndTime && lastValues) {
|
||||
statDataSets.forEach((d, i) => {
|
||||
d.data!.push(
|
||||
this._transformDataValue([lastEndTime, ...lastValues[i]!])
|
||||
@@ -624,13 +636,14 @@ export class StatisticsChart extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
// Append current state if viewing recent data
|
||||
const now = new Date();
|
||||
// allow 10m of leeway for "now", because stats are 5 minute aggregated
|
||||
const isUpToNow = now.getTime() - endTime.getTime() <= 600000;
|
||||
if (isUpToNow) {
|
||||
// Skip external statistics (they have ":" in the ID)
|
||||
if (!statistic_id.includes(":")) {
|
||||
// Show current state if required, and units match (or are unknown)
|
||||
const statisticUnit = getDisplayUnit(this.hass, statistic_id, meta);
|
||||
if (
|
||||
displayCurrentState &&
|
||||
(!this.unit || !statisticUnit || this.unit === statisticUnit)
|
||||
) {
|
||||
// Skip external statistics
|
||||
if (!isExternalStatistic(statistic_id)) {
|
||||
const stateObj = this.hass.states[statistic_id];
|
||||
if (stateObj) {
|
||||
const currentValue = parseFloat(stateObj.state);
|
||||
@@ -640,11 +653,12 @@ export class StatisticsChart extends LitElement {
|
||||
) {
|
||||
// Then push the current state at now
|
||||
statTypes.forEach((type, i) => {
|
||||
const val: (number | null)[] = [];
|
||||
if (type === "sum" || type === "change") {
|
||||
// Skip cumulative types - need special calculation
|
||||
val.push(null);
|
||||
} else if (
|
||||
// Skip cumulative types - need special calculation.
|
||||
return;
|
||||
}
|
||||
const val: (number | null)[] = [];
|
||||
if (
|
||||
type === bandTop &&
|
||||
this.chartType === "line" &&
|
||||
drawBands &&
|
||||
@@ -670,10 +684,6 @@ export class StatisticsChart extends LitElement {
|
||||
Array.prototype.push.apply(legendData, statLegendData);
|
||||
});
|
||||
|
||||
if (unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
legendData.forEach(({ id, name, color, borderColor }) => {
|
||||
// Add an empty series for the legend
|
||||
totalDataSets.push({
|
||||
|
||||
@@ -27,7 +27,7 @@ import type { HomeAssistant } from "../../types";
|
||||
import "../ha-checkbox";
|
||||
import type { HaCheckbox } from "../ha-checkbox";
|
||||
import "../ha-svg-icon";
|
||||
import "../search-input";
|
||||
import "../input/ha-input-search";
|
||||
import { filterData, sortData } from "./sort-filter";
|
||||
|
||||
export interface RowClickedEvent {
|
||||
@@ -391,11 +391,11 @@ export class HaDataTable extends LitElement {
|
||||
${this._filterable
|
||||
? html`
|
||||
<div class="table-header">
|
||||
<search-input
|
||||
.hass=${this.hass}
|
||||
@value-changed=${this._handleSearchChange}
|
||||
.label=${this.searchLabel}
|
||||
></search-input>
|
||||
<ha-input-search
|
||||
appearance="outlined"
|
||||
@input=${this._handleSearchChange}
|
||||
.placeholder=${this.searchLabel}
|
||||
></ha-input-search>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
@@ -970,12 +970,12 @@ export class HaDataTable extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleSearchChange(ev: CustomEvent): void {
|
||||
private _handleSearchChange(ev: InputEvent): void {
|
||||
if (this.filter) {
|
||||
return;
|
||||
}
|
||||
this._lastSelectedRowId = null;
|
||||
this._debounceSearch(ev.detail.value);
|
||||
this._debounceSearch((ev.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
private async _calcTableHeight() {
|
||||
@@ -1388,11 +1388,9 @@ export class HaDataTable extends LitElement {
|
||||
.table-header {
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
search-input {
|
||||
display: block;
|
||||
ha-input-search {
|
||||
flex: 1;
|
||||
--mdc-text-field-fill-color: var(--sidebar-background-color);
|
||||
--mdc-text-field-idle-line-color: transparent;
|
||||
padding: var(--ha-space-3);
|
||||
}
|
||||
slot[name="header"] {
|
||||
display: block;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user