Compare commits

...

101 Commits

Author SHA1 Message Date
Steve Repsher
f4bfcc6a69 Remove uncompressed files from bundles and compress source maps 2024-02-19 17:51:06 +00:00
renovate[bot]
50cf6d2af9 Lock file maintenance (#19832)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-19 12:34:52 -05:00
karwosts
86626b1855 Localize Long Lived Token Dialog (#19830) 2024-02-19 10:06:33 +01:00
renovate[bot]
63603a281e Update dependency webpack to v5.90.2 (#19828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-18 16:01:25 -05:00
renovate[bot]
faf05f5339 Update dependency @bundle-stats/plugin-webpack-filter to v4.10.0 (#19825)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-18 00:42:48 -05:00
Paul Bottein
4de3db52cb Remove cancel button on edit view dialog (#19821) 2024-02-17 20:33:50 -05:00
renovate[bot]
9a9fbda08b Update dependency core-js to v3.36.0 (#19822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 13:09:59 -05:00
renovate[bot]
ea642515c1 Update dependency husky to v9.0.11 (#19818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-17 13:06:30 -05:00
renovate[bot]
add2dedc7f Update dependency hls.js to v1.5.5 (#19817) 2024-02-16 20:30:51 -05:00
Simon Lamon
4ba4a28aa0 Bring back backend translations in devcontainer (#19793)
* Bring back translations

* Apply feedback

Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>

---------

Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2024-02-16 12:41:53 -05:00
Bram Kragten
7050453783 Check active matter fabric by index (#19692)
Check active fabric by id
2024-02-16 17:13:43 +01:00
renovate[bot]
8f984517bb Update typescript-eslint monorepo to v7 (major) (#19810)
Update typescript-eslint monorepo to v7

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 10:05:05 -05:00
karwosts
2524c96db6 Localize system log detail (#19813) 2024-02-16 16:03:35 +01:00
karwosts
26600e3d78 Compact legend mode for statistics-graph-card (#19089)
* Compact legend mode for statistics-graph-card

* remove clearable from selector

* Remove legend_mode, make compact default
2024-02-16 16:02:42 +01:00
renovate[bot]
316756d06a Update dependency eslint-plugin-unused-imports to v3.1.0 (#19814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-16 10:00:56 -05:00
Tucker Kern
7357b914d0 Add fan preset modes feature to Tile cards (#19618)
* Add support for fan preset modes in Tile cards.

Convert climate-preset-mode-card-feature to a more generic preset-modes-card-feature that supports fan entitys.

* Split fan and climate preset modes features

* Restore climate-preset-modes as an editable feature
2024-02-16 15:04:16 +01:00
karwosts
8548c9767b Localize assist pipeline debug (#19812)
localize assist pipeline debug
2024-02-16 10:47:24 +01:00
renovate[bot]
a30a35f82f Update dependency chai to v5.1.0 (#19809) 2024-02-15 19:26:46 -05:00
karwosts
84938ccc94 stopPropagation on automation moveUp/moveDown (#19804)
* stopPropagation on automation moveUp/moveDown

* also conditions and triggers
2024-02-15 10:04:40 +01:00
Bram Kragten
8136cc8008 Cloud: allow remote enable (#19691)
* Cloud: allow remote enable

* core does this
2024-02-14 15:05:18 +01:00
PolishKrowa
2dc9d268ec Allowed ObjectSelector to work in forms (#19761) 2024-02-14 09:16:24 +00:00
Steve Repsher
226dad309c Install libpcap for core in devcontainers (#19787) 2024-02-14 10:08:14 +01:00
Paul Bottein
33cdd51f00 Improve lovelace card events (#19785) 2024-02-14 10:07:50 +01:00
karwosts
a3a099126e Fix to display correct weather attribute unit (#19750) 2024-02-14 10:03:04 +01:00
Jeremy Noesen
4e22fea6e2 Fix status text centering for cast launch screen (#19748) 2024-02-14 09:57:39 +01:00
Yosi Levy
fd06f28253 Patch simple tooltip for RTL (#19767) 2024-02-14 09:57:10 +01:00
Paulus Schoutsen
553230ca23 Change how external QR code scanning works (#19743) 2024-02-14 09:56:02 +01:00
karwosts
208bfebc12 Fix chart alignments (#19788) 2024-02-13 09:24:02 +01:00
Jan Čermák
802b0949ac Fix My redirect for supervisor_logs (#19780)
My link for supervisor_logs redirected to common logs after the removal
of Supervisor panel. Leverage provider param added in #19355 to show
Supervisor logs directly.
2024-02-13 09:23:04 +01:00
Yosi Levy
b65dc47f72 Drawer fix (#19789) 2024-02-13 09:22:19 +01:00
renovate[bot]
c5a3670838 Update dependency prettier to v3.2.5 (#19711)
* Update dependency prettier to v3.2.5

* Reformat

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2024-02-12 22:53:41 +00:00
renovate[bot]
cd167ac645 Update dependency @codemirror/view to v6.24.0 (#19781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-12 13:12:23 -05:00
dependabot[bot]
eb3e756637 Bump actions/setup-node from 4.0.1 to 4.0.2 (#19777) 2024-02-12 08:23:29 +01:00
dependabot[bot]
5049210524 Bump actions/upload-artifact from 4.3.0 to 4.3.1 (#19778) 2024-02-12 07:51:26 +01:00
renovate[bot]
eeaad86c4b Update vaadinWebComponents monorepo to v24.3.6 (#19771) 2024-02-11 22:25:11 -05:00
renovate[bot]
71483e0bc7 Update dependency mocha to v10.3.0 (#19772) 2024-02-11 22:23:51 -05:00
renovate[bot]
10650e8937 Update dependency marked to v12 (#19706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-10 18:19:33 -05:00
renovate[bot]
99d72ba817 Update dependency hls.js to v1.5.4 (#19757)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-10 17:58:45 -05:00
renovate[bot]
e21ad742b1 Update dependency @codemirror/search to v6.5.6 (#19762)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-10 17:56:50 -05:00
renovate[bot]
1bcb1e7768 Update dependency magic-string to v0.30.7 (#19744)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 15:28:26 -05:00
renovate[bot]
618fee98ce Update typescript-eslint monorepo to v6.21.0 (#19747)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 15:27:25 -05:00
renovate[bot]
83da89437f Update dependency lint-staged to v15.2.2 (#19737)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-08 20:19:53 +01:00
Bram Kragten
fac82fa185 Revert "Bumped version to 20240208.0"
This reverts commit 17bd7f9476.
2024-02-08 18:08:35 +01:00
Bram Kragten
17bd7f9476 Bumped version to 20240208.0 2024-02-08 17:45:37 +01:00
Bram Kragten
045ff7a45e cast allow empty view, pick first (#19739) 2024-02-08 17:43:50 +01:00
Bram Kragten
8624853ec4 Fix cast launch screen (#19738) 2024-02-08 17:40:43 +01:00
Bram Kragten
336376d2a5 Fix icons in gallery, demo and cast (#19732)
* Fix icons in gallery and demo

* Add to lovelace gallery

* Update icons.ts

* add BACKWARDS_COMPAT support for icons

* Update entity-state.ts

* Update icons.ts

* Update icons.ts
2024-02-08 17:04:05 +01:00
Paul Bottein
189793bff4 Fix demo dashboard (#19734) 2024-02-08 15:40:03 +01:00
Paul Bottein
1e35f973d6 Fix suggest card dialog (#19735) 2024-02-08 15:38:55 +01:00
Bram Kragten
6033f8b31a Add QR and constant selector to gallery (#19730)
* Add QR and constant selector to gallery

* Update ha-selector.ts
2024-02-08 14:38:08 +01:00
Bram Kragten
c3b2ebf380 Don't use ES5 build for dev server (#19731) 2024-02-08 14:33:34 +01:00
Bram Kragten
23cbecb2c4 Add service icons to traces (again :D) (#19728)
* Add service icons to traces (again :D)

* Update hat-graph-node.ts
2024-02-08 13:33:10 +01:00
Paul Bottein
b1e1b44c75 Use rgb theme variables for qrcode (#19726) 2024-02-08 12:04:23 +01:00
Paul Bottein
abb014745a Show icon of disabled entities (#19717) 2024-02-07 19:42:39 +01:00
Bram Kragten
e51c98e1a7 Improve matter ping dialog (#19715) 2024-02-07 15:52:04 +01:00
Paul Bottein
9513699332 Use gap instead of margin for vertical and horizontal card (#19699) 2024-02-07 15:51:01 +01:00
Bram Kragten
b57bc8cd06 Matter cleanup on close dialog (#19714) 2024-02-07 14:24:14 +00:00
Bram Kragten
d61fc9ec6c Bumped version to 20240207.0 2024-02-07 11:21:33 +01:00
Paul Bottein
d1592bf262 Fix ha-state-badge name in picture element card (#19703)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2024-02-07 10:15:10 +01:00
karwosts
3c744c09f1 Fix plant battery level icon (#19704)
* Fix plant battery level icon

* cleanup

* cleanup
2024-02-06 15:42:23 +01:00
Cody C
3ef61aaf02 Move cloud login forgot password link into card actions area (#19642)
* Move cloud login forgot password link into card actions area

Aligns login forgot password link styling with create resend confirmation styling.

* Amend cloud-login buttons / links to show on single line
2024-02-06 15:30:55 +01:00
Paul Bottein
c738127c09 Fix horizontal stack margin (#19700) 2024-02-06 11:36:22 +01:00
Paulus Schoutsen
6e62f568fc Include climate attributes in history download (#19667)
* Include climate attributes in history download

* Add attributes for water heater and humidifier

* Simplify typing
2024-02-06 10:36:47 +01:00
Bram Kragten
2ba3a991a9 Improve matter share device dialog (#19693)
* Improve matter share device dialog

* Add border

* Update dialog-matter-open-commissioning-window.ts
2024-02-05 18:15:10 +01:00
Bram Kragten
55c6d3a7c4 Bumped version to 20240205.0 2024-02-05 14:45:17 +01:00
renovate[bot]
242f3813bc Update vaadinWebComponents monorepo to v24.3.5 (#19684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 13:32:18 +00:00
renovate[bot]
aa93cb17a7 Update dependency @lit-labs/motion to v1.0.7 (#19650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 13:30:44 +00:00
renovate[bot]
4692d885d1 Update dependency @codemirror/language to v6.10.1 (#19688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 13:30:21 +00:00
Cody C
b39ac984f9 Fix last input margins in supervisor-network.ts (#19659)
* Fix last input margins in supervisor-network.ts

Resolves #19658

* Update src/panels/config/network/supervisor-network.ts

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2024-02-05 13:28:01 +00:00
renovate[bot]
9894d83e22 Update Yarn to v4.1.0 (#19634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-05 14:12:49 +01:00
Cody C
113083a241 Avoid unnecessary redirect on empty state card page (#19643)
Avoid unnecessary redirect on hui-empty-state-card page
2024-02-05 13:53:52 +01:00
Cody C
32971cc875 Remove flag that sets icon as required when creating a new dashboard (#19640)
Less friction to create a new dashboard. Moreover, an icon isn't actually required in order to create the dashboard anyway. See https://github.com/home-assistant/frontend/issues/19639
2024-02-05 13:41:37 +01:00
Bram Kragten
137f59feb1 Remove comments from css (#19689)
remove comments from css
2024-02-05 13:39:47 +01:00
Paulus Schoutsen
6675121b85 Allow deselecting all values (#19677) 2024-02-05 12:48:18 +01:00
Paul Bottein
aed0a35c9c Fix button card color when off (#19685) 2024-02-05 12:47:23 +01:00
Paul Bottein
65a8518d99 Increase drop zone for automation editor (#19687) 2024-02-05 12:35:34 +01:00
dependabot[bot]
cb690e9d4e Bump release-drafter/release-drafter from 5 to 6 (#19683)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2024-02-05 08:59:50 +01:00
renovate[bot]
5da67de95f Update dependency magic-string to v0.30.6 (#19651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 21:04:01 +01:00
renovate[bot]
b9609f2154 Update dependency husky to v9.0.10 (#19674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 21:01:29 +01:00
renovate[bot]
aaabb6e1fb Update dependency webpack to v5.90.1 (#19675)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 20:35:46 +01:00
renovate[bot]
6561de34f0 Update dependency husky to v9.0.9 (#19672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 16:36:32 +01:00
Cody C
016ff74483 Fix grammar of Storage area messages (#19644) 2024-02-03 14:14:01 +01:00
renovate[bot]
f5e9839b42 Update dependency lint-staged to v15.2.1 (#19646)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-03 14:12:04 +01:00
Bram Kragten
eb3b168975 Bumped version to 20240202.0 2024-02-02 16:28:10 +01:00
Bram Kragten
aa400ce6ab Reload entity component icons when missing (#19629)
* Reload entity component icons when missing

* Improve typing, improve caching

* Make copy

* review suggestion

* overload

* Update icons.ts

* Update icons.ts
2024-02-02 16:26:47 +01:00
Paulus Schoutsen
682f9a0f04 Clean up the overlapping history in the CSV download (#19622)
* Clean up the overlapping history in the CSV download

* Speed up merge

* undefined unit

* Fix targetPickerValue handling
2024-02-02 16:06:29 +01:00
Paulus Schoutsen
e478038206 Remove refresh button from history panel (#19631) 2024-02-02 15:21:29 +01:00
Paul Bottein
259a9a4f58 Fix assist devices search bar color (#19627) 2024-02-02 12:12:43 +01:00
Bram Kragten
b08d1ae7e9 Update button focus style to match tile focus style (#19608) 2024-02-02 12:02:54 +01:00
renovate[bot]
3970fdd070 Update dependency hls.js to v1.5.3 (#19619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-02 11:46:33 +01:00
renovate[bot]
946445b2df Update dependency eslint-plugin-lit-a11y to v4.1.2 (#19626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-02 11:45:09 +01:00
renovate[bot]
17b090af58 Update typescript-eslint monorepo to v6.20.0 (#19613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-02 11:44:43 +01:00
karwosts
6690a0e4b1 Add names to map path tooltip (#19565)
* Add names to map path tooltip

* Apply suggestions from code review

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* prettier

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2024-02-02 11:40:31 +01:00
Paul Bottein
c291448ffa Improve automation drag and drop interaction (#19624) 2024-02-02 11:31:01 +01:00
renovate[bot]
6f831699be Update dependency husky to v9.0.7 (#19612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-02 09:37:14 +01:00
Bram Kragten
fb73bfb964 Only show more info on graphs when mouse is used (#19606)
only show more info on graphs when mouse is used
2024-02-01 17:57:08 +01:00
Bram Kragten
28a0d216f9 Fix race in lovelace (#19609) 2024-02-01 17:56:46 +01:00
Bram Kragten
69f2566526 Matter tweaks (#19607) 2024-02-01 17:56:37 +01:00
Yosi Levy
7b3797502a RTL simplification and updates (#19586)
* RTL simplification and updates

* Fixes

* Fix weird icon offset when mwc-list-item is in a concat html``

* Additional updates
2024-02-01 14:29:00 +01:00
Paul Bottein
cf960be07e Fix item path for default in choose automation action (#19602) 2024-02-01 14:05:01 +01:00
Paulus Schoutsen
8a410d6c82 Add error checking to download button (#19596)
Clean up history panel + add better error checking
2024-02-01 09:25:52 +01:00
178 changed files with 5827 additions and 3861 deletions

View File

@@ -2,12 +2,13 @@
"name": "Home Assistant Frontend",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"context": ".."
},
"appPort": "8124:8123",
"postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev",
"postStartCommand": "script/bootstrap",
"containerEnv": {
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}",
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
},
"customizations": {
"vscode": {
@@ -16,7 +17,7 @@
"esbenp.prettier-vscode",
"runem.lit-plugin",
"github.vscode-pull-request-github",
"eamodio.gitlens",
"eamodio.gitlens"
],
"settings": {
"files.eol": "\n",
@@ -27,17 +28,17 @@
"editor.renderWhitespace": "boundary",
"editor.rulers": [80],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.trimTrailingWhitespace": true,
"terminal.integrated.shell.linux": "/usr/bin/zsh",
"gitlens.showWelcomeOnInstall": false,
"gitlens.showWhatsNewAfterUpgrades": false,
"workbench.startupEditor": "none",
},
},
},
"workbench.startupEditor": "none"
}
}
}
}

View File

@@ -26,7 +26,7 @@ jobs:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -62,7 +62,7 @@ jobs:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -60,7 +60,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -78,7 +78,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -89,7 +89,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v4.3.1
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -102,7 +102,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -113,7 +113,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v4.3.1
with:
name: supervisor-bundle-stats
path: build/stats/*.json

View File

@@ -27,7 +27,7 @@ jobs:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -63,7 +63,7 @@ jobs:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v4.3.1
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.3.0
uses: actions/upload-artifact@v4.3.1
with:
name: translations
path: translations.tar.gz

View File

@@ -18,6 +18,6 @@ jobs:
pull-requests: read
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
- uses: release-drafter/release-drafter@v6.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -34,7 +34,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.0.1
uses: actions/setup-node@v4.0.2
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -0,0 +1,13 @@
diff --git a/simple-tooltip.js b/simple-tooltip.js
index 78a87f6a223925f0e29fbedb268c85a142ec6985..3d686dd6a3d5a93342b4b01408089fc316b408ca 100644
--- a/simple-tooltip.js
+++ b/simple-tooltip.js
@@ -195,6 +195,8 @@ class SimpleTooltip extends LitElement {
.hidden {
position: absolute;
left: -10000px;
+ inset-inline-start: -10000px;
+ inset-inline-end: initial;
top: auto;
width: 1px;
height: 1px;

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.0.2.cjs
yarnPath: .yarn/releases/yarn-4.1.0.cjs

View File

@@ -1,19 +1,26 @@
// Tasks to compress
import { deleteAsync } from "del";
import gulp from "gulp";
import gulpIf from "gulp-if";
import vinylPaths from "vinyl-paths";
import zopfli from "gulp-zopfli-green";
import paths from "../paths.cjs";
const zopfliOptions = { threshold: 150 };
const compressedExt = /\.gz$/;
const deleteUncompressed = (p) => deleteAsync(p.replace(compressedExt, ""));
const compressDist = (rootDir) =>
gulp
.src([
`${rootDir}/**/*.{js,json,css,svg,xml}`,
`${rootDir}/**/*.{js?(.map),json,css,svg,xml}`,
`${rootDir}/{authorize,onboarding}.html`,
])
.pipe(zopfli(zopfliOptions))
.pipe(gulp.dest(rootDir));
.pipe(gulp.dest(rootDir))
.pipe(gulpIf(compressedExt, vinylPaths(deleteUncompressed)));
gulp.task("compress-app", () => compressDist(paths.app_output_root));
gulp.task("compress-hassio", () => compressDist(paths.hassio_output_root));

View File

@@ -115,7 +115,9 @@ gulp.task("webpack-prod-app", () =>
gulp.task("webpack-dev-server-demo", () =>
runDevServer({
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
compiler: webpack(
createDemoConfig({ isProdBuild: false, latestBuild: true })
),
contentBase: paths.demo_output_root,
port: 8090,
})
@@ -131,7 +133,9 @@ gulp.task("webpack-prod-demo", () =>
gulp.task("webpack-dev-server-cast", () =>
runDevServer({
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
compiler: webpack(
createCastConfig({ isProdBuild: false, latestBuild: true })
),
contentBase: paths.cast_output_root,
port: 8080,
// Accessible from the network, because that's how Cast hits it.
@@ -174,8 +178,9 @@ gulp.task("webpack-prod-hassio", () =>
gulp.task("webpack-dev-server-gallery", () =>
runDevServer({
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
compiler: webpack(
createGalleryConfig({ isProdBuild: false, latestBuild: true })
),
contentBase: paths.gallery_output_root,
port: 8100,
listenHost: "0.0.0.0",

View File

@@ -28,7 +28,6 @@ class HcLaunchScreen extends LitElement {
:host {
display: block;
height: 100vh;
padding-top: 64px;
background-color: white;
font-size: 24px;
}
@@ -36,17 +35,13 @@ class HcLaunchScreen extends LitElement {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
height: 100%;
justify-content: space-evenly;
}
img {
width: 717px;
height: 376px;
display: block;
margin: 0 auto;
}
.status {
padding-right: 54px;
padding-inline-end: 54px;
padding-inline-start: initial;
max-width: 80%;
object-fit: cover;
}
`;
}

View File

@@ -17,7 +17,7 @@ class HcLovelace extends LitElement {
@property({ attribute: false })
public lovelaceConfig!: LovelaceConfig;
@property() public viewPath?: string | number;
@property() public viewPath?: string | number | null;
@property() public urlPath: string | null = null;
@@ -93,6 +93,9 @@ class HcLovelace extends LitElement {
}
private get _viewIndex() {
if (this.viewPath === null) {
return 0;
}
const selectedView = this.viewPath;
const selectedViewInt = parseInt(selectedView as string, 10);
for (let i = 0; i < this.lovelaceConfig.views.length; i++) {

View File

@@ -51,10 +51,10 @@ export class HcMain extends HassElement {
@state() private _lovelacePath: string | number | null = null;
@state() private _error?: string;
@state() private _urlPath?: string | null;
@state() private _error?: string;
private _hassUUID?: string;
private _unsubLovelace?: UnsubscribeFunc;
@@ -81,7 +81,7 @@ export class HcMain extends HassElement {
if (
!this._lovelaceConfig ||
this._lovelacePath === null ||
this._urlPath === undefined ||
// Guard against part of HA not being loaded yet.
!this.hass ||
!this.hass.states ||
@@ -99,8 +99,8 @@ export class HcMain extends HassElement {
<hc-lovelace
.hass=${this.hass}
.lovelaceConfig=${this._lovelaceConfig}
.viewPath=${this._lovelacePath}
.urlPath=${this._urlPath}
.viewPath=${this._lovelacePath}
@config-refresh=${this._generateDefaultLovelaceConfig}
></hc-lovelace>
`;
@@ -226,9 +226,9 @@ export class HcMain extends HassElement {
this.initializeHass(auth, connection);
if (this._hassUUID !== msg.hassUUID) {
this._hassUUID = msg.hassUUID;
this._lovelacePath = null;
this._urlPath = undefined;
this._lovelaceConfig = undefined;
this._urlPath = undefined;
this._lovelacePath = null;
if (this._unsubLovelace) {
this._unsubLovelace();
this._unsubLovelace = undefined;
@@ -285,7 +285,7 @@ export class HcMain extends HassElement {
],
};
this._urlPath = "energy";
this._lovelacePath = 0;
this._lovelacePath = null;
this._sendStatus();
return;
}

View File

@@ -17,12 +17,14 @@ import { energyEntities } from "./stubs/entities";
import { mockEntityRegistry } from "./stubs/entity_registry";
import { mockEvents } from "./stubs/events";
import { mockFrontend } from "./stubs/frontend";
import { mockIcons } from "./stubs/icons";
import { mockHistory } from "./stubs/history";
import { mockLovelace } from "./stubs/lovelace";
import { mockMediaPlayer } from "./stubs/media_player";
import { mockPersistentNotification } from "./stubs/persistent_notification";
import { mockRecorder } from "./stubs/recorder";
import { mockTodo } from "./stubs/todo";
import { mockSensor } from "./stubs/sensor";
import { mockSystemLog } from "./stubs/system_log";
import { mockTemplate } from "./stubs/template";
import { mockTranslations } from "./stubs/translations";
@@ -50,11 +52,13 @@ export class HaDemo extends HomeAssistantAppEl {
mockHistory(hass);
mockRecorder(hass);
mockTodo(hass);
mockSensor(hass);
mockSystemLog(hass);
mockTemplate(hass);
mockEvents(hass);
mockMediaPlayer(hass);
mockFrontend(hass);
mockIcons(hass);
mockEnergy(hass);
mockPersistentNotification(hass);
mockConfigEntries(hass);

33
demo/src/stubs/icons.ts Normal file
View File

@@ -0,0 +1,33 @@
import { IconCategory } from "../../../src/data/icons";
import { ENTITY_COMPONENT_ICONS } from "../../../src/fake_data/entity_component_icons";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockIcons = (hass: MockHomeAssistant) => {
hass.mockWS(
"frontend/get_icons",
async ({
category,
integration,
}: {
category: IconCategory;
integration?: string;
}) => {
if (integration) {
try {
const response = await fetch(
`https://raw.githubusercontent.com/home-assistant/core/dev/homeassistant/components/${integration}/icons.json`
).then((resp) => resp.json());
return { resources: { [integration]: response[category] || {} } };
} catch {
return { resources: {} };
}
}
if (category === "entity_component") {
return {
resources: ENTITY_COMPONENT_ICONS,
};
}
return { resources: {} };
}
);
};

58
demo/src/stubs/sensor.ts Normal file
View File

@@ -0,0 +1,58 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockSensor = (hass: MockHomeAssistant) => {
hass.mockWS("sensor/numeric_device_classes", () => [
{
numeric_device_classes: [
"volume_storage",
"gas",
"data_size",
"irradiance",
"wind_speed",
"volatile_organic_compounds",
"volatile_organic_compounds_parts",
"voltage",
"frequency",
"precipitation_intensity",
"volume",
"precipitation",
"battery",
"nitrogen_dioxide",
"speed",
"signal_strength",
"pm1",
"nitrous_oxide",
"atmospheric_pressure",
"data_rate",
"temperature",
"power_factor",
"aqi",
"current",
"volume_flow_rate",
"humidity",
"duration",
"ozone",
"distance",
"pressure",
"pm25",
"weight",
"energy",
"carbon_monoxide",
"apparent_power",
"illuminance",
"energy_storage",
"moisture",
"power",
"water",
"carbon_dioxide",
"ph",
"reactive_power",
"monetary",
"nitrogen_monoxide",
"pm10",
"sound_pressure",
"sulphur_dioxide",
],
},
]);
};

View File

@@ -21,4 +21,5 @@ export const mockTodo = (hass: MockHomeAssistant) => {
},
] as TodoItem[],
}));
hass.mockWS("todo/item/subscribe", (_msg, _hass) => () => {});
};

View File

@@ -1,5 +1,5 @@
import { Button } from "@material/mwc-button";
import { html, LitElement, css, TemplateResult } from "lit";
import { html, LitElement, css, TemplateResult, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -9,7 +9,7 @@ import "../../../src/components/ha-card";
class DemoBlackWhiteRow extends LitElement {
@property() title!: string;
@property() value!: any;
@property() value?: any;
@property({ type: Boolean }) public disabled = false;
@@ -45,7 +45,9 @@ class DemoBlackWhiteRow extends LitElement {
</mwc-button>
</div>
</ha-card>
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
${this.value
? html`<pre>${JSON.stringify(this.value, undefined, 2)}</pre>`
: nothing}
</div>
</div>
`;

View File

@@ -3,7 +3,6 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/trace/hat-script-graph";
import "../../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";

View File

@@ -275,6 +275,14 @@ const SCHEMAS: {
selector: { color_temp: {} },
},
color_rgb: { name: "Color", selector: { color_rgb: {} } },
qr_code: {
name: "QR Code",
selector: { qr_code: { data: "https://home-assistant.io" } },
},
constant: {
name: "Constant",
selector: { constant: { value: true, label: "Yes!" } },
},
},
},
{
@@ -501,7 +509,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
this.requestUpdate();
};
return html`
<demo-black-white-row .title=${info.name} .value=${this.data[idx]}>
<demo-black-white-row .title=${info.name}>
${["light", "dark"].map((slot) =>
Object.entries(info.input).map(
([key, value]) => html`
@@ -534,8 +542,8 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
}
static styles = css`
ha-selector {
width: 60;
ha-settings-row {
--paper-item-body-two-line-min-height: 0;
}
.options {
max-width: 800px;

View File

@@ -3,6 +3,7 @@ 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", {
@@ -84,6 +85,7 @@ class DemoAlarmPanelEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -146,6 +147,7 @@ class DemoArea extends LitElement {
entity_id: "binary_sensor.kitchen_door",
},
]);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -66,6 +67,7 @@ class DemoConditional extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -323,6 +324,7 @@ class DemoEntities extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -82,6 +83,7 @@ class DemoButtonEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -123,6 +124,7 @@ class DemoEntityFilter extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {}),
@@ -128,6 +129,7 @@ class DemoGaugeEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -238,6 +239,7 @@ class DemoGlanceEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -4,6 +4,7 @@ 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", {
@@ -214,6 +215,7 @@ class DemoStack extends LitElement {
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockHistory(hass);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -76,6 +77,7 @@ class DemoLightEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -138,6 +139,7 @@ class DemoPictureElements extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -93,6 +94,7 @@ class DemoPictureEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -134,6 +135,7 @@ class DemoPictureGlance extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { createPlantEntities } from "../../data/plants";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const CONFIGS = [
{
@@ -43,6 +44,7 @@ export class DemoPlantEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createPlantEntities());
mockIcons(hass);
}
}

View File

@@ -3,6 +3,7 @@ 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", {
@@ -116,6 +117,7 @@ class DemoThermostatEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -6,6 +6,7 @@ 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";
const ENTITIES = [
getEntity("switch", "tv_outlet", "on", {
@@ -184,6 +185,7 @@ class DemoTile extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}

View File

@@ -4,6 +4,7 @@ import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { mockTodo } from "../../../../demo/src/stubs/todo";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("todo", "shopping_list", "2", {
@@ -47,6 +48,7 @@ class DemoTodoListEntity extends LitElement {
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
mockTodo(hass);
}

View File

@@ -11,6 +11,7 @@ import "../../../../src/components/data-table/ha-data-table";
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
import "../../../../src/components/entity/state-badge";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { mockIcons } from "../../../../demo/src/stubs/icons";
import { HomeAssistant } from "../../../../src/types";
const SENSOR_DEVICE_CLASSES = [
@@ -291,6 +292,7 @@ const ENTITIES: HassEntity[] = [
createEntity("water_heater.high_demand", "high_demand"),
createEntity("water_heater.heat_pump", "heat_pump"),
createEntity("water_heater.gas", "gas"),
createEntity("select.speed", "ridiculous_speed"),
];
function createEntity(
@@ -397,6 +399,16 @@ export class DemoEntityState extends LitElement {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
const hass = provideHass(this);
mockIcons(hass);
hass.updateHass({
entities: {
"select.speed": {
entity_id: "select.speed",
translation_key: "speed",
platform: "demo",
},
},
});
hass.updateTranslations(null, "en");
hass.updateTranslations("config", "en");
}

View File

@@ -29,11 +29,11 @@
"@braintree/sanitize-url": "7.0.0",
"@codemirror/autocomplete": "6.12.0",
"@codemirror/commands": "6.3.3",
"@codemirror/language": "6.10.0",
"@codemirror/language": "6.10.1",
"@codemirror/legacy-modes": "6.3.3",
"@codemirror/search": "6.5.5",
"@codemirror/search": "6.5.6",
"@codemirror/state": "6.4.0",
"@codemirror/view": "6.23.1",
"@codemirror/view": "6.24.0",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.12.2",
"@formatjs/intl-displaynames": "6.6.6",
@@ -51,10 +51,10 @@
"@fullcalendar/timegrid": "6.1.10",
"@lezer/highlight": "1.2.0",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.6",
"@lit-labs/motion": "1.0.7",
"@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.12",
"@lrnwebcomponents/simple-tooltip": "8.0.0",
"@lrnwebcomponents/simple-tooltip": "patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-base": "0.27.0",
@@ -89,8 +89,8 @@
"@polymer/paper-toast": "3.0.1",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.3.4",
"@vaadin/vaadin-themable-mixin": "24.3.4",
"@vaadin/combo-box": "24.3.6",
"@vaadin/vaadin-themable-mixin": "24.3.6",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -100,7 +100,7 @@
"app-datepicker": "5.1.1",
"chart.js": "4.4.1",
"comlink": "4.4.1",
"core-js": "3.35.1",
"core-js": "3.36.0",
"cropperjs": "1.6.1",
"date-fns": "2.30.0",
"date-fns-tz": "2.0.0",
@@ -109,7 +109,7 @@
"element-internals-polyfill": "1.3.10",
"fuse.js": "7.0.0",
"google-timezones-json": "1.2.0",
"hls.js": "1.5.2",
"hls.js": "1.5.5",
"home-assistant-js-websocket": "9.1.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.5.11",
@@ -118,7 +118,7 @@
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.4.4",
"marked": "11.2.0",
"marked": "12.0.0",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
@@ -155,7 +155,7 @@
"@babel/plugin-transform-runtime": "7.23.9",
"@babel/preset-env": "7.23.9",
"@babel/preset-typescript": "7.23.3",
"@bundle-stats/plugin-webpack-filter": "4.9.2",
"@bundle-stats/plugin-webpack-filter": "4.10.0",
"@koa/cors": "5.0.0",
"@lokalise/node-api": "12.1.0",
"@octokit/auth-oauth-device": "6.0.1",
@@ -171,6 +171,7 @@
"@types/chromecast-caf-receiver": "6.0.13",
"@types/chromecast-caf-sender": "1.0.8",
"@types/glob": "8.1.0",
"@types/gulp-if": "^3",
"@types/html-minifier-terser": "7.0.2",
"@types/js-yaml": "4.0.9",
"@types/leaflet": "1.9.8",
@@ -183,13 +184,13 @@
"@types/tar": "6.1.11",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "6.19.1",
"@typescript-eslint/parser": "6.19.1",
"@typescript-eslint/eslint-plugin": "7.0.1",
"@typescript-eslint/parser": "7.0.1",
"@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3",
"babel-plugin-template-html-minifier": "4.1.0",
"chai": "5.0.3",
"chai": "5.1.0",
"del": "7.1.0",
"eslint": "8.56.0",
"eslint-config-airbnb-base": "15.0.0",
@@ -199,32 +200,33 @@
"eslint-plugin-disable": "2.0.3",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-lit": "1.11.0",
"eslint-plugin-lit-a11y": "4.1.1",
"eslint-plugin-unused-imports": "3.0.0",
"eslint-plugin-lit-a11y": "4.1.2",
"eslint-plugin-unused-imports": "3.1.0",
"eslint-plugin-wc": "2.0.4",
"fancy-log": "2.0.0",
"fs-extra": "11.2.0",
"glob": "10.3.10",
"gulp": "4.0.2",
"gulp-flatmap": "1.0.2",
"gulp-if": "3.0.0",
"gulp-json-transform": "0.4.8",
"gulp-merge-json": "2.1.2",
"gulp-rename": "2.0.0",
"gulp-zopfli-green": "6.0.1",
"html-minifier-terser": "7.2.0",
"husky": "9.0.6",
"husky": "9.0.11",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.2.0",
"lint-staged": "15.2.2",
"lit-analyzer": "2.0.3",
"lodash.template": "4.5.0",
"magic-string": "0.30.5",
"magic-string": "0.30.7",
"map-stream": "0.0.7",
"mocha": "10.2.0",
"mocha": "10.3.0",
"object-hash": "3.0.0",
"open": "10.0.3",
"pinst": "3.0.0",
"prettier": "3.2.4",
"prettier": "3.2.5",
"rollup": "2.79.1",
"rollup-plugin-string": "3.0.0",
"rollup-plugin-terser": "7.0.2",
@@ -239,8 +241,9 @@
"ts-lit-plugin": "2.0.2",
"typescript": "5.3.3",
"vinyl-buffer": "1.0.1",
"vinyl-paths": "5.0.0",
"vinyl-source-stream": "2.0.0",
"webpack": "5.90.0",
"webpack": "5.90.2",
"webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.1",
"webpack-manifest-plugin": "5.0.0",
@@ -258,5 +261,5 @@
"sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch",
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
},
"packageManager": "yarn@4.0.2"
"packageManager": "yarn@4.1.0"
}

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240131.0"
version = "20240207.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@@ -40,6 +40,7 @@ if [ -n "$ref" ]; then
echo "Installing Home Assistant core at ${ref}..."
python3 -m pip install --user --upgrade --src "$HOME/src" \
--editable "git+${coreURL}@${ref}#egg=homeassistant"
(cd ~/src/homeassistant && exec python3 -m script.translations develop --all)
fi
if [ ! -d "${WD}/config" ]; then

View File

@@ -53,9 +53,7 @@ export const computeAttributeValueDisplay = (
if (domain === "weather") {
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
}
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
} else if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
unit = config.unit_system.temperature;
}

View File

@@ -38,4 +38,8 @@ export function setDirectionStyles(direction: string, element: LitElement) {
"--margin-title",
direction === "ltr" ? "var(--margin-title-ltr)" : "var(--margin-title-rtl)"
);
element.style.setProperty(
"--scale-direction",
direction === "ltr" ? "1" : "-1"
);
}

View File

@@ -5,12 +5,19 @@ import type {
ChartOptions,
TooltipModel,
} from "chart.js";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import {
css,
CSSResultGroup,
html,
nothing,
LitElement,
PropertyValues,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../../common/dom/fire_event";
import { clamp } from "../../common/number/clamp";
import { computeRTL } from "../../common/util/compute_rtl";
import { HomeAssistant } from "../../types";
import { debounce } from "../../common/util/debounce";
@@ -28,6 +35,11 @@ interface Tooltip
left: string;
}
export interface ChartDatasetExtra {
show_legend?: boolean;
legend_label?: string;
}
@customElement("ha-chart-base")
export class HaChartBase extends LitElement {
public chart?: Chart;
@@ -39,6 +51,8 @@ export class HaChartBase extends LitElement {
@property({ attribute: false }) public data: ChartData = { datasets: [] };
@property({ attribute: false }) public extraData?: ChartDatasetExtra[];
@property({ attribute: false }) public options?: ChartOptions;
@property({ attribute: false }) public plugins?: any[];
@@ -47,6 +61,8 @@ export class HaChartBase extends LitElement {
@property({ type: Number }) public paddingYAxis = 0;
@property({ type: Boolean }) public externalHidden = false;
@state() private _chartHeight?: number;
@state() private _tooltip?: Tooltip;
@@ -149,6 +165,19 @@ export class HaChartBase extends LitElement {
}
}
if (changedProps.has("data")) {
if (this.externalHidden) {
this._hiddenDatasets = new Set();
if (this.data?.datasets) {
this.data.datasets.forEach((dataset, index) => {
if (dataset.hidden) {
this._hiddenDatasets.add(index);
}
});
}
}
}
if (!this.hasUpdated || !this.chart) {
return;
}
@@ -158,7 +187,7 @@ export class HaChartBase extends LitElement {
return;
}
if (changedProps.has("data")) {
if (this._hiddenDatasets.size) {
if (this._hiddenDatasets.size && !this.externalHidden) {
this.data.datasets.forEach((dataset, index) => {
dataset.hidden = this._hiddenDatasets.has(index);
});
@@ -176,25 +205,30 @@ export class HaChartBase extends LitElement {
${this.options?.plugins?.legend?.display === true
? html`<div class="chartLegend">
<ul>
${this.data.datasets.map(
(dataset, index) =>
html`<li
.datasetIndex=${index}
@click=${this._legendClick}
class=${classMap({
hidden: this._hiddenDatasets.has(index),
})}
.title=${dataset.label}
>
<div
class="bullet"
style=${styleMap({
backgroundColor: dataset.backgroundColor as string,
borderColor: dataset.borderColor as string,
${this.data.datasets.map((dataset, index) =>
this.extraData?.[index]?.show_legend === false
? nothing
: html`<li
.datasetIndex=${index}
@click=${this._legendClick}
class=${classMap({
hidden: this._hiddenDatasets.has(index),
})}
></div>
<div class="label">${dataset.label}</div>
</li>`
.title=${this.extraData?.[index]?.legend_label ??
dataset.label}
>
<div
class="bullet"
style=${styleMap({
backgroundColor: dataset.backgroundColor as string,
borderColor: dataset.borderColor as string,
})}
></div>
<div class="label">
${this.extraData?.[index]?.legend_label ??
dataset.label}
</div>
</li>`
)}
</ul>
</div>`
@@ -212,12 +246,10 @@ export class HaChartBase extends LitElement {
height: `${
this.height ?? this._chartHeight ?? this.clientWidth / 2
}px`,
"padding-left": `${
computeRTL(this.hass) ? 0 : this._paddingYAxisInternal
}px`,
"padding-right": `${
computeRTL(this.hass) ? this._paddingYAxisInternal : 0
}px`,
"padding-left": `${this._paddingYAxisInternal}px`,
"padding-right": 0,
"padding-inline-start": `${this._paddingYAxisInternal}px`,
"padding-inline-end": 0,
})}
>
<canvas></canvas>
@@ -342,9 +374,19 @@ export class HaChartBase extends LitElement {
if (this.chart.isDatasetVisible(index)) {
this.chart.setDatasetVisibility(index, false);
this._hiddenDatasets.add(index);
if (this.externalHidden) {
fireEvent(this, "dataset-hidden", {
index,
});
}
} else {
this.chart.setDatasetVisibility(index, true);
this._hiddenDatasets.delete(index);
if (this.externalHidden) {
fireEvent(this, "dataset-unhidden", {
index,
});
}
}
this.chart.update("none");
this.requestUpdate("_hiddenDatasets");
@@ -433,14 +475,6 @@ export class HaChartBase extends LitElement {
.chartTooltip .bullet {
align-self: baseline;
}
:host([rtl]) .chartLegend .bullet,
:host([rtl]) .chartTooltip .bullet {
margin-right: inherit;
margin-left: 6px;
margin-inline-end: inherit;
margin-inline-start: 6px;
direction: var(--direction);
}
.chartTooltip {
padding: 8px;
font-size: 90%;
@@ -449,12 +483,13 @@ export class HaChartBase extends LitElement {
color: white;
border-radius: 4px;
pointer-events: none;
z-index: 1000;
z-index: 1;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
width: 200px;
box-sizing: border-box;
}
:host([rtl]) .chartTooltip {
direction: rtl;
direction: var(--direction);
}
.chartLegend ul,
.chartTooltip ul {
@@ -496,4 +531,8 @@ declare global {
interface HTMLElementTagNameMap {
"ha-chart-base": HaChartBase;
}
interface HASSDomEvents {
"dataset-hidden": { index: number };
"dataset-unhidden": { index: number };
}
}

View File

@@ -220,7 +220,12 @@ export class StateHistoryChartLine extends LitElement {
// @ts-expect-error
locale: numberFormatToLocale(this.hass.locale),
onClick: (e: any) => {
if (!this.clickForMoreInfo) {
if (
!this.clickForMoreInfo ||
!(e.native instanceof MouseEvent) ||
(e.native instanceof PointerEvent &&
e.native.pointerType !== "mouse")
) {
return;
}

View File

@@ -224,7 +224,11 @@ export class StateHistoryChartTimeline extends LitElement {
// @ts-expect-error
locale: numberFormatToLocale(this.hass.locale),
onClick: (e: any) => {
if (!this.clickForMoreInfo) {
if (
!this.clickForMoreInfo ||
!(e.native instanceof MouseEvent) ||
(e.native instanceof PointerEvent && e.native.pointerType !== "mouse")
) {
return;
}

View File

@@ -32,7 +32,11 @@ import {
} from "../../data/recorder";
import type { HomeAssistant } from "../../types";
import "./ha-chart-base";
import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base";
import type {
ChartResizeOptions,
ChartDatasetExtra,
HaChartBase,
} from "./ha-chart-base";
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
mean: "mean",
@@ -79,10 +83,14 @@ export class StatisticsChart extends LitElement {
@state() private _chartData: ChartData = { datasets: [] };
@state() private _chartDatasetExtra: ChartDatasetExtra[] = [];
@state() private _statisticIds: string[] = [];
@state() private _chartOptions?: ChartOptions;
@state() private _hiddenStats = new Set<string>();
@query("ha-chart-base") private _chart?: HaChartBase;
private _computedStyle?: CSSStyleDeclaration;
@@ -96,6 +104,9 @@ export class StatisticsChart extends LitElement {
}
public willUpdate(changedProps: PropertyValues) {
if (changedProps.has("legendMode")) {
this._hiddenStats.clear();
}
if (
!this.hasUpdated ||
changedProps.has("unit") ||
@@ -110,7 +121,8 @@ export class StatisticsChart extends LitElement {
changedProps.has("statisticsData") ||
changedProps.has("statTypes") ||
changedProps.has("chartType") ||
changedProps.has("hideLegend")
changedProps.has("hideLegend") ||
changedProps.has("_hiddenStats")
) {
this._generateData();
}
@@ -145,14 +157,30 @@ export class StatisticsChart extends LitElement {
return html`
<ha-chart-base
externalHidden
.hass=${this.hass}
.data=${this._chartData}
.extraData=${this._chartDatasetExtra}
.options=${this._chartOptions}
.chartType=${this.chartType}
@dataset-hidden=${this._datasetHidden}
@dataset-unhidden=${this._datasetUnhidden}
></ha-chart-base>
`;
}
private _datasetHidden(ev) {
ev.stopPropagation();
this._hiddenStats.add(this._statisticIds[ev.detail.index]);
this.requestUpdate("_hiddenStats");
}
private _datasetUnhidden(ev) {
ev.stopPropagation();
this._hiddenStats.delete(this._statisticIds[ev.detail.index]);
this.requestUpdate("_hiddenStats");
}
private _createOptions(unit?: string) {
this._chartOptions = {
parsing: false,
@@ -274,6 +302,7 @@ export class StatisticsChart extends LitElement {
let colorIndex = 0;
const statisticsData = Object.entries(this.statisticsData);
const totalDataSets: ChartDataset<"line">[] = [];
const totalDatasetExtras: ChartDatasetExtra[] = [];
const statisticIds: string[] = [];
let endTime: Date;
@@ -324,6 +353,7 @@ export class StatisticsChart extends LitElement {
// The datasets for the current statistic
const statDataSets: ChartDataset<"line">[] = [];
const statDatasetExtras: ChartDatasetExtra[] = [];
const pushData = (
start: Date,
@@ -384,9 +414,20 @@ export class StatisticsChart extends LitElement {
})
: this.statTypes;
let displayed_legend = false;
sortedTypes.forEach((type) => {
if (statisticsHaveType(stats, type)) {
const band = drawBands && (type === "min" || type === "max");
if (!this.hideLegend) {
const show_legend = hasMean
? type === "mean"
: displayed_legend === false;
statDatasetExtras.push({
legend_label: name,
show_legend,
});
displayed_legend = displayed_legend || show_legend;
}
statTypes.push(type);
statDataSets.push({
label: name
@@ -408,6 +449,9 @@ export class StatisticsChart extends LitElement {
band && hasMean ? color + (this.hideLegend ? "00" : "7F") : color,
backgroundColor: band ? color + "3F" : color + "7F",
pointRadius: 0,
hidden: !this.hideLegend
? this._hiddenStats.has(statistic_id)
: false,
data: [],
// @ts-ignore
unit: meta?.unit_of_measurement,
@@ -446,6 +490,7 @@ export class StatisticsChart extends LitElement {
// Concat two arrays
Array.prototype.push.apply(totalDataSets, statDataSets);
Array.prototype.push.apply(totalDatasetExtras, statDatasetExtras);
});
if (unit) {
@@ -455,6 +500,7 @@ export class StatisticsChart extends LitElement {
this._chartData = {
datasets: totalDataSets,
};
this._chartDatasetExtra = totalDatasetExtras;
this._statisticIds = statisticIds;
}

View File

@@ -688,15 +688,12 @@ export class HaDataTable extends LitElement {
padding-left: 16px;
/* @noflip */
padding-right: 0;
/* @noflip */
padding-inline-start: 16px;
/* @noflip */
padding-inline-end: initial;
width: 60px;
}
:host([dir="rtl"]) .mdc-data-table__header-cell--checkbox,
:host([dir="rtl"]) .mdc-data-table__cell--checkbox {
/* @noflip */
padding-left: 0;
/* @noflip */
padding-right: 16px;
}
.mdc-data-table__table {
height: 100%;
@@ -723,11 +720,7 @@ export class HaDataTable extends LitElement {
}
.mdc-data-table__cell--numeric {
text-align: right;
}
:host([dir="rtl"]) .mdc-data-table__cell--numeric {
/* @noflip */
text-align: left;
text-align: var(--float-end);
}
.mdc-data-table__cell--icon {
@@ -753,15 +746,7 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(
.not-sorted
) {
text-align: left;
}
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(
.not-sorted
) {
text-align: right;
text-align: var(--float-start);
}
.mdc-data-table__cell--icon:first-child img,
@@ -771,27 +756,14 @@ export class HaDataTable extends LitElement {
.mdc-data-table__cell--icon:first-child ha-domain-icon,
.mdc-data-table__cell--icon:first-child ha-service-icon {
margin-left: 8px;
}
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child ha-icon,
:host([dir="rtl"])
.mdc-data-table__cell--icon:first-child
ha-state-icon,
:host([dir="rtl"])
.mdc-data-table__cell--icon:first-child
ha-svg-icon
:host([dir="rtl"])
.mdc-data-table__cell--icon:first-child
img {
margin-left: auto;
margin-right: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
.mdc-data-table__cell--icon:first-child state-badge {
margin-right: -8px;
}
:host([dir="rtl"]) .mdc-data-table__cell--icon:first-child state-badge {
margin-right: auto;
margin-left: -8px;
margin-inline-end: -8px;
margin-inline-start: initial;
}
.mdc-data-table__cell--overflow-menu,
@@ -824,15 +796,8 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--icon-button:first-child,
.mdc-data-table__cell--icon-button:first-child {
padding-left: 16px;
}
:host([dir="rtl"])
.mdc-data-table__header-cell--overflow-menu:first-child,
:host([dir="rtl"]) .mdc-data-table__cell--overflow-menu:first-child,
:host([dir="rtl"])
.mdc-data-table__header-cell--overflow-menu:first-child,
:host([dir="rtl"]) .mdc-data-table__cell--overflow-menu:first-child {
padding-left: 8px;
padding-right: 16px;
padding-inline-start: 16px;
padding-inline-end: initial;
}
.mdc-data-table__cell--overflow-menu:last-child,
@@ -840,14 +805,8 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--icon-button:last-child,
.mdc-data-table__cell--icon-button:last-child {
padding-right: 16px;
}
:host([dir="rtl"])
.mdc-data-table__header-cell--overflow-menu:last-child,
:host([dir="rtl"]) .mdc-data-table__cell--overflow-menu:last-child,
:host([dir="rtl"]) .mdc-data-table__header-cell--icon-button:last-child,
:host([dir="rtl"]) .mdc-data-table__cell--icon-button:last-child {
padding-right: 8px;
padding-left: 16px;
padding-inline-end: 16px;
padding-inline-start: initial;
}
.mdc-data-table__cell--overflow-menu,
.mdc-data-table__header-cell--overflow-menu {
@@ -867,28 +826,15 @@ export class HaDataTable extends LitElement {
letter-spacing: 0.0071428571em;
text-decoration: inherit;
text-transform: inherit;
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell {
/* @noflip */
text-align: right;
text-align: var(--float-start);
}
.mdc-data-table__header-cell--numeric {
text-align: right;
text-align: var(--float-end);
}
.mdc-data-table__header-cell--numeric.sortable:hover,
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell--numeric {
/* @noflip */
text-align: left;
}
:host([dir="rtl"]) .mdc-data-table__header-cell--numeric.sortable:hover,
:host([dir="rtl"])
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
text-align: right;
text-align: var(--float-start);
}
/* custom from here */
@@ -909,20 +855,15 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell span {
position: relative;
left: 0px;
}
:host([dir="rtl"]) .mdc-data-table__header-cell span {
left: auto;
right: 0px;
inset-inline-start: 0px;
inset-inline-end: initial;
}
.mdc-data-table__header-cell.sortable {
cursor: pointer;
}
.mdc-data-table__header-cell > * {
transition: left 0.2s ease;
}
:host([dir="rtl"]) .mdc-data-table__header-cell > * {
transition: right 0.2s ease;
transition: var(--float-start) 0.2s ease;
}
.mdc-data-table__header-cell ha-svg-icon {
top: -3px;
@@ -930,35 +871,20 @@ export class HaDataTable extends LitElement {
}
.mdc-data-table__header-cell.not-sorted ha-svg-icon {
left: -20px;
}
:host([dir="rtl"]) .mdc-data-table__header-cell.not-sorted ha-svg-icon {
right: -20px;
inset-inline-start: -20px;
inset-inline-end: initial;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) span,
.mdc-data-table__header-cell.sortable.not-sorted:hover span {
left: 24px;
}
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:not(.not-sorted)
span,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable.not-sorted:hover
span {
left: auto;
right: 24px;
inset-inline-start: 24px;
inset-inline-end: initial;
}
.mdc-data-table__header-cell.sortable:not(.not-sorted) ha-svg-icon,
.mdc-data-table__header-cell.sortable:hover.not-sorted ha-svg-icon {
left: 12px;
}
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:not(.not-sorted)
ha-svg-icon,
:host([dir="rtl"])
.mdc-data-table__header-cell.sortable:hover.not-sorted
ha-svg-icon {
left: auto;
right: 12px;
inset-inline-start: 12px;
inset-inline-end: initial;
}
.table-header {
border-bottom: 1px solid var(--divider-color);
@@ -966,6 +892,8 @@ export class HaDataTable extends LitElement {
search-input {
display: block;
flex: 1;
--mdc-text-field-fill-color: var(--sidebar-background-color);
--mdc-text-field-idle-line-color: transparent;
}
slot[name="header"] {
display: block;

View File

@@ -9,6 +9,7 @@ import {
localizeWeekdays,
localizeMonths,
} from "../common/datetime/localize_date";
import { mainWindow } from "../common/dom/get_main_window";
// Set the current date to the left picker instead of the right picker because the right is hidden
const CustomDateRangePicker = Vue.extend({
@@ -157,7 +158,7 @@ class DateRangePickerElement extends WrappedElement {
min-width: initial !important;
max-height: var(--date-range-picker-max-height);
overflow-y: auto;
}
}
.daterangepicker:before {
display: none;
}
@@ -267,15 +268,37 @@ class DateRangePickerElement extends WrappedElement {
.calendar-table {
padding: 0 !important;
}
.daterangepicker.ltr {
.calendar-time {
direction: ltr;
text-align: left;
}
.daterangepicker.ltr {
direction: var(--direction);
text-align: var(--float-start);
}
.vue-daterange-picker{
min-width: unset !important;
display: block !important;
}
`;
if (mainWindow.document.dir === "rtl") {
style.innerHTML += `
.daterangepicker .calendar-table .next span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker td.start-date {
border-radius: 0 50% 50% 0;
}
.daterangepicker td.end-date {
border-radius: 50% 0 0 50%;
}
`;
}
const shadowRoot = this.shadowRoot!;
shadowRoot.appendChild(style);
// Stop click events from reaching the document, otherwise it will close the picker immediately.

View File

@@ -133,9 +133,9 @@ export class HaStateLabelBadge extends LitElement {
entityState,
this._timerTimeRemaining
)}
.description=${this.showName === false
? undefined
: this.name ?? computeStateName(entityState)}
.description=${this.showName
? this.name ?? computeStateName(entityState)
: undefined}
>
${!image && showIcon
? html`<ha-state-icon

View File

@@ -3,7 +3,6 @@ import type { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { computeStateName } from "../../common/entity/compute_state_name";
import { computeRTL } from "../../common/util/compute_rtl";
import type { HomeAssistant } from "../../types";
import "../ha-relative-time";
import "./state-badge";
@@ -16,9 +15,6 @@ class StateInfo extends LitElement {
@property({ type: Boolean }) public inDialog = false;
// property used only in CSS
@property({ type: Boolean, reflect: true }) public rtl = false;
@property() public color?: string;
protected render() {
@@ -79,18 +75,6 @@ class StateInfo extends LitElement {
</div>`;
}
protected updated(changedProps) {
super.updated(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
this.rtl = computeRTL(this.hass);
}
}
static get styles(): CSSResultGroup {
return css`
:host {
@@ -106,17 +90,14 @@ class StateInfo extends LitElement {
.info {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
min-width: 0;
}
:host([rtl]) .info {
margin-right: 8px;
margin-left: 0;
text-align: right;
text-align: var(--float-start);
}
.name {

View File

@@ -69,6 +69,7 @@ export class HaButtonToggleGroup extends LitElement {
display: flex;
--mdc-icon-button-size: var(--button-toggle-size, 36px);
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
direction: ltr;
}
mwc-button {
--mdc-shape-small: 0;
@@ -119,19 +120,6 @@ export class HaButtonToggleGroup extends LitElement {
--mdc-shape-small: 4px;
border-right-width: 1px;
}
:host([dir="rtl"]) ha-icon-button:first-child,
:host([dir="rtl"]) mwc-button:first-child {
border-radius: 0 4px 4px 0;
border-right-width: 1px;
--mdc-shape-small: 0 4px 4px 0;
--mdc-button-outline-width: 1px;
}
:host([dir="rtl"]) ha-icon-button:last-child,
:host([dir="rtl"]) mwc-button:last-child {
--mdc-shape-small: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
`;
}
}

View File

@@ -32,7 +32,6 @@ import { firstWeekdayIndex } from "../common/datetime/first_weekday";
import { formatDate } from "../common/datetime/format_date";
import { formatDateTime } from "../common/datetime/format_date_time";
import { useAmPm } from "../common/datetime/use_am_pm";
import { computeRTLDirection } from "../common/util/compute_rtl";
import { HomeAssistant } from "../types";
import "./date-range-picker";
import "./ha-icon-button";
@@ -65,8 +64,6 @@ export class HaDateRangePicker extends LitElement {
@state() private _hour24format = false;
@state() private _rtlDirection = "ltr";
@property({ type: Boolean }) public extendedPresets = false;
@property() public openingDirection?: "right" | "left" | "center" | "inline";
@@ -236,7 +233,6 @@ export class HaDateRangePicker extends LitElement {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
this._hour24format = !useAmPm(this.hass.locale);
this._rtlDirection = computeRTLDirection(this.hass);
}
}
}
@@ -306,11 +302,7 @@ export class HaDateRangePicker extends LitElement {
></ha-icon-button>`}
</div>
${this.ranges !== false && (this.ranges || this._ranges)
? html`<div
slot="ranges"
class="date-range-ranges"
.dir=${this._rtlDirection}
>
? html`<div slot="ranges" class="date-range-ranges">
<mwc-list @action=${this._setDateRange} activatable>
${Object.keys(this.ranges || this._ranges!).map(
(name) => html`<mwc-list-item>${name}</mwc-list-item>`

View File

@@ -12,6 +12,8 @@ export class HaDrawer extends DrawerBase {
private _mc?: HammerManager;
private _rtlStyle?: HTMLElement;
protected createAdapter() {
return {
...super.createAdapter(),
@@ -32,7 +34,26 @@ export class HaDrawer extends DrawerBase {
super.updated(changedProps);
if (changedProps.has("direction")) {
this.mdcRoot.dir = this.direction;
if (this.direction === "rtl") {
this._rtlStyle = document.createElement("style");
this._rtlStyle.innerHTML = `
.mdc-drawer--animate {
transform: translateX(100%);
}
.mdc-drawer--opening {
transform: translateX(0);
}
.mdc-drawer--closing {
transform: translateX(100%);
}
`;
this.shadowRoot!.appendChild(this._rtlStyle);
} else if (this._rtlStyle) {
this.shadowRoot!.removeChild(this._rtlStyle);
}
}
if (changedProps.has("open") && this.open && this.type === "modal") {
this._setupSwipe();
} else if (this._mc) {
@@ -66,6 +87,8 @@ export class HaDrawer extends DrawerBase {
position: fixed;
top: 0;
border-color: var(--divider-color, rgba(0, 0, 0, 0.12));
inset-inline-start: 0 !important;
inset-inline-end: initial !important;
}
.mdc-drawer.mdc-drawer--modal.mdc-drawer--open {
z-index: 200;

View File

@@ -54,7 +54,8 @@ export const computeInitialHaFormData = (
"icon" in selector ||
"template" in selector ||
"text" in selector ||
"theme" in selector
"theme" in selector ||
"object" in selector
) {
data[field.name] = "";
} else if ("number" in selector) {

View File

@@ -2,6 +2,7 @@ import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import QRCode from "qrcode";
import "./ha-alert";
import { rgb2hex } from "../common/color/convert-color";
@customElement("ha-qr-code")
export class HaQrCode extends LitElement {
@@ -65,6 +66,26 @@ export class HaQrCode extends LitElement {
changedProperties.has("centerImage"))
) {
const computedStyles = getComputedStyle(this);
const textRgb = computedStyles.getPropertyValue(
"--rgb-primary-text-color"
);
const backgroundRgb = computedStyles.getPropertyValue(
"--rgb-card-background-color"
);
const textHex = rgb2hex(
textRgb.split(",").map((a) => parseInt(a, 10)) as [
number,
number,
number,
]
);
const backgroundHex = rgb2hex(
backgroundRgb.split(",").map((a) => parseInt(a, 10)) as [
number,
number,
number,
]
);
QRCode.toCanvas(canvas, this.data, {
errorCorrectionLevel:
@@ -74,8 +95,8 @@ export class HaQrCode extends LitElement {
margin: this.margin,
maskPattern: this.maskPattern,
color: {
light: computedStyles.getPropertyValue("--card-background-color"),
dark: computedStyles.getPropertyValue("--primary-text-color"),
light: backgroundHex,
dark: textHex,
},
}).catch((err) => {
this._error = err.message;

View File

@@ -38,7 +38,6 @@ import { storage } from "../common/decorators/storage";
import { fireEvent } from "../common/dom/fire_event";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { stringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl";
import { throttle } from "../common/util/throttle";
import { ActionHandlerDetail } from "../data/lovelace/action_handler";
import {
@@ -307,16 +306,12 @@ class HaSidebar extends SubscribeMixin(LitElement) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
this._calculateCounts();
if (!SUPPORT_SCROLL_IF_NEEDED) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.panelUrl !== this.hass.panelUrl) {
const selectedEl = this.shadowRoot!.querySelector(".iron-selected");
if (selectedEl) {
@@ -851,29 +846,22 @@ class HaSidebar extends SubscribeMixin(LitElement) {
font-size: 20px;
align-items: center;
padding-left: calc(4px + env(safe-area-inset-left));
}
:host([rtl]) .menu {
padding-left: 4px;
padding-right: calc(4px + env(safe-area-inset-right));
padding-inline-start: calc(4px + env(safe-area-inset-left));
padding-inline-end: initial;
}
:host([expanded]) .menu {
width: calc(256px + env(safe-area-inset-left));
}
:host([rtl][expanded]) .menu {
width: calc(256px + env(safe-area-inset-right));
}
.menu ha-icon-button {
color: var(--sidebar-icon-color);
}
.title {
margin-left: 19px;
margin-inline-start: 19px;
margin-inline-end: initial;
width: 100%;
display: none;
}
:host([rtl]) .title {
margin-left: 0;
margin-right: 19px;
}
:host([narrow]) .title {
margin: 0;
padding: 0 16px;
@@ -904,11 +892,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
overflow-x: hidden;
background: none;
margin-left: env(safe-area-inset-left);
}
:host([rtl]) paper-listbox {
margin-left: initial;
margin-right: env(safe-area-inset-right);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
a {
@@ -925,6 +910,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
box-sizing: border-box;
margin: 4px;
padding-left: 12px;
padding-inline-start: 12px;
padding-inline-end: initial;
border-radius: 4px;
--paper-item-min-height: 40px;
width: 48px;
@@ -932,10 +919,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
:host([expanded]) paper-icon-item {
width: 248px;
}
:host([rtl]) paper-icon-item {
padding-left: auto;
padding-right: 12px;
}
ha-icon[slot="item-icon"],
ha-svg-icon[slot="item-icon"] {
@@ -1010,11 +993,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
.configuration-container {
display: flex;
margin-left: env(safe-area-inset-left);
}
:host([rtl]) .notifications-container,
:host([rtl]) .configuration-container {
margin-left: initial;
margin-right: env(safe-area-inset-right);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
.notifications {
cursor: pointer;
@@ -1025,23 +1005,18 @@ class HaSidebar extends SubscribeMixin(LitElement) {
}
.profile {
margin-left: env(safe-area-inset-left);
}
:host([rtl]) .profile {
margin-left: initial;
margin-right: env(safe-area-inset-right);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: initial;
}
.profile paper-icon-item {
padding-left: 4px;
}
:host([rtl]) .profile paper-icon-item {
padding-left: auto;
padding-right: 4px;
margin-inline-start: 4px;
margin-inline-end: auto;
}
.profile .item-text {
margin-left: 8px;
}
:host([rtl]) .profile .item-text {
margin-right: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
.notification-badge,
@@ -1106,9 +1081,9 @@ class HaSidebar extends SubscribeMixin(LitElement) {
font-weight: 500;
}
:host([rtl]) .menu ha-icon-button {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
.menu ha-icon-button {
-webkit-transform: scaleX(var(--scale-direction));
transform: scaleX(var(--scale-direction));
}
`,
];

View File

@@ -2,9 +2,15 @@ import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { MdSlider } from "@material/web/slider/slider";
import { CSSResult, css } from "lit";
import { mainWindow } from "../common/dom/get_main_window";
@customElement("ha-slider")
export class HaSlider extends MdSlider {
public connectedCallback() {
super.connectedCallback();
this.dir = mainWindow.document.dir;
}
static override styles: CSSResult[] = [
...MdSlider.styles,
css`

View File

@@ -39,6 +39,12 @@ export class HaSortable extends LitElement {
@property({ type: String, attribute: "group" })
public group?: string;
@property({ type: Number, attribute: "swap-threshold" })
public swapThreshold?: number;
@property({ type: Boolean, attribute: "invert-swap" })
public invertSwap?: boolean;
protected updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("disabled")) {
if (this.disabled) {
@@ -81,7 +87,7 @@ export class HaSortable extends LitElement {
}
.sortable-ghost {
border: 2px solid var(--primary-color);
box-shadow: 0 0 0 2px var(--primary-color);
background: rgba(var(--rgb-primary-color), 0.25);
border-radius: 4px;
opacity: 0.4;
@@ -108,7 +114,7 @@ export class HaSortable extends LitElement {
const options: SortableInstance.Options = {
animation: 150,
swapThreshold: 0.75,
swapThreshold: 1,
onChoose: this._handleChoose,
onEnd: this._handleEnd,
};
@@ -116,6 +122,13 @@ export class HaSortable extends LitElement {
if (this.draggableSelector) {
options.draggable = this.draggableSelector;
}
if (this.swapThreshold !== undefined) {
options.swapThreshold = this.swapThreshold;
}
if (this.invertSwap !== undefined) {
options.invertSwap = this.invertSwap;
}
if (this.handleSelector) {
options.handle = this.handleSelector;
}

View File

@@ -3,18 +3,11 @@ import { styles as textfieldStyles } from "@material/mwc-textfield/mwc-textfield
import { styles as textareaStyles } from "@material/mwc-textarea/mwc-textarea.css";
import { css, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
@customElement("ha-textarea")
export class HaTextArea extends TextAreaBase {
@property({ type: Boolean, reflect: true }) autogrow = false;
firstUpdated() {
super.firstUpdated();
this.setAttribute("dir", mainWindow.document.dir);
}
updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (this.autogrow && changedProperties.has("value")) {
@@ -54,9 +47,10 @@ export class HaTextArea extends TextAreaBase {
margin-top: 16px;
margin-bottom: 16px;
}
:host([dir="rtl"]) .mdc-floating-label {
right: 16px;
left: initial;
.mdc-floating-label {
inset-inline-start: 16px !important;
inset-inline-end: initial !important;
transform-origin: var(--float-start) top;
}
`,
];

View File

@@ -8,12 +8,18 @@ import type {
Marker,
Polyline,
} from "leaflet";
import { isToday } from "date-fns";
import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import {
LeafletModuleType,
setupLeafletMap,
} from "../../common/dom/setup-leaflet-map";
import {
formatTimeWithSeconds,
formatTimeWeekday,
} from "../../common/datetime/format_time";
import { formatDateTime } from "../../common/datetime/format_date_time";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
@@ -27,12 +33,14 @@ const getEntityId = (entity: string | HaMapEntity): string =>
export interface HaMapPathPoint {
point: LatLngTuple;
tooltip: string;
timestamp: Date;
}
export interface HaMapPaths {
points: HaMapPathPoint[];
color?: string;
name?: string;
gradualOpacity?: number;
fullDatetime?: boolean;
}
export interface HaMapEntity {
@@ -242,6 +250,30 @@ export class HaMap extends ReactiveElement {
});
}
private _computePathTooltip(path: HaMapPaths, point: HaMapPathPoint): string {
let formattedTime: string;
if (path.fullDatetime) {
formattedTime = formatDateTime(
point.timestamp,
this.hass.locale,
this.hass.config
);
} else if (isToday(point.timestamp)) {
formattedTime = formatTimeWithSeconds(
point.timestamp,
this.hass.locale,
this.hass.config
);
} else {
formattedTime = formatTimeWeekday(
point.timestamp,
this.hass.locale,
this.hass.config
);
}
return `${path.name}<br>${formattedTime}`;
}
private _drawPaths(): void {
const hass = this.hass;
const map = this.leafletMap;
@@ -289,7 +321,10 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity,
interactive: true,
})
.bindTooltip(path.points[pointIndex].tooltip, { direction: "top" })
.bindTooltip(
this._computePathTooltip(path, path.points[pointIndex]),
{ direction: "top" }
)
);
// DRAW line between this and next point
@@ -319,7 +354,10 @@ export class HaMap extends ReactiveElement {
fillOpacity: opacity,
interactive: true,
})
.bindTooltip(path.points[pointIndex].tooltip, { direction: "top" })
.bindTooltip(
this._computePathTooltip(path, path.points[pointIndex]),
{ direction: "top" }
)
);
}
this._mapPaths.forEach((marker) => map.addLayer(marker));
@@ -556,6 +594,7 @@ export class HaMap extends ReactiveElement {
color: white !important;
border-radius: 4px;
box-shadow: none !important;
text-align: center;
}
`;
}

View File

@@ -25,7 +25,6 @@ import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { until } from "lit/directives/until";
import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import { debounce } from "../../common/util/debounce";
import { isUnavailableState } from "../../data/entity";
import type { MediaPlayerItem } from "../../data/media-player";
@@ -539,7 +538,6 @@ export class HaMediaPlayerBrowse extends LitElement {
.graphic=${mediaClass.show_list_images
? "medium"
: "avatar"}
dir=${computeRTLDirection(this.hass)}
>
<span class="title">
${this.hass.localize(
@@ -637,7 +635,6 @@ export class HaMediaPlayerBrowse extends LitElement {
@click=${this._childClicked}
.item=${child}
.graphic=${mediaClass.show_list_images ? "medium" : "avatar"}
dir=${computeRTLDirection(this.hass)}
>
${backgroundImage === "none" && !child.can_play
? html`<ha-svg-icon
@@ -1198,10 +1195,8 @@ export class HaMediaPlayerBrowse extends LitElement {
mwc-list-item .title {
margin-left: 16px;
}
mwc-list-item[dir="rtl"] .title {
margin-right: 16px;
margin-left: 0;
margin-inline-start: 16px;
margin-inline-end: initial;
}
/* ============= Narrow ============= */
@@ -1332,6 +1327,10 @@ export class HaMediaPlayerBrowse extends LitElement {
lit-virtualizer.not_shown {
height: calc(100% - 36px);
}
ha-browse-media-tts {
direction: var(--direction);
}
`,
];
}

View File

@@ -5,6 +5,7 @@ import {
html,
TemplateResult,
svg,
nothing,
} from "lit";
import { customElement, property } from "lit/decorators";
import { NODE_SIZE, SPACING } from "./hat-graph-const";
@@ -51,7 +52,7 @@ export class HatGraphNode extends LitElement {
: Math.ceil((NODE_SIZE + SPACING * 2) / 2)} ${width} ${height}"
>
${this.graphStart
? ``
? nothing
: svg`
<path
class="connector"
@@ -64,7 +65,6 @@ export class HatGraphNode extends LitElement {
`}
<g class="node">
<circle cx="0" cy="0" r=${NODE_SIZE / 2} />
}
${this.badge
? svg`
<g class="number">
@@ -81,9 +81,11 @@ export class HatGraphNode extends LitElement {
>${this.badge > 9 ? "9+" : this.badge}</text>
</g>
`
: ""}
: nothing}
<g style="pointer-events: none" transform="translate(${-12} ${-12})">
${this.iconPath ? svg`<path class="icon" d=${this.iconPath}/>` : ""}
${this.iconPath
? svg`<path class="icon" d=${this.iconPath}/>`
: svg`<foreignObject><span class="icon"><slot name="icon"></slot></span></foreignObject>`}
</g>
</g>
</svg>
@@ -152,6 +154,13 @@ export class HatGraphNode extends LitElement {
path.icon {
fill: var(--icon-clr);
}
foreignObject {
width: 24px;
height: 24px;
}
.icon {
color: var(--icon-clr);
}
`;
}
}

View File

@@ -17,11 +17,10 @@ import {
mdiRoomService,
mdiShuffleDisabled,
} from "@mdi/js";
import { LitElement, PropertyValues, css, html } from "lit";
import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event";
import { ACTION_ICONS } from "../../data/action";
import { Condition, Trigger } from "../../data/automation";
import {
Action,
@@ -41,11 +40,14 @@ import {
IfActionTraceStep,
TraceExtended,
} from "../../data/trace";
import { HomeAssistant } from "../../types";
import "../ha-icon-button";
import "../ha-service-icon";
import "./hat-graph-branch";
import { BRANCH_HEIGHT, NODE_SIZE, SPACING } from "./hat-graph-const";
import "./hat-graph-node";
import "./hat-graph-spacer";
import { ACTION_ICONS } from "../../data/action";
export interface NodeInfo {
path: string;
@@ -64,6 +66,8 @@ export class HatScriptGraph extends LitElement {
@property({ attribute: false }) public selected?: string;
public hass!: HomeAssistant;
public renderedNodes: Record<string, NodeInfo> = {};
public trackedNodes: Record<string, NodeInfo> = {};
@@ -415,13 +419,21 @@ export class HatScriptGraph extends LitElement {
return html`
<hat-graph-node
.graphStart=${graphStart}
.iconPath=${mdiRoomService}
.iconPath=${node.service ? undefined : mdiRoomService}
@focus=${this.selectNode(node, path)}
?track=${path in this.trace.trace}
?active=${this.selected === path}
.notEnabled=${disabled || node.enabled === false}
tabindex=${this.trace && path in this.trace.trace ? "0" : "-1"}
></hat-graph-node>
>
${node.service
? html`<ha-service-icon
slot="icon"
.hass=${this.hass}
.service=${node.service}
></ha-service-icon>`
: nothing}
</hat-graph-node>
`;
}
@@ -667,8 +679,6 @@ export class HatScriptGraph extends LitElement {
}
.parent {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
margin-top: 16px;
}
.error {

View File

@@ -18,6 +18,7 @@ export interface CloudPreferences {
google_enabled: boolean;
alexa_enabled: boolean;
remote_enabled: boolean;
remote_allow_remote_enable: boolean;
google_secure_devices_pin: string | undefined;
cloudhooks: { [webhookId: string]: CloudWebhook };
alexa_report_state: boolean;
@@ -139,6 +140,7 @@ export const updateCloudPref = (
google_report_state?: CloudPreferences["google_report_state"];
google_secure_devices_pin?: CloudPreferences["google_secure_devices_pin"];
tts_default_voice?: CloudPreferences["tts_default_voice"];
remote_allow_remote_enable?: CloudPreferences["remote_allow_remote_enable"];
}
) =>
hass.callWS({

View File

@@ -8,37 +8,49 @@ import {
EntityRegistryDisplayEntry,
EntityRegistryEntry,
} from "./entity_registry";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { atLeastVersion } from "../common/config/version";
const resources: Record<IconCategory, any> = {
const resources: {
entity: Record<string, Promise<PlatformIcons>>;
entity_component: {
domains?: string[];
resources?: Promise<Record<string, ComponentIcons>>;
};
services: {
all?: Promise<Record<string, ServiceIcons>>;
domains: { [domain: string]: ServiceIcons | Promise<ServiceIcons> };
};
} = {
entity: {},
entity_component: undefined,
services: {},
entity_component: {},
services: { domains: {} },
};
interface IconResources {
resources: Record<string, string | Record<string, string>>;
interface IconResources<
T extends ComponentIcons | PlatformIcons | ServiceIcons,
> {
resources: Record<string, T>;
}
interface PlatformIcons {
[domain: string]: {
[translation_key: string]: {
state: Record<string, string>;
state_attributes: Record<
string,
{
state: Record<string, string>;
default: string;
}
>;
default: string;
};
[translation_key: string]: {
state: Record<string, string>;
state_attributes: Record<
string,
{
state: Record<string, string>;
default: string;
}
>;
default: string;
};
}
interface ComponentIcons {
export interface ComponentIcons {
[device_class: string]: {
state: Record<string, string>;
state_attributes: Record<
state?: Record<string, string>;
state_attributes?: Record<
string,
{
state: Record<string, string>;
@@ -55,12 +67,18 @@ interface ServiceIcons {
export type IconCategory = "entity" | "entity_component" | "services";
export const getHassIcons = async (
type CategoryType = {
entity: PlatformIcons;
entity_component: ComponentIcons;
services: ServiceIcons;
};
export const getHassIcons = async <T extends IconCategory>(
hass: HomeAssistant,
category: IconCategory,
category: T,
integration?: string
): Promise<IconResources> =>
hass.callWS<{ resources: Record<string, string> }>({
) =>
hass.callWS<IconResources<CategoryType[T]>>({
type: "frontend/get_icons",
category,
integration,
@@ -70,14 +88,20 @@ export const getPlatformIcons = async (
hass: HomeAssistant,
integration: string,
force = false
): Promise<PlatformIcons> => {
): Promise<PlatformIcons | undefined> => {
if (!force && integration in resources.entity) {
return resources.entity[integration];
}
const result = getHassIcons(hass, "entity", integration);
resources.entity[integration] = result.then(
if (
!isComponentLoaded(hass, integration) ||
!atLeastVersion(hass.connection.haVersion, 2024, 2)
) {
return undefined;
}
const result = getHassIcons(hass, "entity", integration).then(
(res) => res?.resources[integration]
);
resources.entity[integration] = result;
return resources.entity[integration];
};
@@ -85,45 +109,70 @@ export const getComponentIcons = async (
hass: HomeAssistant,
domain: string,
force = false
): Promise<ComponentIcons> => {
if (!force && resources.entity_component) {
return resources.entity_component.then((res) => res[domain]);
): Promise<ComponentIcons | undefined> => {
// For Cast, old instances can connect to it.
if (
__BACKWARDS_COMPAT__ &&
!atLeastVersion(hass.connection.haVersion, 2024, 2)
) {
return import("../fake_data/entity_component_icons")
.then((mod) => mod.ENTITY_COMPONENT_ICONS)
.then((res) => res[domain]);
}
resources.entity_component = getHassIcons(hass, "entity_component").then(
(result) => result.resources
);
return resources.entity_component.then((res) => res[domain]);
if (
!force &&
resources.entity_component.resources &&
resources.entity_component.domains?.includes(domain)
) {
return resources.entity_component.resources.then((res) => res[domain]);
}
if (!isComponentLoaded(hass, domain)) {
return undefined;
}
resources.entity_component.domains = [...hass.config.components];
resources.entity_component.resources = getHassIcons(
hass,
"entity_component"
).then((result) => result.resources);
return resources.entity_component.resources.then((res) => res[domain]);
};
export const getServiceIcons = async (
hass: HomeAssistant,
domain?: string,
force = false
): Promise<ServiceIcons> => {
): Promise<ServiceIcons | Record<string, ServiceIcons> | undefined> => {
if (!domain) {
if (!force && resources.services.all) {
return resources.services.all;
}
resources.services.all = getHassIcons(hass, "services", domain).then(
(res) => {
resources.services = res.resources;
resources.services.domains = res.resources;
return res?.resources;
}
);
return resources.services.all;
}
if (!force && domain && domain in resources.services) {
return resources.services[domain];
if (!force && domain in resources.services.domains) {
return resources.services.domains[domain];
}
if (resources.services.all && !force) {
await resources.services.all;
if (domain in resources.services) {
return resources.services[domain];
if (domain in resources.services.domains) {
return resources.services.domains[domain];
}
}
if (!isComponentLoaded(hass, domain)) {
return undefined;
}
const result = getHassIcons(hass, "services", domain);
resources.services[domain] = result.then((res) => res?.resources[domain]);
return resources.services[domain];
resources.services.domains[domain] = result.then(
(res) => res?.resources[domain]
);
return resources.services.domains[domain];
};
export const entityIcon = async (
@@ -238,7 +287,7 @@ export const serviceIcon = async (
const serviceName = computeObjectId(service);
const serviceIcons = await getServiceIcons(hass, domain);
if (serviceIcons) {
icon = serviceIcons[serviceName];
icon = serviceIcons[serviceName] as string;
}
if (!icon) {
icon = await domainIcon(hass, domain);

View File

@@ -35,6 +35,7 @@ export interface MatterNodeDiagnostics {
mac_address?: string;
available: boolean;
active_fabrics: MatterFabricData[];
active_fabric_index: number;
}
export interface MatterPingResult {

View File

@@ -2,7 +2,6 @@ import "@material/mwc-button/mwc-button";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import "../../components/ha-dialog";
import "../../components/ha-formfield";
import "../../components/ha-switch";
@@ -82,7 +81,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
}
)}
</p>`}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${!this._disableNewEntities}
@@ -109,7 +107,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
}
)}
</p>`}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.checked=${!this._disablePolling}

View File

@@ -14,7 +14,6 @@ import { customElement, property } from "lit/decorators";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { stateActive } from "../../../common/entity/state_active";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-icon-button";
import "../../../components/ha-select";
import "../../../components/ha-slider";
@@ -131,7 +130,6 @@ class MoreInfoMediaPlayer extends LitElement {
<ha-slider
labeled
id="input"
.dir=${computeRTLDirection(this.hass!)}
.value=${Number(stateObj.attributes.volume_level) * 100}
@change=${this._selectedValueChanged}
></ha-slider>

View File

@@ -14,6 +14,7 @@ import "./notification-item";
import "../../components/ha-header-bar";
import "../../components/ha-drawer";
import type { HaDrawer } from "../../components/ha-drawer";
import { computeRTLDirection } from "../../common/util/compute_rtl";
@customElement("notification-drawer")
export class HuiNotificationDrawer extends LitElement {
@@ -92,7 +93,12 @@ export class HuiNotificationDrawer extends LitElement {
});
return html`
<ha-drawer type="modal" open @MDCDrawer:closed=${this._dialogClosed}>
<ha-drawer
type="modal"
open
@MDCDrawer:closed=${this._dialogClosed}
.direction=${computeRTLDirection(this.hass)}
>
<ha-header-bar>
<div slot="title">
${this.hass.localize("ui.notification_drawer.title")}

View File

@@ -35,13 +35,22 @@ interface EMOutgoingMessageConfigGet extends EMMessage {
type: "config/get";
}
interface EMOutgoingMessageScanQRCode extends EMMessage {
type: "qr_code/scan";
interface EMOutgoingMessageBarCodeScan extends EMMessage {
type: "bar_code/scan";
title: string;
description: string;
alternative_option_label?: string;
}
interface EMOutgoingMessageBarCodeClose extends EMMessage {
type: "bar_code/close";
}
interface EMOutgoingMessageBarCodeNotify extends EMMessage {
type: "bar_code/notify";
message: string;
}
interface EMOutgoingMessageMatterCommission extends EMMessage {
type: "matter/commission";
}
@@ -55,13 +64,6 @@ type EMOutgoingMessageWithAnswer = {
request: EMOutgoingMessageConfigGet;
response: ExternalConfig;
};
"qr_code/scan": {
request: EMOutgoingMessageScanQRCode;
response:
| EMIncomingMessageQRCodeResponseCanceled
| EMIncomingMessageQRCodeResponseAlternativeOptions
| EMIncomingMessageQRCodeResponseScanResult;
};
};
interface EMOutgoingMessageExoplayerPlayHLS extends EMMessage {
@@ -124,20 +126,23 @@ interface EMOutgoingMessageAssistShow extends EMMessage {
}
type EMOutgoingMessageWithoutAnswer =
| EMOutgoingMessageHaptic
| EMOutgoingMessageConnectionStatus
| EMMessageResultError
| EMMessageResultSuccess
| EMOutgoingMessageAppConfiguration
| EMOutgoingMessageTagWrite
| EMOutgoingMessageSidebarShow
| EMOutgoingMessageAssistShow
| EMOutgoingMessageBarCodeClose
| EMOutgoingMessageBarCodeNotify
| EMOutgoingMessageBarCodeScan
| EMOutgoingMessageConnectionStatus
| EMOutgoingMessageExoplayerPlayHLS
| EMOutgoingMessageExoplayerResize
| EMOutgoingMessageExoplayerStop
| EMOutgoingMessageThemeUpdate
| EMMessageResultSuccess
| EMMessageResultError
| EMOutgoingMessageHaptic
| EMOutgoingMessageImportThreadCredentials
| EMOutgoingMessageMatterCommission
| EMOutgoingMessageImportThreadCredentials;
| EMOutgoingMessageSidebarShow
| EMOutgoingMessageTagWrite
| EMOutgoingMessageThemeUpdate;
interface EMIncomingMessageRestart {
id: number;
@@ -172,17 +177,39 @@ interface EMIncomingMessageShowAutomationEditor {
};
}
export interface EMIncomingMessageQRCodeResponseCanceled {
action: "canceled";
export interface EMIncomingMessageBarCodeScanResult {
id: number;
type: "command";
command: "bar_code/scan_result";
payload: {
// A string decoded from the barcode data.
rawValue: string;
// https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API#supported_barcode_formats
format:
| "aztec"
| "code_128"
| "code_39"
| "code_93"
| "codabar"
| "data_matrix"
| "ean_13"
| "ean_8"
| "itf"
| "pdf417"
| "qr_code"
| "upc_a"
| "upc_e"
| "unknown";
};
}
export interface EMIncomingMessageQRCodeResponseAlternativeOptions {
action: "alternative_options";
}
export interface EMIncomingMessageQRCodeResponseScanResult {
action: "scan_result";
result: string;
export interface EMIncomingMessageBarCodeScanAborted {
id: number;
type: "command";
command: "bar_code/aborted";
payload: {
reason: "canceled" | "alternative_options";
};
}
export type EMIncomingMessageCommands =
@@ -190,7 +217,9 @@ export type EMIncomingMessageCommands =
| EMIncomingMessageShowNotifications
| EMIncomingMessageToggleSidebar
| EMIncomingMessageShowSidebar
| EMIncomingMessageShowAutomationEditor;
| EMIncomingMessageShowAutomationEditor
| EMIncomingMessageBarCodeScanResult
| EMIncomingMessageBarCodeScanAborted;
type EMIncomingMessage =
| EMMessageResultSuccess
@@ -207,7 +236,7 @@ export interface ExternalConfig {
canCommissionMatter: boolean;
canImportThreadCredentials: boolean;
hasAssist: boolean;
hasQRScanner: number;
hasBarCodeScanner: number;
}
export class ExternalMessaging {

View File

@@ -14,7 +14,53 @@ export const demoConfig: HassConfig = {
wind_speed: "m/s",
accumulated_precipitation: "mm",
},
components: ["notify.html5", "history", "todo", "forecast_solar", "energy"],
components: [
"notify.html5",
"history",
"forecast_solar",
"energy",
"person",
"number",
"select",
"tts",
"datetime",
"vacuum",
"wake_word",
"light",
"alarm_control_panel",
"text",
"lawn_mower",
"siren",
"input_boolean",
"lock",
"calendar",
"image",
"device_tracker",
"scene",
"script",
"todo",
"cover",
"switch",
"button",
"water_heater",
"binary_sensor",
"sensor",
"humidifier",
"valve",
"time",
"media_player",
"air_quality",
"camera",
"date",
"fan",
"automation",
"weather",
"climate",
"stt",
"update",
"event",
"demo",
],
time_zone: "America/Los_Angeles",
config_dir: "/config",
version: "DEMO",

View File

@@ -0,0 +1,962 @@
import { ComponentIcons } from "../data/icons";
export const ENTITY_COMPONENT_ICONS: Record<string, ComponentIcons> = {
person: {
_: {
default: "mdi:account",
state: {
not_home: "mdi:account-arrow-right",
},
},
},
number: {
_: {
default: "mdi:ray-vertex",
},
apparent_power: {
default: "mdi:flash",
},
aqi: {
default: "mdi:air-filter",
},
atmospheric_pressure: {
default: "mdi:thermometer-lines",
},
battery: {
default: "mdi:battery",
},
carbon_dioxide: {
default: "mdi:molecule-co2",
},
carbon_monoxide: {
default: "mdi:molecule-co",
},
current: {
default: "mdi:current-ac",
},
data_rate: {
default: "mdi:transmission-tower",
},
data_size: {
default: "mdi:database",
},
distance: {
default: "mdi:arrow-left-right",
},
duration: {
default: "mdi:progress-clock",
},
energy: {
default: "mdi:lightning-bolt",
},
energy_storage: {
default: "mdi:car-battery",
},
frequency: {
default: "mdi:sine-wave",
},
gas: {
default: "mdi:meter-gas",
},
humidity: {
default: "mdi:water-percent",
},
illuminance: {
default: "mdi:brightness-5",
},
irradiance: {
default: "mdi:sun-wireless",
},
moisture: {
default: "mdi:water-percent",
},
monetary: {
default: "mdi:cash",
},
nitrogen_dioxide: {
default: "mdi:molecule",
},
nitrogen_monoxide: {
default: "mdi:molecule",
},
nitrous_oxide: {
default: "mdi:molecule",
},
ozone: {
default: "mdi:molecule",
},
ph: {
default: "mdi:ph",
},
pm1: {
default: "mdi:molecule",
},
pm10: {
default: "mdi:molecule",
},
pm25: {
default: "mdi:molecule",
},
power: {
default: "mdi:flash",
},
power_factor: {
default: "mdi:angle-acute",
},
precipitation: {
default: "mdi:weather-rainy",
},
precipitation_intensity: {
default: "mdi:weather-pouring",
},
pressure: {
default: "mdi:gauge",
},
reactive_power: {
default: "mdi:flash",
},
signal_strength: {
default: "mdi:wifi",
},
sound_pressure: {
default: "mdi:ear-hearing",
},
speed: {
default: "mdi:speedometer",
},
sulfur_dioxide: {
default: "mdi:molecule",
},
temperature: {
default: "mdi:thermometer",
},
volatile_organic_compounds: {
default: "mdi:molecule",
},
volatile_organic_compounds_parts: {
default: "mdi:molecule",
},
voltage: {
default: "mdi:sine-wave",
},
volume: {
default: "mdi:car-coolant-level",
},
volume_storage: {
default: "mdi:storage-tank",
},
water: {
default: "mdi:water",
},
weight: {
default: "mdi:weight",
},
wind_speed: {
default: "mdi:weather-windy",
},
},
select: {
_: {
default: "mdi:format-list-bulleted",
},
},
tts: {
_: {
default: "mdi:speaker-message",
},
},
datetime: {
_: {
default: "mdi:calendar-clock",
},
},
vacuum: {
_: {
default: "mdi:robot-vacuum",
},
},
wake_word: {
_: {
default: "mdi:chat-sleep",
},
},
light: {
_: {
default: "mdi:lightbulb",
},
},
alarm_control_panel: {
_: {
default: "mdi:shield",
state: {
armed_away: "mdi:shield-lock",
armed_custom_bypass: "mdi:security",
armed_home: "mdi:shield-home",
armed_night: "mdi:shield-moon",
armed_vacation: "mdi:shield-airplane",
disarmed: "mdi:shield-off",
pending: "mdi:shield-outline",
triggered: "mdi:bell-ring",
},
},
},
text: {
_: {
default: "mdi:form-textbox",
},
},
lawn_mower: {
_: {
default: "mdi:robot-mower",
},
},
siren: {
_: {
default: "mdi:bullhorn",
},
},
input_boolean: {
_: {
default: "mdi:check-circle-outline",
state: {
off: "mdi:close-circle-outline",
},
},
},
lock: {
_: {
default: "mdi:lock",
state: {
jammed: "mdi:lock-alert",
locking: "mdi:lock-clock",
unlocked: "mdi:lock-open",
unlocking: "mdi:lock-clock",
},
},
},
calendar: {
_: {
default: "mdi:calendar",
state: {
on: "mdi:calendar-check",
off: "mdi:calendar-blank",
},
},
},
image: {
_: {
default: "mdi:image",
},
},
device_tracker: {
_: {
default: "mdi:account",
state: {
not_home: "mdi:account-arrow-right",
},
},
},
scene: {
_: {
default: "mdi:palette",
},
},
script: {
_: {
default: "mdi:script-text",
state: {
on: "mdi:script-text-play",
},
},
},
todo: {
_: {
default: "mdi:clipboard-list",
},
},
cover: {
_: {
default: "mdi:window-open",
state: {
closed: "mdi:window-closed",
closing: "mdi:arrow-down-box",
opening: "mdi:arrow-up-box",
},
},
blind: {
default: "mdi:blinds-horizontal",
state: {
closed: "mdi:blinds-horizontal-closed",
closing: "mdi:arrow-down-box",
opening: "mdi:arrow-up-box",
},
},
curtain: {
default: "mdi:curtains",
state: {
closed: "mdi:curtains-closed",
closing: "mdi:arrow-collapse-horizontal",
opening: "mdi:arrow-split-vertical",
},
},
damper: {
default: "mdi:circle",
state: {
closed: "mdi:circle-slice-8",
},
},
door: {
default: "mdi:door-open",
state: {
closed: "mdi:door-closed",
},
},
garage: {
default: "mdi:garage-open",
state: {
closed: "mdi:garage",
closing: "mdi:arrow-down-box",
opening: "mdi:arrow-up-box",
},
},
gate: {
default: "mdi:gate-open",
state: {
closed: "mdi:gate",
closing: "mdi:arrow-right",
opening: "mdi:arrow-right",
},
},
shade: {
default: "mdi:roller-shade",
state: {
closed: "mdi:roller-shade-closed",
closing: "mdi:arrow-down-box",
opening: "mdi:arrow-up-box",
},
},
shutter: {
default: "mdi:window-shutter-open",
state: {
closed: "mdi:window-shutter",
closing: "mdi:arrow-down-box",
opening: "mdi:arrow-up-box",
},
},
window: {
default: "mdi:window-open",
state: {
closed: "mdi:window-closed",
closing: "mdi:arrow-down-box",
opening: "mdi:arrow-up-box",
},
},
},
switch: {
_: {
default: "mdi:toggle-switch-variant",
},
switch: {
default: "mdi:toggle-switch-variant",
state: {
off: "mdi:toggle-switch-variant-off",
},
},
outlet: {
default: "mdi:power-plug",
state: {
off: "mdi:power-plug-off",
},
},
},
button: {
_: {
default: "mdi:button-pointer",
},
restart: {
default: "mdi:restart",
},
identify: {
default: "mdi:crosshairs-question",
},
update: {
default: "mdi:package-up",
},
},
water_heater: {
_: {
default: "mdi:water-boiler",
state: {
off: "mdi:water-boiler-off",
},
state_attributes: {
operation_mode: {
default: "mdi:circle-medium",
state: {
eco: "mdi:leaf",
electric: "mdi:lightning-bolt",
gas: "mdi:fire-circle",
heat_pump: "mdi:heat-wave",
high_demand: "mdi:finance",
off: "mdi:power",
performance: "mdi:rocket-launch",
},
},
},
},
},
binary_sensor: {
_: {
default: "mdi:radiobox-blank",
state: {
on: "mdi:checkbox-marked-circle",
},
},
battery: {
default: "mdi:battery",
state: {
on: "mdi:battery-outline",
},
},
battery_charging: {
default: "mdi:battery",
state: {
on: "mdi:battery-charging",
},
},
carbon_monoxide: {
default: "mdi:smoke-detector",
state: {
on: "mdi:smoke-detector-alert",
},
},
cold: {
default: "mdi:thermometer",
state: {
on: "mdi:snowflake",
},
},
connectivity: {
default: "mdi:close-network-outline",
state: {
on: "mdi:check-network-outline",
},
},
door: {
default: "mdi:door-closed",
state: {
on: "mdi:door-open",
},
},
garage_door: {
default: "mdi:garage",
state: {
on: "mdi:garage-open",
},
},
gas: {
default: "mdi:check-circle",
state: {
on: "mdi:alert-circle",
},
},
heat: {
default: "mdi:thermometer",
state: {
on: "mdi:fire",
},
},
light: {
default: "mdi:brightness-5",
state: {
on: "mdi:brightness-7",
},
},
lock: {
default: "mdi:lock",
state: {
on: "mdi:lock-open",
},
},
moisture: {
default: "mdi:water-off",
state: {
on: "mdi:water",
},
},
motion: {
default: "mdi:motion-sensor-off",
state: {
on: "mdi:motion-sensor",
},
},
moving: {
default: "mdi:arrow-right",
state: {
on: "mdi:octagon",
},
},
occupancy: {
default: "mdi:home-outline",
state: {
on: "mdi:home",
},
},
opening: {
default: "mdi:square",
state: {
on: "mdi:square-outline",
},
},
plug: {
default: "mdi:power-plug-off",
state: {
on: "mdi:power-plug",
},
},
power: {
default: "mdi:power-plug-off",
state: {
on: "mdi:power-plug",
},
},
presence: {
default: "mdi:home-outline",
state: {
on: "mdi:home",
},
},
problem: {
default: "mdi:check-circle",
state: {
on: "mdi:alert-circle",
},
},
running: {
default: "mdi:stop",
state: {
on: "mdi:play",
},
},
safety: {
default: "mdi:check-circle",
state: {
on: "mdi:alert-circle",
},
},
smoke: {
default: "mdi:smoke-detector-variant",
state: {
on: "mdi:smoke-detector-variant-alert",
},
},
sound: {
default: "mdi:music-note-off",
state: {
on: "mdi:music-note",
},
},
tamper: {
default: "mdi:check-circle",
state: {
on: "mdi:alert-circle",
},
},
update: {
default: "mdi:package",
state: {
on: "mdi:package-up",
},
},
vibration: {
default: "mdi:crop-portrait",
state: {
on: "mdi:vibrate",
},
},
window: {
default: "mdi:window-closed",
state: {
on: "mdi:window-open",
},
},
},
sensor: {
_: {
default: "mdi:eye",
},
apparent_power: {
default: "mdi:flash",
},
aqi: {
default: "mdi:air-filter",
},
atmospheric_pressure: {
default: "mdi:thermometer-lines",
},
carbon_dioxide: {
default: "mdi:molecule-co2",
},
carbon_monoxide: {
default: "mdi:molecule-co",
},
current: {
default: "mdi:current-ac",
},
data_rate: {
default: "mdi:transmission-tower",
},
data_size: {
default: "mdi:database",
},
date: {
default: "mdi:calendar",
},
distance: {
default: "mdi:arrow-left-right",
},
duration: {
default: "mdi:progress-clock",
},
energy: {
default: "mdi:lightning-bolt",
},
energy_storage: {
default: "mdi:car-battery",
},
enum: {
default: "mdi:eye",
},
frequency: {
default: "mdi:sine-wave",
},
gas: {
default: "mdi:meter-gas",
},
humidity: {
default: "mdi:water-percent",
},
illuminance: {
default: "mdi:brightness-5",
},
irradiance: {
default: "mdi:sun-wireless",
},
moisture: {
default: "mdi:water-percent",
},
monetary: {
default: "mdi:cash",
},
nitrogen_dioxide: {
default: "mdi:molecule",
},
nitrogen_monoxide: {
default: "mdi:molecule",
},
nitrous_oxide: {
default: "mdi:molecule",
},
ozone: {
default: "mdi:molecule",
},
ph: {
default: "mdi:ph",
},
pm1: {
default: "mdi:molecule",
},
pm10: {
default: "mdi:molecule",
},
pm25: {
default: "mdi:molecule",
},
power: {
default: "mdi:flash",
},
power_factor: {
default: "mdi:angle-acute",
},
precipitation: {
default: "mdi:weather-rainy",
},
precipitation_intensity: {
default: "mdi:weather-pouring",
},
pressure: {
default: "mdi:gauge",
},
reactive_power: {
default: "mdi:flash",
},
signal_strength: {
default: "mdi:wifi",
},
sound_pressure: {
default: "mdi:ear-hearing",
},
speed: {
default: "mdi:speedometer",
},
sulfur_dioxide: {
default: "mdi:molecule",
},
temperature: {
default: "mdi:thermometer",
},
timestamp: {
default: "mdi:clock",
},
volatile_organic_compounds: {
default: "mdi:molecule",
},
volatile_organic_compounds_parts: {
default: "mdi:molecule",
},
voltage: {
default: "mdi:sine-wave",
},
volume: {
default: "mdi:car-coolant-level",
},
volume_storage: {
default: "mdi:storage-tank",
},
water: {
default: "mdi:water",
},
weight: {
default: "mdi:weight",
},
wind_speed: {
default: "mdi:weather-windy",
},
},
humidifier: {
_: {
default: "mdi:air-humidifier",
state: {
off: "mdi:air-humidifier-off",
},
state_attributes: {
action: {
default: "mdi:circle-medium",
state: {
drying: "mdi:arrow-down-bold",
humidifying: "mdi:arrow-up-bold",
idle: "mdi:clock-outline",
off: "mdi:power",
},
},
mode: {
default: "mdi:circle-medium",
state: {
auto: "mdi:refresh-auto",
away: "mdi:account-arrow-right",
baby: "mdi:baby-carriage",
boost: "mdi:rocket-launch",
comfort: "mdi:sofa",
eco: "mdi:leaf",
home: "mdi:home",
normal: "mdi:water-percent",
sleep: "mdi:power-sleep",
},
},
},
},
},
valve: {
_: {
default: "mdi:pipe-valve",
},
gas: {
default: "mdi:meter-gas",
},
water: {
default: "mdi:pipe-valve",
},
},
time: {
_: {
default: "mdi:clock",
},
},
media_player: {
_: {
default: "mdi:cast",
state: {
off: "mdi:cast-off",
paused: "mdi:cast-connected",
playing: "mdi:cast-connected",
},
},
receiver: {
default: "mdi:audio-video",
state: {
off: "mdi:audio-video-off",
},
},
speaker: {
default: "mdi:speaker",
state: {
off: "mdi:speaker-off",
paused: "mdi:speaker-pause",
playing: "mdi:speaker-play",
},
},
tv: {
default: "mdi:television",
state: {
off: "mdi:television-off",
paused: "mdi:television-pause",
playing: "mdi:television-play",
},
},
},
air_quality: {
_: {
default: "mdi:air-filter",
},
},
camera: {
_: {
default: "mdi:video",
state: {
off: "mdi:video-off",
},
},
},
date: {
_: {
default: "mdi:calendar",
},
},
fan: {
_: {
default: "mdi:fan",
state: {
off: "mdi:fan-off",
},
state_attributes: {
direction: {
default: "mdi:rotate-right",
state: {
reverse: "mdi:rotate-left",
},
},
},
},
},
automation: {
_: {
default: "mdi:robot",
state: {
off: "mdi:robot-off",
unavailable: "mdi:robot-confused",
},
},
},
weather: {
_: {
default: "mdi:weather-partly-cloudy",
state: {
"clear-night": "mdi:weather-night",
cloudy: "mdi:weather-cloudy",
exceptional: "mdi:alert-circle-outline",
fog: "mdi:weather-fog",
hail: "mdi:weather-hail",
lightning: "mdi:weather-lightning",
"lightning-rainy": "mdi:weather-lightning-rainy",
pouring: "mdi:weather-pouring",
rainy: "mdi:weather-rainy",
snowy: "mdi:weather-snowy",
"snowy-rainy": "mdi:weather-snowy-rainy",
sunny: "mdi:weather-sunny",
windy: "mdi:weather-windy",
"windy-variant": "mdi:weather-windy-variant",
},
},
},
climate: {
_: {
default: "mdi:thermostat",
state_attributes: {
fan_mode: {
default: "mdi:circle-medium",
state: {
diffuse: "mdi:weather-windy",
focus: "mdi:target",
high: "mdi:speedometer",
low: "mdi:speedometer-slow",
medium: "mdi:speedometer-medium",
middle: "mdi:speedometer-medium",
off: "mdi:fan-off",
on: "mdi:fan",
},
},
hvac_action: {
default: "mdi:circle-medium",
state: {
cooling: "mdi:snowflake",
drying: "mdi:water-percent",
fan: "mdi:fan",
heating: "mdi:fire",
idle: "mdi:clock-outline",
off: "mdi:power",
preheating: "mdi:heat-wave",
},
},
preset_mode: {
default: "mdi:circle-medium",
state: {
activity: "mdi:motion-sensor",
away: "mdi:account-arrow-right",
boost: "mdi:rocket-launch",
comfort: "mdi:sofa",
eco: "mdi:leaf",
home: "mdi:home",
sleep: "mdi:bed",
},
},
swing_mode: {
default: "mdi:circle-medium",
state: {
both: "mdi:arrow-all",
horizontal: "mdi:arrow-left-right",
off: "mdi:arrow-oscillating-off",
on: "mdi:arrow-oscillating",
vertical: "mdi:arrow-up-down",
},
},
},
},
},
stt: {
_: {
default: "mdi:microphone-message",
},
},
update: {
_: {
default: "mdi:package-up",
state: {
off: "mdi:package",
},
},
},
event: {
_: {
default: "mdi:eye-check",
},
button: {
default: "mdi:gesture-tap-button",
},
doorbell: {
default: "mdi:doorbell",
},
motion: {
default: "mdi:motion-sensor",
},
},
};

View File

@@ -1,15 +1,6 @@
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, eventOptions, property } from "lit/decorators";
import { restoreScroll } from "../common/decorators/restore-scroll";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/ha-icon-button-arrow-prev";
import "../components/ha-menu-button";
import { HomeAssistant } from "../types";
@@ -34,17 +25,6 @@ class HassSubpage extends LitElement {
// @ts-ignore
@restoreScroll(".content") private _savedScrollPos?: number;
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (!changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
toggleAttribute(this, "rtl", computeRTL(this.hass));
}
}
protected render(): TemplateResult {
return html`
<div class="toolbar">
@@ -160,6 +140,9 @@ class HassSubpage extends LitElement {
#fab {
position: absolute;
right: calc(16px + env(safe-area-inset-right));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
@@ -169,15 +152,8 @@ class HassSubpage extends LitElement {
#fab[is-wide] {
bottom: 24px;
right: 24px;
}
:host([rtl]) #fab {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
inset-inline-end: 24px;
inset-inline-start: initial;
}
`,
];

View File

@@ -4,7 +4,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { LocalizeFunc } from "../common/translations/localize";
import { computeRTLDirection } from "../common/util/compute_rtl";
import "../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@@ -244,7 +243,6 @@ export class HaTabsSubpageDataTable extends LitElement {
.selectable=${this.selectable}
.hasFab=${this.hasFab}
.id=${this.id}
.dir=${computeRTLDirection(this.hass)}
.clickable=${this.clickable}
.appendRow=${this.appendRow}
>

View File

@@ -13,7 +13,6 @@ import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { restoreScroll } from "../common/decorators/restore-scroll";
import { LocalizeFunc } from "../common/translations/localize";
import { computeRTL } from "../common/util/compute_rtl";
import "../components/ha-icon-button-arrow-prev";
import "../components/ha-menu-button";
import "../components/ha-svg-icon";
@@ -58,8 +57,6 @@ class HassTabsSubpage extends LitElement {
@property({ type: Boolean, reflect: true, attribute: "is-wide" })
public isWide = false;
@property({ type: Boolean, reflect: true }) public rtl = false;
@state() private _activeTab?: PageNavigation;
// @ts-ignore
@@ -123,14 +120,6 @@ class HassTabsSubpage extends LitElement {
`${this.route.prefix}${this.route.path}`.includes(tab.path)
);
}
if (changedProperties.has("hass")) {
const oldHass = changedProperties.get("hass") as
| HomeAssistant
| undefined;
if (!oldHass || oldHass.language !== this.hass.language) {
this.rtl = computeRTL(this.hass);
}
}
super.willUpdate(changedProperties);
}
@@ -334,6 +323,8 @@ class HassTabsSubpage extends LitElement {
#fab {
position: fixed;
right: calc(16px + env(safe-area-inset-right));
inset-inline-end: calc(16px + env(safe-area-inset-right));
inset-inline-start: initial;
bottom: calc(16px + env(safe-area-inset-bottom));
z-index: 1;
}
@@ -343,15 +334,8 @@ class HassTabsSubpage extends LitElement {
#fab[is-wide] {
bottom: 24px;
right: 24px;
}
:host([rtl]) #fab {
right: auto;
left: calc(16px + env(safe-area-inset-left));
}
:host([rtl][is-wide]) #fab {
bottom: 24px;
left: 24px;
right: auto;
inset-inline-end: 24px;
inset-inline-start: initial;
}
`,
];

View File

@@ -11,11 +11,11 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent, HASSDomEvent } from "../common/dom/fire_event";
import { listenMediaQuery } from "../common/dom/media_query";
import { toggleAttribute } from "../common/dom/toggle_attribute";
import { computeRTLDirection } from "../common/util/compute_rtl";
import "../components/ha-drawer";
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
import type { HomeAssistant, Route } from "../types";
import "./partial-panel-resolver";
import { computeRTLDirection } from "../common/util/compute_rtl";
declare global {
// for fire event

View File

@@ -28,7 +28,6 @@ import { useAmPm } from "../../common/datetime/use_am_pm";
import { fireEvent } from "../../common/dom/fire_event";
import { supportsFeature } from "../../common/entity/supports-feature";
import { LocalizeFunc } from "../../common/translations/localize";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import "../../components/ha-button-toggle-group";
import "../../components/ha-fab";
import "../../components/ha-icon-button-next";
@@ -169,7 +168,6 @@ export class HAFullCalendar extends LitElement {
.buttons=${viewToggleButtons}
.active=${this._activeView}
@value-changed=${this._handleView}
.dir=${computeRTLDirection(this.hass)}
></ha-button-toggle-group>
`
: html`
@@ -203,7 +201,6 @@ export class HAFullCalendar extends LitElement {
.buttons=${viewToggleButtons}
.active=${this._activeView}
@value-changed=${this._handleView}
.dir=${computeRTLDirection(this.hass)}
></ha-button-toggle-group>
</div>
`}
@@ -503,6 +500,8 @@ export class HAFullCalendar extends LitElement {
position: absolute;
bottom: 32px;
right: 32px;
inset-inline-end: 32px;
inset-inline-start: initial;
z-index: 1;
}

View File

@@ -77,10 +77,12 @@ export default class HaAutomationAction extends LitElement {
return html`
<ha-sortable
handle-selector=".handle"
draggable-selector="ha-automation-action-row"
.disabled=${!this._showReorder || this.disabled}
@item-moved=${this._actionMoved}
group="actions"
.path=${this.path}
invert-swap
>
<div class="actions">
${repeat(
@@ -111,30 +113,29 @@ export default class HaAutomationAction extends LitElement {
</ha-automation-action-row>
`
)}
<div class="buttons">
<ha-button
outlined
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.add"
)}
@click=${this._addActionDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
<ha-button
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.add_building_block"
)}
@click=${this._addActionBuildingBlockDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
</div>
</div>
</ha-sortable>
<div class="buttons">
<ha-button
outlined
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.add"
)}
@click=${this._addActionDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
<ha-button
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.add_building_block"
)}
@click=${this._addActionBuildingBlockDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
</div>
`;
}
@@ -202,12 +203,14 @@ export default class HaAutomationAction extends LitElement {
}
private _moveUp(ev) {
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index - 1;
this._move(index, newIndex);
}
private _moveDown(ev) {
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index + 1;
this._move(index, newIndex);
@@ -266,22 +269,29 @@ export default class HaAutomationAction extends LitElement {
static get styles(): CSSResultGroup {
return css`
.actions {
padding: 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
ha-automation-action-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
ha-alert {
display: block;
margin-bottom: 16px;
border-radius: var(--ha-card-border-radius, 12px);
overflow: hidden;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
@@ -293,6 +303,7 @@ export default class HaAutomationAction extends LitElement {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
}

View File

@@ -122,9 +122,11 @@ export class HaChooseAction extends LitElement implements ActionElement {
return html`
<ha-sortable
handle-selector=".handle"
draggable-selector=".option"
.disabled=${!this._showReorder || this.disabled}
group="choose-options"
.path=${[...(this.path ?? []), "choose"]}
invert-swap
>
<div class="options">
${repeat(
@@ -276,18 +278,21 @@ export class HaChooseAction extends LitElement implements ActionElement {
</div>
`
)}
<div class="buttons">
<ha-button
outlined
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.add_option"
)}
.disabled=${this.disabled}
@click=${this._addOption}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
</div>
</div>
</ha-sortable>
<ha-button
outlined
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.choose.add_option"
)}
.disabled=${this.disabled}
@click=${this._addOption}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
${this._showDefault || action.default
? html`
<h2>
@@ -296,7 +301,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
)}:
</h2>
<ha-automation-action
.path=${[...(this.path ?? []), "choose", "default"]}
.path=${[...(this.path ?? []), "default"]}
.actions=${ensureArray(action.default) || []}
.disabled=${this.disabled}
@value-changed=${this._defaultChanged}
@@ -502,8 +507,19 @@ export class HaChooseAction extends LitElement implements ActionElement {
return [
haStyle,
css`
.option {
margin: 0 0 16px 0;
.options {
padding: 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
.add-card mwc-button {
display: block;
@@ -539,7 +555,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
padding: 0 16px 16px 16px;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
@@ -547,6 +563,12 @@ export class HaChooseAction extends LitElement implements ActionElement {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`,
];
}

View File

@@ -117,10 +117,12 @@ export default class HaAutomationCondition extends LitElement {
return html`
<ha-sortable
handle-selector=".handle"
draggable-selector="ha-automation-condition-row"
.disabled=${!this._showReorder || this.disabled}
@item-moved=${this._conditionMoved}
group="conditions"
.path=${this.path}
invert-swap
>
<div class="conditions">
${repeat(
@@ -151,29 +153,29 @@ export default class HaAutomationCondition extends LitElement {
</ha-automation-condition-row>
`
)}
<div class="buttons">
<ha-button
outlined
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.add"
)}
@click=${this._addConditionDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
<ha-button
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.add_building_block"
)}
@click=${this._addConditionBuildingBlockDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
</div>
</div>
</ha-sortable>
<div class="buttons">
<ha-button
outlined
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.add"
)}
@click=${this._addConditionDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
<ha-button
.disabled=${this.disabled}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.add_building_block"
)}
@click=${this._addConditionBuildingBlockDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
</div>
`;
}
@@ -225,12 +227,14 @@ export default class HaAutomationCondition extends LitElement {
}
private _moveUp(ev) {
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index - 1;
this._move(index, newIndex);
}
private _moveDown(ev) {
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index + 1;
this._move(index, newIndex);
@@ -291,22 +295,32 @@ export default class HaAutomationCondition extends LitElement {
static get styles(): CSSResultGroup {
return css`
.conditions {
padding: 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
ha-automation-condition-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
.buttons {
order: 1;
}
ha-svg-icon {
height: 20px;
}
ha-alert {
display: block;
margin-bottom: 16px;
border-radius: var(--ha-card-border-radius, 12px);
overflow: hidden;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}

View File

@@ -1,5 +1,4 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
mdiCheck,
mdiContentDuplicate,
@@ -35,6 +34,7 @@ import "../../../components/ha-fab";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import "../../../components/ha-yaml-editor";
import "../../../components/ha-list-item";
import {
AutomationConfig,
AutomationEntity,
@@ -150,7 +150,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._showInfo}
@@ -160,20 +160,20 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._runActions}
>
${this.hass.localize("ui.panel.config.automation.editor.run")}
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
${stateObj && this._config && this.narrow
? html`<a href="/config/automation/trace/${this._config.id}">
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.show_trace"
)}
@@ -181,22 +181,22 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiTransitConnection}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</a>`
: ""}
<mwc-list-item
<ha-list-item
graphic="icon"
@click=${this._promptAutomationAlias}
.disabled=${!this.automationId || this._mode === "yaml"}
>
${this.hass.localize("ui.panel.config.automation.editor.rename")}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
${this._config && !("use_blueprint" in this._config)
? html`
<mwc-list-item
<ha-list-item
graphic="icon"
@click=${this._promptAutomationMode}
.disabled=${this._readOnly || this._mode === "yaml"}
@@ -208,11 +208,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiDebugStepOver}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
`
: ""}
<mwc-list-item
<ha-list-item
.disabled=${!this._readOnly && !this.automationId}
graphic="icon"
@click=${this._duplicate}
@@ -226,11 +226,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" @click=${this._switchUiMode}>
<ha-list-item graphic="icon" @click=${this._switchUiMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${this._mode === "gui"
? html`<ha-svg-icon
@@ -239,8 +239,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon" @click=${this._switchYamlMode}>
</ha-list-item>
<ha-list-item graphic="icon" @click=${this._switchYamlMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
${this._mode === "yaml"
? html`<ha-svg-icon
@@ -249,11 +249,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${!stateObj}
@click=${this._toggle}
@@ -267,9 +267,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
.disabled=${!this.automationId}
class=${classMap({ warning: Boolean(this.automationId) })}
graphic="icon"
@@ -282,7 +282,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
.path=${mdiDelete}
>
</ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</ha-button-menu>
${this._config

View File

@@ -232,6 +232,7 @@ export class HaAutomationTrace extends LitElement {
<div class="main">
<div class="graph">
<hat-script-graph
.hass=${this.hass}
.trace=${this._trace}
.selected=${this._selected?.path}
@graph-node-selected=${this._pickNode}

View File

@@ -74,10 +74,12 @@ export default class HaAutomationTrigger extends LitElement {
return html`
<ha-sortable
handle-selector=".handle"
draggable-selector="ha-automation-trigger-row"
.disabled=${!this._showReorder || this.disabled}
@item-moved=${this._triggerMoved}
group="triggers"
.path=${this.path}
invert-swap
>
<div class="triggers">
${repeat(
@@ -107,18 +109,20 @@ export default class HaAutomationTrigger extends LitElement {
</ha-automation-trigger-row>
`
)}
<div class="buttons">
<ha-button
outlined
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.add"
)}
.disabled=${this.disabled}
@click=${this._addTriggerDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
</div>
</div>
</ha-sortable>
<ha-button
outlined
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.add"
)}
.disabled=${this.disabled}
@click=${this._addTriggerDialog}
>
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
</ha-button>
`;
}
@@ -176,12 +180,14 @@ export default class HaAutomationTrigger extends LitElement {
}
private _moveUp(ev) {
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index - 1;
this._move(index, newIndex);
}
private _moveDown(ev) {
ev.stopPropagation();
const index = (ev.target as any).index;
const newIndex = index + 1;
this._move(index, newIndex);
@@ -240,22 +246,29 @@ export default class HaAutomationTrigger extends LitElement {
static get styles(): CSSResultGroup {
return css`
.triggers {
padding: 16px;
margin: -16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sortable-ghost {
background: none;
border-radius: var(--ha-card-border-radius, 12px);
}
.sortable-drag {
background: none;
}
ha-automation-trigger-row {
display: block;
margin-bottom: 16px;
scroll-margin-top: 48px;
}
ha-svg-icon {
height: 20px;
}
ha-alert {
display: block;
margin-bottom: 16px;
border-radius: var(--ha-card-border-radius, 16px);
overflow: hidden;
}
.handle {
padding: 12px 4px;
padding: 12px;
cursor: move; /* fallback if grab cursor is unsupported */
cursor: grab;
}
@@ -263,6 +276,12 @@ export default class HaAutomationTrigger extends LitElement {
pointer-events: none;
height: 24px;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
order: 1;
}
`;
}
}

View File

@@ -1,9 +1,8 @@
import "@material/mwc-button";
import { css, html, LitElement, PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatDateTime } from "../../../../common/datetime/format_date_time";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import { debounce } from "../../../../common/util/debounce";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
@@ -37,8 +36,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
@state() private _subscription?: SubscriptionInfo;
@state() private _rtlDirection: "rtl" | "ltr" = "rtl";
protected render() {
return html`
<hass-subpage
@@ -172,13 +169,11 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
<cloud-remote-pref
.hass=${this.hass}
.cloudStatus=${this.cloudStatus}
dir=${this._rtlDirection}
></cloud-remote-pref>
<cloud-tts-pref
.hass=${this.hass}
.cloudStatus=${this.cloudStatus}
dir=${this._rtlDirection}
></cloud-tts-pref>
<ha-tip .hass=${this.hass}>
@@ -193,7 +188,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
.hass=${this.hass}
.narrow=${this.narrow}
.cloudStatus=${this.cloudStatus}
dir=${this._rtlDirection}
></cloud-webhooks>
</ha-config-section>
</div>
@@ -205,15 +199,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
this._fetchSubscriptionInfo();
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.locale !== this.hass.locale) {
this._rtlDirection = computeRTLDirection(this.hass);
}
}
}
protected override hassSubscribe() {
const googleCheck = debounce(
() => {
@@ -272,10 +257,6 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
fireEvent(this, "ha-refresh-cloud-status");
}
_computeRTLDirection(hass) {
return computeRTLDirection(hass);
}
static get styles() {
return [
haStyle,

View File

@@ -13,6 +13,7 @@ import {
CloudStatusLoggedIn,
connectCloudRemote,
disconnectCloudRemote,
updateCloudPref,
} from "../../../../data/cloud";
import type { HomeAssistant } from "../../../../types";
import { showToast } from "../../../../util/toast";
@@ -29,7 +30,8 @@ export class CloudRemotePref extends LitElement {
return nothing;
}
const { remote_enabled } = this.cloudStatus.prefs;
const { remote_enabled, remote_allow_remote_enable } =
this.cloudStatus.prefs;
const {
remote_connected,
@@ -126,6 +128,12 @@ export class CloudRemotePref extends LitElement {
.path=${mdiContentCopy}
@click=${this._copyURL}
></ha-svg-icon>
<ha-formfield .label=${"Allow external activation"}>
<ha-switch
.checked=${remote_allow_remote_enable}
@change=${this._toggleAllowRemoteEnabledChanged}
></ha-switch>
</ha-formfield>
</div>
<div class="card-actions">
<mwc-button @click=${this._openCertInfo}>
@@ -160,6 +168,20 @@ export class CloudRemotePref extends LitElement {
}
}
private async _toggleAllowRemoteEnabledChanged(ev) {
const toggle = ev.target as HaSwitch;
try {
await updateCloudPref(this.hass, {
remote_allow_remote_enable: toggle.checked,
});
fireEvent(this, "ha-refresh-cloud-status");
} catch (err: any) {
alert(err.message);
toggle.checked = !toggle.checked;
}
}
private async _copyURL(ev): Promise<void> {
const url = ev.currentTarget.url;
await copyToClipboard(url);
@@ -179,14 +201,12 @@ export class CloudRemotePref extends LitElement {
.header-actions {
position: absolute;
right: 24px;
inset-inline-end: 24px;
inset-inline-start: initial;
top: 24px;
display: flex;
flex-direction: row;
}
:host([dir="rtl"]) .header-actions {
right: auto;
left: 24px;
}
.header-actions .icon-link {
margin-top: -16px;
margin-right: 8px;
@@ -218,6 +238,9 @@ export class CloudRemotePref extends LitElement {
color: var(--secondary-text-color);
cursor: pointer;
}
ha-formfield {
margin-top: 8px;
}
`;
}
}

View File

@@ -177,12 +177,10 @@ export class CloudTTSPref extends LitElement {
.example {
position: absolute;
right: 16px;
inset-inline-end: 16px;
inset-inline-start: initial;
top: 16px;
}
:host([dir="rtl"]) .example {
right: auto;
left: 24px;
}
.row {
display: flex;
}

View File

@@ -144,15 +144,6 @@ export class CloudLogin extends LitElement {
"ui.panel.config.cloud.login.password_error_msg"
)}
></ha-textfield>
<button
class="link pwd-forgot-link"
.disabled=${this._requestInProgress}
@click=${this._handleForgotPassword}
>
${this.hass.localize(
"ui.panel.config.cloud.login.forgot_password"
)}
</button>
</div>
<div class="card-actions">
<ha-progress-button
@@ -162,6 +153,15 @@ export class CloudLogin extends LitElement {
"ui.panel.config.cloud.login.sign_in"
)}</ha-progress-button
>
<button
class="link pwd-forgot-link"
.disabled=${this._requestInProgress}
@click=${this._handleForgotPassword}
>
${this.hass.localize(
"ui.panel.config.cloud.login.forgot_password"
)}
</button>
</div>
</ha-card>
@@ -311,11 +311,6 @@ export class CloudLogin extends LitElement {
display: flex;
flex-direction: column;
}
.pwd-forgot-link {
color: var(--secondary-text-color) !important;
text-align: right !important;
align-self: flex-end;
}
`,
];
}

View File

@@ -9,7 +9,6 @@ import {
} from "lit";
import { customElement, state } from "lit/decorators";
import { computeStateName } from "../../../../../../common/entity/compute_state_name";
import { computeRTLDirection } from "../../../../../../common/util/compute_rtl";
import "../../../../../../components/ha-dialog";
import "../../../../../../components/ha-formfield";
import "../../../../../../components/ha-switch";
@@ -51,8 +50,6 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
return nothing;
}
const dir = computeRTLDirection(this.hass!);
return html`
<ha-dialog
open
@@ -72,7 +69,6 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
.label=${this.hass!.localize(
"ui.dialogs.mqtt_device_debug_info.deserialize"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._showDeserialized}
@@ -87,7 +83,6 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
.label=${this.hass!.localize(
"ui.dialogs.mqtt_device_debug_info.show_as_yaml"
)}
.dir=${dir}
>
<ha-switch
.checked=${this._showAsYaml}

View File

@@ -20,7 +20,6 @@ import {
} from "../../../common/integrations/protocolIntegrationPicked";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import { computeRTL } from "../../../common/util/compute_rtl";
import {
DataTableColumnContainer,
RowClickedEvent,
@@ -488,7 +487,6 @@ export class HaConfigDeviceDashboard extends LitElement {
.label=${this.hass.localize("ui.panel.config.devices.add_device")}
extended
@click=${this._addDevice}
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>

View File

@@ -31,11 +31,11 @@ import "../../../components/ha-area-picker";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button-next";
import "../../../components/ha-icon-picker";
import "../../../components/ha-state-icon";
import "../../../components/ha-list-item";
import "../../../components/ha-radio";
import "../../../components/ha-select";
import "../../../components/ha-settings-row";
import "../../../components/ha-state-icon";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/ha-textfield";
@@ -52,10 +52,6 @@ import {
createConfigFlow,
handleConfigFlowStep,
} from "../../../data/config_flow";
import {
createOptionsFlow,
handleOptionsFlowStep,
} from "../../../data/options_flow";
import { DataEntryFlowStepCreateEntry } from "../../../data/data_entry_flow";
import {
DeviceRegistryEntry,
@@ -70,9 +66,13 @@ import {
subscribeEntityRegistry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import { entityIcon } from "../../../data/icons";
import { entityIcon, entryIcon } from "../../../data/icons";
import { domainToName } from "../../../data/integration";
import { getNumberDeviceClassConvertibleUnits } from "../../../data/number";
import {
createOptionsFlow,
handleOptionsFlowStep,
} from "../../../data/options_flow";
import {
getSensorDeviceClassConvertibleUnits,
getSensorNumericDeviceClasses,
@@ -392,7 +392,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
)}
.placeholder=${this.entry.original_icon ||
stateObj?.attributes.icon ||
(stateObj && until(entityIcon(this.hass, stateObj)))}
(stateObj && until(entityIcon(this.hass, stateObj))) ||
until(entryIcon(this.hass, this.entry))}
.disabled=${this.disabled}
>
${!this._icon && !stateObj?.attributes.icon && stateObj

View File

@@ -25,6 +25,7 @@ import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { until } from "lit/directives/until";
import memoize from "memoize-one";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain";
@@ -35,7 +36,6 @@ import {
} from "../../../common/integrations/protocolIntegrationPicked";
import { navigate } from "../../../common/navigate";
import { LocalizeFunc } from "../../../common/translations/localize";
import { computeRTL } from "../../../common/util/compute_rtl";
import type {
DataTableColumnContainer,
RowClickedEvent,
@@ -43,6 +43,7 @@ import type {
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-button-menu";
import "../../../components/ha-check-list-item";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
@@ -54,6 +55,7 @@ import {
removeEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import { entryIcon } from "../../../data/icons";
import { domainToName } from "../../../data/integration";
import {
showAlertDialog,
@@ -208,14 +210,23 @@ export class HaConfigEntities extends LitElement {
title: "",
label: localize("ui.panel.config.entities.picker.headers.state_icon"),
type: "icon",
template: (entry) => html`
<ha-state-icon
title=${ifDefined(entry.entity?.state)}
slot="item-icon"
.hass=${this.hass}
.stateObj=${entry.entity}
></ha-state-icon>
`,
template: (entry) =>
entry.icon
? html`
<ha-state-icon
title=${ifDefined(entry.entity?.state)}
slot="item-icon"
.hass=${this.hass}
.stateObj=${entry.entity}
></ha-state-icon>
`
: html`
<ha-icon
icon=${until(
entryIcon(this.hass, entry as EntityRegistryEntry)
)}
></ha-icon>
`,
},
name: {
main: true,
@@ -680,7 +691,6 @@ export class HaConfigEntities extends LitElement {
extended
@click=${this._addDevice}
slot="fab"
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>`

View File

@@ -1,5 +1,5 @@
import "@material/mwc-button/mwc-button";
import { mdiClose } from "@mdi/js";
import { mdiDelete } from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
@@ -20,8 +20,6 @@ import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { MatterManageFabricsDialogParams } from "./show-dialog-matter-manage-fabrics";
const NABUCASA_FABRIC = 4939;
@customElement("dialog-matter-manage-fabrics")
class DialogMatterManageFabrics extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -61,8 +59,9 @@ class DialogMatterManageFabrics extends LitElement {
(fabric) =>
html`<ha-list-item
noninteractive
.hasMeta=${this._nodeDiagnostics?.available &&
fabric.vendor_id !== NABUCASA_FABRIC}
.hasMeta=${this._nodeDiagnostics!.available &&
fabric.fabric_index !==
this._nodeDiagnostics!.active_fabric_index}
>${fabric.vendor_name ||
fabric.fabric_label ||
fabric.vendor_id}
@@ -70,7 +69,7 @@ class DialogMatterManageFabrics extends LitElement {
@click=${this._removeFabric}
slot="meta"
.fabric=${fabric}
.path=${mdiClose}
.path=${mdiDelete}
></ha-icon-button>
</ha-list-item>`
)}
@@ -99,6 +98,9 @@ class DialogMatterManageFabrics extends LitElement {
private async _removeFabric(ev) {
const fabric: MatterFabricData = ev.target.fabric;
if (this._nodeDiagnostics!.active_fabric_index === fabric.fabric_index) {
return;
}
const fabricName =
fabric.vendor_name || fabric.fabric_label || fabric.vendor_id.toString();
const confirm = await showConfirmationDialog(this, {

View File

@@ -1,18 +1,21 @@
import "@material/mwc-button/mwc-button";
import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { mdiCloseCircle } from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-circular-progress";
import "../../../../../components/ha-qr-code";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import "../../../../../components/ha-qr-code";
import { domainToName } from "../../../../../data/integration";
import {
openMatterCommissioningWindow,
MatterCommissioningParameters,
openMatterCommissioningWindow,
} from "../../../../../data/matter";
import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { brandsUrl } from "../../../../../util/brands-url";
import { MatterOpenCommissioningWindowDialogParams } from "./show-dialog-matter-open-commissioning-window";
import { copyToClipboard } from "../../../../../common/util/copy-clipboard";
@customElement("dialog-matter-open-commissioning-window")
class DialogMatterOpenCommissioningWindow extends LitElement {
@@ -48,27 +51,50 @@ class DialogMatterOpenCommissioningWindow extends LitElement {
>
${this._commissionParams
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.panel.config.matter.open_commissioning_window.sharing_code"
)}: <b>${this._commissionParams.setup_manual_code}</b>
</p>
<p>
${this.hass.localize(
"ui.panel.config.matter.open_commissioning_window.success"
)}
<br />
${this.hass.localize(
"ui.panel.config.matter.open_commissioning_window.scan_code"
)}
</p>
<div class="sharing-code-container">
<div class="sharing-code">
<img
crossorigin="anonymous"
referrerpolicy="no-referrer"
alt=${domainToName(this.hass.localize, "matter")}
src=${brandsUrl({
domain: "matter",
type: "logo",
darkOptimized: this.hass.themes?.darkMode,
})}
/>
<ha-qr-code
.data=${this._commissionParams.setup_qr_code}
errorCorrectionLevel="quartile"
scale="6"
margin="1"
></ha-qr-code>
<span class="code"
>${this._commissionParams.setup_manual_code.substring(
0,
4
)}-${this._commissionParams.setup_manual_code.substring(
4,
7
)}-${this._commissionParams.setup_manual_code.substring(
7
)}</span
>
</div>
</div>
<ha-qr-code
.data=${this._commissionParams.setup_qr_code}
errorCorrectionLevel="quartile"
scale="6"
></ha-qr-code>
<div></div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
<mwc-button slot="primaryAction" @click=${this._copyCode}>
${this.hass.localize(
"ui.panel.config.matter.open_commissioning_window.copy_code"
)}
</mwc-button>
`
: this._status === "started"
@@ -140,9 +166,18 @@ class DialogMatterOpenCommissioningWindow extends LitElement {
}
}
private async _copyCode() {
if (!this._commissionParams) {
return;
}
await copyToClipboard(this._commissionParams.setup_manual_code);
this.closeDialog();
}
public closeDialog(): void {
this.device_id = undefined;
this._status = undefined;
this._commissionParams = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
@@ -188,6 +223,30 @@ class DialogMatterOpenCommissioningWindow extends LitElement {
.flex-container ha-svg-icon {
margin-right: 20px;
}
.sharing-code-container {
display: flex;
justify-content: center;
padding-top: 16px;
}
.sharing-code {
display: flex;
flex-direction: column;
align-items: center;
border: 2px solid;
border-radius: 16px;
padding: 16px;
}
.sharing-code img {
width: 160px;
margin-bottom: 8px;
}
.code {
font-family: monospace;
}
`,
];
}

View File

@@ -1,12 +1,12 @@
import "@material/mwc-button/mwc-button";
import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
import { mdiAlertCircle, mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import { pingMatterNode, MatterPingResult } from "../../../../../data/matter";
import { haStyleDialog } from "../../../../../resources/styles";
import { haStyle, haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { MatterPingNodeDialogParams } from "./show-dialog-matter-ping-node";
@@ -40,33 +40,24 @@ class DialogMatterPingNode extends LitElement {
>
${this._pingResult
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.panel.config.matter.ping_node.ping_complete"
)}
</p>
</div>
</div>
<div>
<mwc-list>
${Object.entries(this._pingResult).map(
([ip, success]) =>
html`<ha-list-item hasMeta
>${ip}
<ha-icon
slot="meta"
icon=${success ? "mdi:check" : "mdi:close"}
></ha-icon>
</ha-list-item>`
)}
</mwc-list>
</div>
<h2>
${this.hass.localize(
"ui.panel.config.matter.ping_node.ping_complete"
)}
</h2>
<mwc-list>
${Object.entries(this._pingResult).map(
([ip, success]) =>
html`<ha-list-item hasMeta noninteractive
>${ip}
<ha-svg-icon
slot="meta"
.path=${success ? mdiCheckCircle : mdiAlertCircle}
class=${success ? "success" : "failed"}
></ha-svg-icon>
</ha-list-item>`
)}
</mwc-list>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.common.close")}
</mwc-button>
@@ -151,6 +142,7 @@ class DialogMatterPingNode extends LitElement {
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
.success {
@@ -170,23 +162,22 @@ class DialogMatterPingNode extends LitElement {
margin-top: 16px;
}
.stage ha-svg-icon {
width: 16px;
height: 16px;
}
.stage {
padding: 8px;
}
ha-svg-icon {
width: 68px;
height: 48px;
mwc-list {
--mdc-list-side-padding: 0;
}
.flex-container ha-circular-progress,
.flex-container ha-svg-icon {
margin-right: 20px;
}
.flex-container ha-svg-icon {
width: 68px;
height: 48px;
}
`,
];
}

View File

@@ -1,7 +1,6 @@
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
@@ -82,7 +81,6 @@ export class ZHAClustersDataTable extends LitElement {
.id=${"cluster_id"}
selectable
auto-height
.dir=${computeRTLDirection(this.hass)}
.searchLabel=${this.hass.localize("ui.components.data-table.search")}
.noDataText=${this.hass.localize("ui.components.data-table.no-data")}
></ha-data-table>

Some files were not shown because too many files have changed in this diff Show More