Compare commits

..

327 Commits

Author SHA1 Message Date
Bram Kragten
e98eb8de7f Add inital form data to hui-form-editor 2023-12-12 09:58:50 +01:00
renovate[bot]
12e6701ffa Update dependency marked to v11.0.1 (#19007) 2023-12-11 20:56:46 -05:00
karwosts
7167b66719 Don't leave empty arrays in the service control target (#18986) 2023-12-11 11:19:28 +01:00
dependabot[bot]
a52ba5fbd1 Bump actions/stale from 8.0.0 to 9.0.0 (#19004) 2023-12-11 08:06:47 +01:00
dependabot[bot]
22cf903656 Bump actions/setup-python from 4 to 5 (#19005) 2023-12-11 08:05:40 +01:00
dependabot[bot]
d77b657036 Bump actions/labeler from 4.3.0 to 5.0.0 (#19003) 2023-12-11 07:46:59 +01:00
renovate[bot]
4a3038c12c Update dependency hls.js to v1.4.13 (#18994) 2023-12-10 22:02:24 -05:00
renovate[bot]
3ac7cd5d4a Update dependency @codemirror/state to v6.3.3 (#18984) 2023-12-09 20:57:44 -05:00
renovate[bot]
58eddd2b42 Update dependency typescript to v5.3.3 (#18993) 2023-12-09 16:44:38 -05:00
renovate[bot]
d808da68bd Update dependency @lokalise/node-api to v12.1.0 (#18973) 2023-12-08 22:09:23 -05:00
renovate[bot]
2ed4d1efa0 Update dependency @braintree/sanitize-url to v7 (#18975) 2023-12-08 22:00:36 -05:00
Paul Bottein
fcb9e13a84 Bumped version to 20231208.2 2023-12-08 14:49:04 +01:00
Bram Kragten
3ada2f3279 Fix label when there is no target (#18969) 2023-12-08 13:38:01 +00:00
Paul Bottein
8d2d45ae4e Add dropdown style to hvac_modes feature (#18963) 2023-12-08 13:43:59 +01:00
Simon Lamon
c9e6963387 Fix todo url (#18954)
* Fix todo url

* Move searchParams

* Update src/panels/todo/ha-panel-todo.ts

* check if saved entity exists

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-12-08 13:35:57 +01:00
Paul Bottein
6d36b0e28c Hide climate mode control on default dashboard if there is only one hvac mode (#18964)
Hide hvac mode on default dashboard if there is only one hvac mode
2023-12-08 10:58:08 +00:00
Paul Bottein
61117bb34f Bumped version to 20231208.1 2023-12-08 10:30:33 +01:00
Simon Lamon
86a3c32844 Fix area in device picker (#18955)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-12-08 09:17:40 +00:00
Paul Bottein
b855b3e103 Format number attribute for media player (#18949) 2023-12-08 10:14:55 +01:00
renovate[bot]
80edeebab9 Update dependency chart.js to v4.4.1 (#18957) 2023-12-07 20:38:49 -05:00
renovate[bot]
f366e287b1 Update typescript-eslint monorepo to v6.13.2 (#18953) 2023-12-07 20:34:08 -05:00
renovate[bot]
9ce8684aba Update dependency lint-staged to v15.2.0 (#18930) 2023-12-07 20:32:47 -05:00
renovate[bot]
caa6ea531c Update dependency @types/luxon to v3.3.7 (#18956) 2023-12-07 20:28:26 -05:00
Paul Bottein
cca1183ee3 Revert "Remove card features for humidifier and climate on default dashboard" (#18944)
* Revert "Remove card features for humidifier and climate on default dashboard (#18747)"

This reverts commit 2afd2788e2.

* Rename humidifier feature
2023-12-07 16:53:07 +01:00
Paul Bottein
0f9c97aea0 Center login content for every screen size (#18943) 2023-12-07 13:45:05 +00:00
Paul Bottein
8d08aa8c79 Fix tile card interaction when border width is set to 0 (#18941) 2023-12-07 14:40:23 +01:00
renovate[bot]
eebcab435d Update dependency rollup-plugin-visualizer to v5.10.0 (#18936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-07 11:15:26 +01:00
Bram Kragten
76d3c6e237 Revert fetching config flow after subscribe for progress (#18939) 2023-12-07 09:31:34 +01:00
karwosts
a820ca1e90 Fix number selector display for 0 (#18927) 2023-12-06 17:03:44 +01:00
Paul Bottein
39260d172f Bumped version to 20231206.0 2023-12-06 14:23:09 +01:00
Paul Bottein
e646528b86 Fix empty classmap in state control (#18922)
* Fix empy classmap in state control

* Don't use class map
2023-12-06 14:22:32 +01:00
Bram Kragten
86726102fb Fix issues with circular progress (#18920) 2023-12-06 14:21:28 +01:00
Bram Kragten
1330558819 Force media player browser dialog re-layout after open animation (#18910) 2023-12-06 14:14:14 +01:00
Paul Bottein
b4ab0fc10b Reduce sensitivity of the circular slider on touch devices (#18921) 2023-12-06 14:13:19 +01:00
Paul Bottein
15becf9ef6 Add ellipsis to thermostat and humidifier card title (#18924) 2023-12-06 13:28:03 +01:00
Paul Bottein
ef3785ce9f Fix particles over alert in login screen (#18923) 2023-12-06 13:16:47 +01:00
renovate[bot]
134e13005d Update dependency eslint-config-prettier to v9.1.0 (#18909) 2023-12-05 18:18:46 -05:00
Bram Kragten
dccd9b2541 Bumped version to 20231205.0 2023-12-05 18:07:59 +01:00
Paul Bottein
de83ad7a7a Reduce circular slider sensitivity when used in card (#18908)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-12-05 18:06:29 +01:00
Paul Bottein
7e55b9e6b8 Add humidifier modes and toggle feature (#18912) 2023-12-05 18:06:03 +01:00
Paul Bottein
1b74ca47bf Add target humidity feature (#18913) 2023-12-05 16:42:07 +01:00
Bram Kragten
c3d4be9ceb wrap config validation (#18914) 2023-12-05 14:32:35 +00:00
karwosts
b8d0c7f7c7 Consistent sortable cursor (#18897) 2023-12-05 10:45:20 +01:00
karwosts
92bce4078f Confirmation dialog for todo clear checked items (#18905) 2023-12-05 10:44:40 +01:00
Bram Kragten
ff8f0697c2 Fix media control card background image (#18891) 2023-12-05 09:15:04 +01:00
renovate[bot]
dddba7af00 Update dependency eslint to v8.55.0 (#18904) 2023-12-05 02:19:03 +00:00
karwosts
14a49d6664 Copy to clipboard button for service response (#18872) 2023-12-04 14:24:35 +00:00
Bram Kragten
22da402d56 Fix border radius for outlined icon button old browsers (#18888) 2023-12-04 15:17:43 +01:00
Bram Kragten
aa4acc6572 Use resize polyfill from lit virtualizer (#18886) 2023-12-04 08:34:31 -05:00
Simon Lamon
4d432fba57 Hide battery level and battery icon in tile card (#18826) 2023-12-04 14:26:45 +01:00
G Johansson
97b71c785b Fix use of "" on lock entity more info (use default code) (#18706)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-12-04 13:23:11 +00:00
Paul Bottein
8a93284bb3 Use resize-controller instead of container queries (#18885) 2023-12-04 14:16:10 +01:00
renovate[bot]
bb2abe4efc Update dependency marked to v11 (#18860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-12-04 13:11:51 +00:00
Simon Lamon
ccada33caf Accessibility improvements for circular progress indicators (#18506) 2023-12-04 14:06:25 +01:00
ildar170975
c5f15ee6ba Update ha-chart-base.ts: fix "Tooltip: a long name may overflow" (#18849) 2023-12-04 14:05:02 +01:00
renovate[bot]
6f240ec681 Update vaadinWebComponents monorepo to v24.2.5 (#18887)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 12:59:01 +00:00
renovate[bot]
fc6aef138d Update dependency fs-extra to v11.2.0 (#18848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 13:57:40 +01:00
Bram Kragten
6d0eb05954 Bumped version to 20231204.0 2023-12-04 12:08:11 +01:00
Bram Kragten
96fbbc55e1 Make history split by device class option in history card (#18871) 2023-12-04 12:06:26 +01:00
Bram Kragten
7e74502ba3 Add particles to login, move forgot password link (#18868) 2023-12-04 11:34:42 +01:00
Paul Bottein
b273b31b03 Don't allow yaml dashboard to be in edit mode (#18879) 2023-12-04 10:17:52 +01:00
renovate[bot]
cf00ac74c6 Update dependency sortablejs to v1.15.1 (#18876)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 00:57:31 -05:00
renovate[bot]
cc702f0fb3 Update dependency @types/luxon to v3.3.6 (#18875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 00:53:54 -05:00
renovate[bot]
cc6eb5789d Update dependency clean-css to v5.3.3 (#18870) 2023-12-03 11:28:51 -05:00
renovate[bot]
669b80a9e1 Update babel monorepo to v7.23.5 (#18865) 2023-12-02 19:41:02 -05:00
karwosts
4c4fddee94 Fix for energy cards not refreshing hourly (#18854) 2023-12-02 23:33:04 +01:00
renovate[bot]
b28a4e6b50 Update fullcalendar monorepo to v6.1.10 (#18859) 2023-12-02 03:26:39 +00:00
renovate[bot]
bdd6a55a84 Update dependency @codemirror/commands to v6.3.2 (#18857) 2023-12-01 22:16:38 -05:00
renovate[bot]
d1ebc06994 Update typescript-eslint monorepo to v6.13.1 (#18853) 2023-12-01 22:15:42 -05:00
renovate[bot]
2a150788b4 Update typescript-eslint monorepo to v6.13.0 (#18845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-30 15:06:07 -05:00
Bram Kragten
72caf72e80 Bumped version to 20231130.0 2023-11-30 17:18:58 +01:00
Bram Kragten
a6ff1899df Prevent double quick action (#18842) 2023-11-30 16:18:04 +00:00
Paul Bottein
6a999597df Rename number feature to number value feature (#18836) 2023-11-30 16:17:54 +00:00
Bram Kragten
219fc9e53a Group history by device class (#18841) 2023-11-30 17:17:26 +01:00
Bram Kragten
861959ed2d add previous button to login screen (#18837) 2023-11-30 17:06:22 +01:00
Damian Sypniewski
f7f50294e7 Added hold and double tap actions for tile card (#18345) 2023-11-30 16:03:07 +01:00
Bram Kragten
a226333c1e Update area-filter-dialog.ts 2023-11-30 15:16:01 +01:00
Bram Kragten
75f18aa69b Move disabled items always to the bottom, only change color of text, … (#18814)
* Move disabled items always to the bottom, only change color of text, dont drag disabled items

* Don't allow to drag hidden items

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2023-11-30 14:37:10 +01:00
Simon Lamon
60ff4fdc1f Ensure helper type is always localized (#18825)
* Ensure helper type is always localized

* Remove the check
2023-11-30 14:14:13 +01:00
renovate[bot]
8d14cb0ab7 Update vaadinWebComponents monorepo to v24.2.4 (#18835)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-30 08:12:43 -05:00
Bram Kragten
606c809880 Update size of icon on launch screen (#18833) 2023-11-30 12:34:46 +01:00
Simon Lamon
f1692038f8 Fix developer tools set state for unexisting entity (#18827)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-30 10:59:51 +00:00
Paul Bottein
79f5ec0cd5 Fix dashboard crashed when deleting hidden area (#18832) 2023-11-30 10:59:31 +00:00
renovate[bot]
416a2a080d Update CodeMirror (#18829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-30 10:58:49 +00:00
Paul Bottein
92dc8b1561 Fix map dark mode (#18830) 2023-11-30 11:47:44 +01:00
karwosts
07b807adfc Fit map on load (#18819) 2023-11-30 10:49:40 +01:00
Simon Lamon
843430ef41 Cleanup iron resize event (#18813) 2023-11-29 21:02:57 +01:00
Bram Kragten
b5b2392dd2 Guard for no person for user (#18812) 2023-11-29 20:59:06 +01:00
karwosts
bf24740c7b Fix non-hass shouldUpdate regressions (#18810) 2023-11-29 14:28:38 +00:00
Bram Kragten
4ec11c7cf6 Bumped version to 20231129.1 2023-11-29 15:25:35 +01:00
Bram Kragten
8ff36d0ccf Merge branch 'master' into dev 2023-11-29 12:39:58 +01:00
Bram Kragten
e7e465df69 Bumped version to 20231129.0 2023-11-29 12:38:24 +01:00
karwosts
0c042079ed ha-selector-selector implementation (#18421)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-29 11:09:49 +00:00
lunmay
a5a9bcafa7 Typo fixes: "url" -> "URL" in blueprint (#18808) 2023-11-29 11:48:20 +01:00
Bram Kragten
867f625b0a Fix has changed for when hass is not changed (#18778) 2023-11-29 11:20:04 +01:00
Jan Bouwhuis
3876c67588 Add translation support for script errors (#18786)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-29 11:14:29 +01:00
Paul Bottein
93158bb3af Add re-import blueprint (#18807)
* Add re-import blueprint

* Add disabled color to icon
2023-11-29 10:56:52 +01:00
schelv
5c47d8652d Add Option For Kelvin Unit To Color Temperature Selector (#18627) 2023-11-29 10:56:44 +01:00
Josh McCarty
eda0d12867 Dev tools events layout (#18389)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-29 09:51:33 +00:00
Bram Kragten
4a53de4466 Lazy load language picker in auth and onboarding (#18806) 2023-11-29 10:50:05 +01:00
karwosts
e730649a8d Fix condition key function for template shorthand notation (#18107) 2023-11-29 10:46:22 +01:00
Steve Repsher
3a94deef69 Fix minifying templates from dependencies (#18795) 2023-11-29 10:35:53 +01:00
Bram Kragten
c1c186d279 Cleanup event listeners (#18803) 2023-11-29 10:21:12 +01:00
Matthias Alphart
7356db919a Default to ltr for reading direction (#18767) 2023-11-29 10:17:31 +01:00
Amit Finkelstein
44157a5df3 Fix map auto-fit (#18801) 2023-11-29 10:16:34 +01:00
Paul Bottein
2acd1ff5e8 Refactor and cleanup device picker (#18804) 2023-11-29 10:01:54 +01:00
Bram Kragten
150a5571cf Refactor and cleanup entity config page (#18797) 2023-11-29 09:19:53 +01:00
Bram Kragten
1eb25f4829 Add import thread credentials to external bus (#18777) 2023-11-29 09:01:33 +01:00
Bram Kragten
d8d4ecf79f Refactor and clean up of device pages (#18796) 2023-11-29 09:01:11 +01:00
karwosts
081bd98e98 Fix map autofit (#18799) 2023-11-29 00:29:52 +01:00
Robert Van Gorkom
6134f415e9 Fix race condition with picture-elements card custom elements (#18765) 2023-11-28 21:23:06 +00:00
karwosts
b6a7581eca Augment history panel with Long Term Statistics (#18213)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-28 22:14:05 +01:00
Paul Bottein
7727f34e8f Add area filter selector for default dashboard (#18779)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-28 21:43:34 +01:00
Amit Finkelstein
9b20e1cf56 Avoid re-rendering map with "sources" (#18635) 2023-11-28 20:30:28 +01:00
Steve Repsher
b901ecacca Add missing Intl polyfills to modern build (#18794) 2023-11-28 19:28:10 +00:00
Simon Lamon
7691f7eb05 PolymerElement cleanup (#18793) 2023-11-28 20:25:12 +01:00
Bram Kragten
8c0c56b954 Login page feedback (#18791) 2023-11-28 20:21:10 +01:00
karwosts
d968a20862 More disconnected callback errors (#18792) 2023-11-28 20:19:49 +01:00
karwosts
9f1cd80a31 Add a Grid/List toggle for Media Browser (#18256)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-28 16:34:34 +00:00
Paul Bottein
5a9ccc5ae7 Fix humidifier more info (#18789) 2023-11-28 17:30:39 +01:00
Paul Bottein
6d7b0c5626 Fix tile features colors (#18788) 2023-11-28 17:30:23 +01:00
karwosts
400b8034e1 missing disconnectedCallback in energy-period-selector (#18790) 2023-11-28 17:27:08 +01:00
Bram Kragten
05a22e3271 Keep targets when switching services (#18772) 2023-11-28 14:42:40 +01:00
Bram Kragten
c1f76e0565 Update ha-authorize.ts 2023-11-28 13:44:23 +01:00
Simon Lamon
4582c3ba0a Migrate state cards from Polymer to Lit (#18257) 2023-11-28 13:30:39 +01:00
Kendell R
f4ee734ea3 Use new localize format (#18574)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-28 12:29:41 +00:00
Bram Kragten
2087028c47 Update home.markdown 2023-11-28 13:24:09 +01:00
Paul Bottein
37a56b250a Add multiple option to text selector (#18785)
* Add multiple option to text selector

* Make multiple optional

* Update src/components/ha-selector/ha-selector-text.ts

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

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-28 11:28:29 +01:00
Márton Braun
71a41be20a Add fit_zones parameter to map card (#18760) 2023-11-28 11:04:43 +01:00
Bram Kragten
ffb19b31a5 Use subscribe for todo items (#18776) 2023-11-28 08:28:13 +01:00
Bram Kragten
db68c7faa9 Log errors when downloading lokalise translations (#18781) 2023-11-28 08:21:19 +01:00
renovate[bot]
07ae958eb0 Update dependency vis-data to v7.1.9 (#18783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-27 19:16:43 -05:00
renovate[bot]
b62b5f6575 Update dependency rollup-plugin-visualizer to v5.9.3 (#18782)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-27 19:15:00 -05:00
Bram Kragten
2e1fb9df66 Update design of login page (#18770) 2023-11-27 23:06:51 +01:00
Bram Kragten
31c91cea9a Update translations_upload_base 2023-11-27 17:59:13 +01:00
Bram Kragten
9dc844ca28 Dont change pipeline if local storage changes (#18773) 2023-11-27 15:42:07 +01:00
karwosts
02ebc028c8 Add custom card names to the edit card dialog header (#18750)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-27 15:30:39 +01:00
karwosts
5d35605b82 Filter energy card updates with shouldUpdate (#18763) 2023-11-27 15:29:15 +01:00
Bram Kragten
22e86af5b3 Fix race condition with Data Entry Flow Progress (#18775) 2023-11-27 11:28:49 +01:00
dependabot[bot]
8c39ed46a8 Bump dessant/lock-threads from 5.0.0 to 5.0.1 (#18769) 2023-11-27 07:40:59 +01:00
Raman Gupta
5965c3fdaa Add alert to zwave_js device info page for custom device config (#18686) 2023-11-26 11:09:02 +01:00
renovate[bot]
15a5d2bc38 Update dependency @bundle-stats/plugin-webpack-filter to v4.8.3 (#18761) 2023-11-25 22:22:03 -05:00
Paul Bottein
0432cc95fc Allow overriding blueprints (#18718) 2023-11-25 11:49:58 +01:00
renovate[bot]
6dafaac021 Update dependency @types/sortablejs to v1.15.7 (#18756)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-25 00:03:20 -05:00
renovate[bot]
d8929c9252 Update dependency @types/mocha to v10.0.6 (#18755)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-25 00:02:42 -05:00
renovate[bot]
5fa3a720b3 Update dependency @bundle-stats/plugin-webpack-filter to v4.8.2 (#18752) 2023-11-24 23:34:03 -05:00
renovate[bot]
6d3b838b6a Update dependency @types/leaflet-draw to v1.0.11 (#18753) 2023-11-24 23:33:03 -05:00
Bram Kragten
58e0179321 Add local login flow (#18717) 2023-11-24 17:22:05 +01:00
Paul Bottein
eaf7e29b8a Fix thermostat humidifier card size (#18749) 2023-11-24 16:27:01 +01:00
renovate[bot]
260f1df1b9 Update babel monorepo to v7.23.4 (#18730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-24 15:58:57 +01:00
renovate[bot]
a88a4ef82c Update dependency typescript to v5.3.2 (#18736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-24 15:57:57 +01:00
Steve Repsher
075cca5991 Inject polyfills where used (#18719) 2023-11-24 15:57:21 +01:00
Simon Lamon
1d2dc37f75 Migrate push notification toggle from Polymer to Lit (#18565) 2023-11-24 15:47:09 +01:00
Paul Bottein
3da7025d78 Improve search for single character (#18748) 2023-11-24 14:37:59 +00:00
Paul Bottein
15b1d8ee14 Move more info control component to state control (#18745) 2023-11-24 14:33:11 +00:00
karwosts
239d8fa948 Fix card-picker search for single character (#18733)
* Fix card-picker search for single character

* Update src/panels/lovelace/editor/card-editor/hui-card-picker.ts

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2023-11-24 15:23:27 +01:00
Bram Kragten
1de2e6161d Make thermostat centered in panel or grid view (#18731)
* Make thermostat centered in panel or grid view

* Update hui-humidifier-card.ts
2023-11-24 14:17:06 +01:00
Paul Bottein
3e7f008277 Rename tile features to card features (#18732) 2023-11-24 13:36:58 +01:00
G Johansson
31a93d360d Make lock default code a password field (#18722) 2023-11-24 13:11:18 +01:00
Paul Bottein
2afd2788e2 Remove card features for humidifier and climate on default dashboard (#18747) 2023-11-24 12:53:41 +01:00
renovate[bot]
322fa99147 Update dependency @types/tar to v6.1.10 (#18742)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-24 11:13:49 +00:00
renovate[bot]
c43ad52ff1 Update dependency @types/sortablejs to v1.15.6 (#18741)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-24 12:03:01 +01:00
Paul Bottein
6aa9737481 Fix ha control select option size (#18746) 2023-11-24 12:02:44 +01:00
renovate[bot]
7d9d6b99d9 Update dependency @types/luxon to v3.3.5 (#18739) 2023-11-24 03:46:28 +00:00
renovate[bot]
9c69e6f87c Update dependency @types/mocha to v10.0.5 (#18740) 2023-11-23 22:36:00 -05:00
renovate[bot]
0c90021895 Update typescript-eslint monorepo to v6.12.0 (#18734) 2023-11-23 21:59:42 -05:00
Paul Bottein
9163b9c124 Add new design to humidifier card (#18711) 2023-11-23 00:37:19 +01:00
renovate[bot]
61717e1529 Update dependency core-js to v3.33.3 (#18721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-22 13:38:08 -05:00
renovate[bot]
168d418c27 Update dependency @bundle-stats/plugin-webpack-filter to v4.8.1 (#18720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-22 13:36:11 -05:00
Paul Bottein
c787c920fc Add new design to thermostat card (#18709)
* Add new design for thermostat card

* Add feature to thermostat card

* Fix margin

* Add current

* use big number component

* Add current

* Fix translations

* Add theme and name options

* Reduce margin on small card

* Fix types

* Add hvac mode to default dashboard

* Don't put feature full size

* Full width for features

* Improve design on small screen
2023-11-22 14:42:42 +01:00
Steve Repsher
1526209f82 Adjust bundle filenames for stats and debugging (#18677) 2023-11-22 14:11:23 +01:00
Steve Repsher
cf355c419d Fix Babel runtime helper injections (#18698) 2023-11-22 14:10:28 +01:00
Paul Bottein
eef024587b Use entity component translations for update entity (#18608) 2023-11-22 12:43:33 +01:00
Paul Bottein
270d463d02 Add configuration to default dashboard (#18658)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2023-11-22 12:40:17 +01:00
renovate[bot]
0735705dd3 Update dependency eslint to v8.54.0 (#18707) 2023-11-20 19:21:17 -05:00
dependabot[bot]
0a1dbe6d9d Bump dessant/lock-threads from 4.0.1 to 5.0.0 (#18699)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2023-11-20 08:47:54 +01:00
renovate[bot]
15395275ba Update dependency prettier to v3.1.0 (#18675)
* Update dependency prettier to v3.1.0

* Reformat

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2023-11-19 23:31:56 -05:00
karwosts
6e66ba202f Add a button to delete all refresh tokens (#18680)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-19 13:32:25 +01:00
karwosts
5ebe1e0369 Add shortcut from energy dashboard to energy settings (#18596) 2023-11-19 13:31:55 +01:00
renovate[bot]
860ab7a3ba Lock file maintenance (#18696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 22:50:36 -05:00
renovate[bot]
d499640304 Update dependency @lezer/highlight to v1.2.0 (#18672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 22:28:46 -05:00
renovate[bot]
4f3f516767 Update dependency rrule to v2.8.1 (#18648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 21:06:30 -05:00
renovate[bot]
0996dbf2a1 Update formatjs monorepo (#18684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 21:37:37 +00:00
renovate[bot]
bc59e52cea Update dependency marked to v10 (#18651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-18 16:33:31 -05:00
wittypluck
b8d4e957e2 Add logarithmic scale option to Statistics and History graph cards (#18637) 2023-11-18 17:31:29 +01:00
karwosts
8002ec75bc Temporarily filter cleared statistics from devtools table (#18694) 2023-11-18 17:30:48 +01:00
karwosts
a45eefa742 Add live camera & custom AR to Area card (#18643) 2023-11-18 17:26:21 +01:00
Jan Bouwhuis
a1236924aa Ensure websocket_api error messages shows longer (#18690) 2023-11-17 20:35:08 +01:00
renovate[bot]
9320e8c1a3 Update Yarn to v4.0.2 (#18687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-17 11:35:32 -05:00
karwosts
addcd3a983 Typo in bug_report.yml (#18681)
Update bug_report.yml
2023-11-17 15:53:12 +01:00
Paul Bottein
e1dc73e992 Fix yaml editor lovelace (#18664) 2023-11-16 18:55:09 +01:00
renovate[bot]
ba8849ed4d Update typescript-eslint monorepo to v6.11.0 (#18679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 12:48:12 -05:00
Thomas Lovén
36bca04341 Polyfill ElementInternals for material-web components. Fix #18600 (#18638) 2023-11-16 18:17:41 +01:00
Simon Lamon
fb95de1f73 Add translations for the developer tools statistic issues (#18646) 2023-11-16 18:16:51 +01:00
karwosts
3addfc3548 Localization bugfixes in config devices/entities/helpers (#18645) 2023-11-16 18:13:31 +01:00
renovate[bot]
e2e80d1f49 Update vaadinWebComponents monorepo to v24.2.3 (#18678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 12:03:38 -05:00
renovate[bot]
a96eba2637 Update dependency @lit-labs/virtualizer to v2.0.11 (#18583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 11:55:24 -05:00
renovate[bot]
274810b954 Update dependency luxon to v3.4.4 (#18674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 11:40:03 -05:00
Paul Bottein
842df983c3 Add new dashboard dialog (#18657)
* Add new dashboard dialog

* Update default dashboard wording
2023-11-16 13:43:55 +01:00
Simon Lamon
0eeadcd31a Replace paper-input in cast (#18647) 2023-11-16 12:39:54 +01:00
Josh McCarty
8d37c5612b Move attribute pre styles to ha-attribute-value component (#18649) 2023-11-16 12:28:45 +01:00
dnikles
e94461f7fe Update dialog-add-integration.ts (#18615) 2023-11-16 12:18:18 +01:00
Bram Kragten
f5edee1e91 Add support for warnings in check_config (#18489) 2023-11-16 12:17:14 +01:00
Paul Bottein
98cdbb4559 Add minimal UI for climate and water entity without target (#18666) 2023-11-16 12:16:37 +01:00
Paul Bottein
f3f63d95b5 Restore map filter theme (#18667) 2023-11-16 12:12:17 +01:00
karwosts
244340f211 Allow search for integration name in log search (#18668) 2023-11-16 12:08:10 +01:00
Paul Bottein
75626431b1 Improve ripple background color for tile card (#18579) 2023-11-16 10:57:24 +01:00
Allen Porter
244868348b Delete To-do items in a batched single request (#18662) 2023-11-16 09:52:03 +01:00
renovate[bot]
a909c6d905 Update formatjs monorepo (#18663) 2023-11-15 20:49:16 -05:00
Michael Hansen
f2efff9d71 Use stt-vad-end event in assist debug panel (#18671)
* Use stt-vad-end event in assist debug panel

* Remove end_suffix
2023-11-15 21:36:49 +00:00
karwosts
08a7a10e1c Don't save new automation when save dialog is cancelled (#18655) 2023-11-15 18:03:47 +01:00
Simon Lamon
b35e5abd83 Fix Edit Area dialog doesn't save area (#18659)
secondary action
2023-11-15 18:00:35 +01:00
karwosts
291e9fc0a2 Fix onboarding password form (#18665) 2023-11-15 17:58:22 +01:00
Paul Bottein
de370d6384 Add todo entity_id in URL (#18653) 2023-11-15 11:26:03 +01:00
Paul Bottein
1cf928b425 Improve Lovelace types (#18652) 2023-11-15 11:13:49 +01:00
renovate[bot]
b9c62c34a0 Update dependency lint-staged to v15.1.0 (#18660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-15 10:49:33 +01:00
renovate[bot]
37554213dc Update dependency @bundle-stats/plugin-webpack-filter to v4.8.0 (#18654) 2023-11-14 20:37:44 -05:00
renovate[bot]
3845fd2ef9 Update dependency @types/tar to v6.1.9 (#18650) 2023-11-14 20:35:55 -05:00
renovate[bot]
837b36a430 Update dependency marked to v9.1.6 (#18644)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-13 19:46:13 +01:00
karwosts
bff09a7a36 Fix login password form (#18619) 2023-11-13 19:45:34 +01:00
renovate[bot]
a5e5efa526 Update dependency @codemirror/autocomplete to v6.11.0 (#18633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 22:29:54 -05:00
renovate[bot]
1cd5b9ff37 Update babel monorepo to v7.23.3 (#18632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-12 21:16:45 -05:00
renovate[bot]
6a11155dd7 Update dependency vis-data to v7.1.8 (#18625) 2023-11-11 21:09:28 -05:00
renovate[bot]
1acb76d2f0 Update dependency @types/leaflet to v1.9.8 (#18622) 2023-11-12 02:07:18 +00:00
renovate[bot]
2e8a40efd5 Update dependency @types/ua-parser-js to v0.7.39 (#18616) 2023-11-11 20:58:50 -05:00
renovate[bot]
845a0e4e39 Update dependency @types/js-yaml to v4.0.9 (#18621) 2023-11-11 20:56:30 -05:00
Paul Bottein
9417f13cc9 Add unit for number tile feature (#18602) 2023-11-10 19:30:07 +01:00
renovate[bot]
d3f52f3bc0 Update dependency @types/tar to v6.1.8 (#18614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 18:17:57 +00:00
renovate[bot]
7d0ec00fde Update dependency @types/sortablejs to v1.15.5 (#18613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 13:06:13 -05:00
renovate[bot]
6dbb5dd4be Update dependency @types/qrcode to v1.5.5 (#18610)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 13:05:36 -05:00
renovate[bot]
f30e7948cb Update dependency @types/luxon to v3.3.4 (#18605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 13:04:49 -05:00
renovate[bot]
41472d8160 Update dependency @types/serve-handler to v6.1.4 (#18612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:43:05 -05:00
renovate[bot]
e596d32426 Update dependency @types/mocha to v10.0.4 (#18606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:41:33 -05:00
renovate[bot]
89de62812f Update dependency @types/leaflet-draw to v1.0.10 (#18603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:39:56 -05:00
renovate[bot]
3e391fcbe6 Update dependency @types/html-minifier-terser to v7.0.2 (#18601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 12:37:31 -05:00
karwosts
708ab2ed69 Localize hide/show password (#18607) 2023-11-10 18:12:36 +01:00
Paul Bottein
1d45cb78fe Rename tooltipUnit to unit for slider (#18599) 2023-11-10 18:08:12 +01:00
Philip Allgaier
d3f6ebd1d0 Add unit to temperature tile number button group (#17841)
* Add unit to temperature tile number button group

* Update gallery

* Use blank before unit

* Hide unit if no space

* Fix build

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2023-11-10 14:22:10 +01:00
Joseph Abbey
21644c70b3 Add number tile feature (#18562)
* hui-number-tile-feature

* Added support for number domain

* Apply suggestions from code review (thanks @piitaya)

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

* Fixed the callback to use the correct service

* fix formatting

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2023-11-10 08:34:29 +00:00
karwosts
eb75389cac Localize clear in entity filter (#18598)
Localize  in entity filter
2023-11-10 08:27:36 +01:00
Bram Kragten
cd4f3a091b Fix add device dialog (#18591) 2023-11-10 08:27:02 +01:00
renovate[bot]
19c1973ec1 Update dependency @types/chromecast-caf-sender to v1.0.8 (#18597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 03:35:04 +00:00
renovate[bot]
a8efbe5b06 Update dependency @types/chromecast-caf-receiver to v6.0.12 (#18595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-10 03:22:14 +00:00
renovate[bot]
44b5c66c19 Update dependency @types/babel__plugin-transform-runtime to v7.9.5 (#18594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 22:08:58 -05:00
renovate[bot]
3fa3bba4d8 Update formatjs monorepo (#18592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 22:07:01 -05:00
renovate[bot]
ab09c821a2 Update typescript-eslint monorepo to v6.10.0 (#18586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-09 22:05:26 -05:00
Paul Bottein
18b5215ce6 Only update dev tools yaml editor on entity change or submit (#18575)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2023-11-09 18:24:45 +01:00
Bram Kragten
00a311bd63 Bumped version to 20231030.2 2023-11-09 18:23:17 +01:00
tzagim
749c2ab8ac Fix for RTL (#18509) 2023-11-09 18:22:27 +01:00
Kendell R
d06fdeb265 Fix "ttme" typo on the demo page (#18543) 2023-11-09 18:22:12 +01:00
Simon Lamon
06d76be2c2 Fix refresh in developer state tools (#18532) 2023-11-09 18:21:50 +01:00
David Bell
6728e8d107 Don't display hidden/non-visible calendars on the Calendar panel (#18541) 2023-11-09 18:21:32 +01:00
Kendell R
533e2bed8a Properly set direction styles (#18483) 2023-11-09 18:20:44 +01:00
karwosts
310a08f3e6 Fix choose add option (#18493) 2023-11-09 18:19:19 +01:00
Paul Bottein
0566889a1e Use strict comparison for above condition in conditional card (#18560) 2023-11-09 18:18:37 +01:00
Paul Bottein
37b7395986 Migrate chips to Material 3 chips. (#18513) 2023-11-09 17:57:37 +01:00
Bram Kragten
4933413140 Fix virtualizer with v2.0.10 (#18584) 2023-11-09 11:53:35 -05:00
Bram Kragten
1c16bd5ab2 rollback lit-labs/virtualizer to 2.0.8 (#18580) 2023-11-09 16:26:09 +01:00
Paul Bottein
3a926c6f83 Add pointer-events none to slider tooltip (#18577) 2023-11-09 14:57:23 +01:00
Paul Bottein
7a619ad55a Only update dev tools yaml editor on entity change or submit (#18575)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2023-11-09 10:00:24 +01:00
Paul Bottein
bc21425981 Add tooltip to tile sliders and more info sliders (#18567) 2023-11-08 12:47:55 +01:00
renovate[bot]
f2505c0798 Update dependency eslint to v8.53.0 (#18558)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-08 11:48:22 +01:00
Steve Repsher
cee8c756fc Remove dependency esprima (#18564) 2023-11-08 11:24:03 +01:00
renovate[bot]
396b3aace9 Update dependency @codemirror/view to v6.22.0 (#18553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 18:02:11 -05:00
renovate[bot]
28dc1e4da9 Update vaadinWebComponents monorepo to v24.2.2 (#18552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-07 17:59:54 -05:00
Kendell R
aa0419e783 Minify logos (#18542) 2023-11-07 13:39:38 +01:00
Jan Bouwhuis
fd9c24d05e Add localization to service call exceptions (#18447) 2023-11-07 12:04:53 +01:00
Paul Bottein
8bdbe8c6a6 Use strict comparison for above condition in conditional card (#18560) 2023-11-07 12:04:04 +01:00
Steve Repsher
39550cefa0 Add some Renovate groups and enable NVM updates (#18559) 2023-11-07 11:53:47 +01:00
David Bell
589e3b63c7 Don't display hidden/non-visible calendars on the Calendar panel (#18541) 2023-11-06 15:09:54 +00:00
renovate[bot]
0cc38278b9 Update dependency @lit-labs/virtualizer to v2.0.10 (#18547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:56:45 +01:00
renovate[bot]
aafdf7bed7 Update dependency vis-network to v9.1.9 (#18548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-06 15:55:24 +01:00
Simon Lamon
a7f6ce3079 Fix refresh in developer state tools (#18532) 2023-11-06 15:53:23 +01:00
karwosts
3d03f74d66 Improve template trigger/condition descriptions (#18504) 2023-11-06 10:04:58 +01:00
tzagim
e09a6a23fc Fix for RTL (#18509) 2023-11-06 10:02:44 +01:00
Kendell R
9e4bb6ed0c Fix "ttme" typo on the demo page (#18543) 2023-11-06 10:02:00 +01:00
renovate[bot]
c45c8ab5c0 Update dependency sinon to v17.0.1 (#18535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-05 14:16:21 -05:00
renovate[bot]
5ef6973933 Update dependency marked to v9.1.5 (#18523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-05 14:14:50 -05:00
renovate[bot]
1df344580a Update dependency eslint-plugin-lit-a11y to v4.1.1 (#18534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-05 14:13:25 -05:00
renovate[bot]
25acc479b9 Update dependency core-js to v3.33.2 (#18515) 2023-11-02 20:54:59 -04:00
renovate[bot]
c9f051374b Update dependency punycode to v2.3.1 (#18516) 2023-11-02 20:50:03 -04:00
renovate[bot]
bf5116fc0b Update typescript-eslint monorepo to v6.9.1 (#18518) 2023-11-02 20:47:03 -04:00
renovate[bot]
edc8cb1d2e Update dependency @rollup/plugin-replace to v5.0.5 (#18505) 2023-11-02 20:42:05 -04:00
Bram Kragten
a60bb3ae0a Clean up auth flow translation placeholders (#18502) 2023-11-02 21:02:42 +01:00
Paul Bottein
784f753f07 Display default action label in action selector (#18398) 2023-11-02 16:10:15 +01:00
Paul Bottein
e63c7e3763 Add trigger selector for blueprint (#18469) 2023-11-02 16:01:59 +01:00
karwosts
cfa522068c Localize trigger condition and add list formatting (#18488) 2023-11-01 15:42:43 +01:00
schelv
5f2375fe84 feat: Dynamically adjust gradient percentages based on min/max values (#18479) 2023-11-01 15:41:26 +01:00
Kendell R
a9e34d7590 Stop using ha-markdown on the auth page (#18480) 2023-11-01 15:36:17 +01:00
Paul Bottein
463cfb869f Add and and or condition to conditional card (#18409) 2023-11-01 15:28:37 +01:00
Paul Bottein
acb5a2b283 Add test button to conditional card (#18411) 2023-11-01 15:25:56 +01:00
Bram Kragten
08597d6e91 Bump node to v20 (#18497) 2023-11-01 15:22:23 +01:00
renovate[bot]
9478485268 Update Yarn to v4 (#18414)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-11-01 10:16:46 +00:00
renovate[bot]
0af9ec2fff Pin dependencies (#18498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-01 09:51:15 +00:00
Kendell R
f6e74de05e Properly set direction styles (#18483) 2023-11-01 10:38:02 +01:00
Bram Kragten
8b0373d5c0 Revert bump to @lit/context@1.0.1 (#18496) 2023-11-01 10:35:50 +01:00
Bram Kragten
ddca7584ef Bumped version to 20231030.1 2023-11-01 10:21:08 +01:00
karwosts
e6cfe74cac Fix choose add option (#18493) 2023-11-01 10:20:25 +01:00
karwosts
d4218250af Fix choose add option (#18493) 2023-11-01 09:37:26 +01:00
renovate[bot]
78783942be Update dependency @lit-labs/observers to v2.0.2 (#18484)
* Update dependency @lit-labs/observers to v2.0.2

* Resolve @lit/reactive-element to v1

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2023-10-31 14:42:28 -04:00
renovate[bot]
17ad8013ca Update dependency @lit-labs/context to v0.5.1 (#18208)
* Update dependency @lit-labs/context to v0.5.1

* Switch to @lit/context and resolve reactive-element to v1

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2023-10-31 13:53:53 -04:00
renovate[bot]
f70767f084 Update dependency @lit-labs/motion to v1.0.6 (#18205)
* Update dependency @lit-labs/motion to v1.0.6

* Resolve to lit v2

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2023-10-31 14:31:42 +00:00
renovate[bot]
3972394d82 Update dependency @lit-labs/virtualizer to v2.0.9 (#18206)
* Update dependency @lit-labs/virtualizer to v2.0.9

* Resolve to lit v2

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
2023-10-31 13:22:54 +00:00
renovate[bot]
5331fb4f5f Update dependency marked to v9.1.3 (#18485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-31 08:44:18 -04:00
Matthias de Baat
16df352ba8 Update ha-dialogs.markdown (#18487) 2023-10-31 12:27:46 +00:00
Bram Kragten
3b553a3a4b 20231030.0 (#18478) 2023-10-30 21:45:08 +01:00
Bram Kragten
8e1e75dee1 Bumped version to 20231030.0 2023-10-30 21:44:17 +01:00
Bram Kragten
d27b4e04a9 Adapt to new todo services (#18477) 2023-10-30 20:32:32 +00:00
Simon Lamon
36f7b34ac5 Use trigger and condition type translation labels (#18413)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-10-30 20:54:30 +01:00
renovate[bot]
c0ff24bf1b Update dependency ua-parser-js to v1.0.37 (#18473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-30 12:29:17 -04:00
Duco Sebel
abb5aa348f Allow individual item deletion in To-do list (#18464)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-10-30 14:21:29 +00:00
renovate[bot]
37ab5cbdc3 Update dependency fuse.js to v7 (#18462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2023-10-30 15:09:12 +01:00
Bram Kragten
42be3b331c Fix buttons (#18472) 2023-10-30 13:56:48 +01:00
Paul Bottein
71e05f79c2 Update ts-lit-plugin to 2.0.1 (#18471) 2023-10-30 13:40:07 +01:00
karwosts
c5a0a5bbbf Fix statistics bar chart time axis (#18450) 2023-10-30 13:39:15 +01:00
dependabot[bot]
c3d809fcf3 Bump actions/setup-node from 3.8.1 to 4.0.0 (#18468)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 11:18:14 +01:00
renovate[bot]
cbd0c39091 Update formatjs monorepo (#18423) 2023-10-29 22:05:19 -04:00
renovate[bot]
113eb5be24 Update dependency eslint-plugin-lit to v1.10.1 (#18460) 2023-10-28 21:45:57 -04:00
renovate[bot]
c497669fd3 Update dependency ts-lit-plugin to v2.0.0 (#18459) 2023-10-29 01:39:04 +00:00
renovate[bot]
c32ca5885b Update dependency lit-analyzer to v2.0.1 (#18458) 2023-10-28 21:28:01 -04:00
renovate[bot]
3c792c4019 Update dependency @bundle-stats/plugin-webpack-filter to v4.7.8 (#18457) 2023-10-28 21:23:43 -04:00
renovate[bot]
9762e61ee2 Update vaadinWebComponents monorepo to v24.2.1 (#18454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 20:36:52 +00:00
renovate[bot]
c09c39998b Update dependency vue to v2.7.15 (#18424)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 16:22:32 -04:00
renovate[bot]
d37e29c247 Update CodeMirror (#18453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 16:20:45 -04:00
renovate[bot]
51f22cd74a Update dependency eslint-import-resolver-webpack to v0.13.8 (#18422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 16:12:52 -04:00
renovate[bot]
b57dc968bd Update dependency eslint-plugin-import to v2.29.0 (#18425)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 16:07:23 -04:00
renovate[bot]
e2e8cb785a Update typescript-eslint monorepo to v6.9.0 (#18433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-28 16:06:02 -04:00
698 changed files with 22882 additions and 18136 deletions

View File

@@ -9,7 +9,7 @@ body:
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
**Please not not report issues for custom cards.**
**Please do not report issues for custom cards.**
[fr]: https://github.com/home-assistant/frontend/discussions
[releases]: https://github.com/home-assistant/home-assistant/releases

View File

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

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@v3.8.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -57,7 +57,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v3.8.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -75,7 +75,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v3.8.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -99,7 +99,7 @@ jobs:
- name: Check out files from GitHub
uses: actions/checkout@v4.1.1
- name: Setup Node
uses: actions/setup-node@v3.8.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Apply labels
uses: actions/labeler@v4.3.0
uses: actions/labeler@v5.0.0
with:
sync-labels: true

View File

@@ -9,9 +9,10 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4.0.1
- uses: dessant/lock-threads@v5.0.1
with:
github-token: ${{ github.token }}
process-only: "issues, prs"
issue-lock-inactive-days: "30"
issue-exclude-created-before: "2020-10-01T00:00:00Z"
issue-lock-reason: ""

View File

@@ -23,12 +23,12 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v3.8.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -29,12 +29,12 @@ jobs:
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v3.8.1
uses: actions/setup-node@v4.0.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@v8.0.0
uses: actions/stale@v9.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90

2
.nvmrc
View File

@@ -1 +1 @@
18
lts/iron

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

893
.yarn/releases/yarn-4.0.2.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,9 @@
compressionLevel: mixed
defaultSemverRangePrefix: ""
enableGlobalCache: false
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.6.4.cjs
yarnPath: .yarn/releases/yarn-4.0.2.cjs

View File

@@ -0,0 +1,56 @@
import defineProvider from "@babel/helper-define-polyfill-provider";
// List of polyfill keys with supported browser targets for the functionality
const PolyfillSupport = {
fetch: {
android: 42,
chrome: 42,
edge: 14,
firefox: 39,
ios: 10.3,
opera: 29,
opera_mobile: 29,
safari: 10.1,
samsung: 4.0,
},
proxy: {
android: 49,
chrome: 49,
edge: 12,
firefox: 18,
ios: 10.0,
opera: 36,
opera_mobile: 36,
safari: 10.0,
samsung: 5.0,
},
};
// Map of global variables and/or instance and static properties to the
// corresponding polyfill key and actual module to import
const polyfillMap = {
global: {
Proxy: { key: "proxy", module: "proxy-polyfill" },
fetch: { key: "fetch", module: "unfetch/polyfill" },
},
instance: {},
static: {},
};
// Create plugin using the same factory as for CoreJS
export default defineProvider(
({ createMetaResolver, debug, shouldInjectPolyfill }) => {
const resolvePolyfill = createMetaResolver(polyfillMap);
return {
name: "HA Custom",
polyfills: PolyfillSupport,
usageGlobal(meta, utils) {
const polyfill = resolvePolyfill(meta);
if (polyfill && shouldInjectPolyfill(polyfill.desc.key)) {
debug(polyfill.desc.key);
utils.injectGlobalImport(polyfill.desc.module);
}
},
};
}
);

View File

@@ -12,11 +12,7 @@ module.exports.sourceMapURL = () => {
};
// Files from NPM Packages that should not be imported
// eslint-disable-next-line unused-imports/no-unused-vars
module.exports.ignorePackages = ({ latestBuild }) => [
// Part of yaml.js and only used for !!js functions that we don't use
require.resolve("esprima"),
];
module.exports.ignorePackages = () => [];
// Files from NPM packages that we should replace with empty file
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
@@ -35,8 +31,6 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
require.resolve(
path.resolve(paths.polymer_dir, "src/resources/compatibility.ts")
),
// This polyfill is loaded in workers to support ES5, filter it out.
latestBuild && require.resolve("proxy-polyfill/src/index.js"),
// Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild &&
require.resolve(
@@ -91,14 +85,12 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
setSpreadProperties: true,
},
browserslistEnv: latestBuild ? "modern" : "legacy",
// Must be unambiguous because some dependencies are CommonJS only
sourceType: "unambiguous",
presets: [
[
"@babel/preset-env",
{
useBuiltIns: latestBuild ? false : "entry",
corejs: latestBuild ? false : { version: "3.33", proposals: true },
useBuiltIns: latestBuild ? false : "usage",
corejs: latestBuild ? false : "3.33",
bugfixes: true,
shippedProposals: true,
},
@@ -116,21 +108,33 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
ignoreModuleNotFound: true,
},
],
[
path.resolve(
paths.polymer_dir,
"build-scripts/babel-plugins/custom-polyfill-plugin.js"
),
{ method: "usage-global" },
],
// Minify template literals for production
isProdBuild && [
"template-html-minifier",
{
modules: {
lit: [
"html",
{ name: "svg", encapsulation: "svg" },
{ name: "css", encapsulation: "style" },
],
"@polymer/polymer/lib/utils/html-tag": ["html"],
...Object.fromEntries(
["lit", "lit-element", "lit-html"].map((m) => [
m,
[
"html",
{ name: "svg", encapsulation: "svg" },
{ name: "css", encapsulation: "style" },
],
])
),
"@polymer/polymer/lib/utils/html-tag.js": ["html"],
},
strictCSS: true,
htmlMinifier: module.exports.htmlMinifierOptions,
failOnError: true, // we can turn this off in case of false positives
failOnError: false, // we can turn this off in case of false positives
},
],
// Import helpers and regenerator from runtime package
@@ -147,6 +151,18 @@ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
/node_modules[\\/]webpack[\\/]buildin/,
],
sourceMaps: !isTestBuild,
overrides: [
{
// Use unambiguous for dependencies so that require() is correctly injected into CommonJS files
// Exclusions are needed in some cases where ES modules have no static imports or exports, such as polyfills
sourceType: "unambiguous",
include: /\/node_modules\//,
exclude: [
"element-internals-polyfill",
"@?lit(?:-labs|-element|-html)?",
].map((p) => new RegExp(`/node_modules/${p}/`)),
},
],
});
const nameSuffix = (latestBuild) => (latestBuild ? "-modern" : "-legacy");

View File

@@ -30,8 +30,8 @@ gulp.task(
env.useWDS()
? "wds-watch-app"
: env.useRollup()
? "rollup-watch-app"
: "webpack-watch-app"
? "rollup-watch-app"
: "webpack-watch-app"
)
);

View File

@@ -161,6 +161,10 @@ gulp.task("fetch-lokalise", async function () {
})
);
})
.catch((err) => {
console.error(err);
throw err;
})
)
);
});

View File

@@ -51,8 +51,8 @@ const createWebpackConfig = ({
devtool: isTestBuild
? false
: isProdBuild
? "nosources-source-map"
: "eval-cheap-module-source-map",
? "nosources-source-map"
: "eval-cheap-module-source-map",
entry,
node: false,
module: {
@@ -191,11 +191,11 @@ const createWebpackConfig = ({
filename: ({ chunk }) =>
!isProdBuild || isStatsBuild || dontHash.has(chunk.name)
? "[name].js"
: "[name]-[contenthash].js",
: "[name].[contenthash].js",
chunkFilename:
isProdBuild && !isStatsBuild ? "[id]-[contenthash].js" : "[name].js",
isProdBuild && !isStatsBuild ? "[name].[contenthash].js" : "[name].js",
assetModuleFilename:
isProdBuild && !isStatsBuild ? "[id]-[contenthash][ext]" : "[id][ext]",
isProdBuild && !isStatsBuild ? "[id].[contenthash][ext]" : "[id][ext]",
crossOriginLoading: "use-credentials",
hashFunction: "xxhash64",
hashDigest: "base64url",

View File

@@ -3,7 +3,7 @@ import { mdiCast, mdiCastConnected } from "@mdi/js";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { CastManager } from "../../../../src/cast/cast_manager";
import {
@@ -22,8 +22,9 @@ import "../../../../src/components/ha-svg-icon";
import {
getLegacyLovelaceCollection,
getLovelaceCollection,
LovelaceConfig,
} from "../../../../src/data/lovelace";
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout";
@@ -38,10 +39,10 @@ class HcCast extends LitElement {
@state() private askWrite = false;
@state() private lovelaceConfig?: LovelaceConfig | null;
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) {
if (this.lovelaceViews === undefined) {
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
}
@@ -72,43 +73,44 @@ class HcCast extends LitElement {
${error
? html` <div class="card-content">${error}</div> `
: !this.castManager.status
? html`
<p class="center-item">
<mwc-button raised @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
Start Casting
</mwc-button>
</p>
`
: html`
<div class="section-header">PICK A VIEW</div>
<paper-listbox
attr-for-selected="data-path"
.selected=${this.castManager.status.lovelacePath || ""}
>
${(this.lovelaceConfig
? this.lovelaceConfig.views
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
).map(
(view, idx) => html`
<paper-icon-item
@click=${this._handlePickView}
data-path=${view.path || idx}
>
${view.icon
? html`
<ha-icon
.icon=${view.icon}
slot="item-icon"
></ha-icon>
`
: ""}
${view.title || view.path}
</paper-icon-item>
`
)}
</paper-listbox>
`}
? html`
<p class="center-item">
<mwc-button raised @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
Start Casting
</mwc-button>
</p>
`
: html`
<div class="section-header">PICK A VIEW</div>
<paper-listbox
attr-for-selected="data-path"
.selected=${this.castManager.status.lovelacePath || ""}
>
${(
this.lovelaceViews ?? [
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
]
).map(
(view, idx) => html`
<paper-icon-item
@click=${this._handlePickView}
data-path=${view.path || idx}
>
${view.icon
? html`
<ha-icon
.icon=${view.icon}
slot="item-icon"
></ha-icon>
`
: ""}
${view.title || view.path}
</paper-icon-item>
`
)}
</paper-listbox>
`}
<div class="card-actions">
${this.castManager.status
? html`
@@ -136,11 +138,15 @@ class HcCast extends LitElement {
llColl.refresh().then(
() => {
llColl.subscribe((config) => {
this.lovelaceConfig = config;
if (isStrategyDashboard(config)) {
this.lovelaceViews = null;
} else {
this.lovelaceViews = config.views;
}
});
},
async () => {
this.lovelaceConfig = null;
this.lovelaceViews = null;
}
);
@@ -159,9 +165,7 @@ class HcCast extends LitElement {
toggleAttribute(
this,
"hide-icons",
this.lovelaceConfig
? !this.lovelaceConfig.views.some((view) => view.icon)
: true
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
);
}

View File

@@ -1,6 +1,5 @@
import "@material/mwc-button";
import { mdiCastConnected, mdiCast } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import {
Auth,
Connection,
@@ -24,6 +23,7 @@ import "../../../../src/components/ha-svg-icon";
import "../../../../src/layouts/hass-loading-screen";
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
import "./hc-layout";
import "../../../../src/components/ha-textfield";
const seeFAQ = (qid) => html`
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
@@ -33,13 +33,13 @@ const translateErr = (err) =>
err === ERR_CANNOT_CONNECT
? "Unable to connect"
: err === ERR_HASS_HOST_REQUIRED
? "Please enter a Home Assistant URL."
: err === ERR_INVALID_HTTPS_TO_HTTP
? html`
Cannot connect to Home Assistant instances over "http://".
${seeFAQ("https")}
`
: `Unknown error (${err}).`;
? "Please enter a Home Assistant URL."
: err === ERR_INVALID_HTTPS_TO_HTTP
? html`
Cannot connect to Home Assistant instances over "http://".
${seeFAQ("https")}
`
: `Unknown error (${err}).`;
const INTRO = html`
<p>
@@ -116,13 +116,11 @@ export class HcConnect extends LitElement {
To get started, enter your Home Assistant URL and click authorize.
If you want a preview instead, click the show demo button.
</p>
<p>
<paper-input
label="Home Assistant URL"
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
@keydown=${this._handleInputKeyDown}
></paper-input>
</p>
<ha-textfield
label="Home Assistant URL"
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
@keydown=${this._handleInputKeyDown}
></ha-textfield>
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
</div>
<div class="card-actions">
@@ -196,7 +194,7 @@ export class HcConnect extends LitElement {
}
private async _handleConnect() {
const inputEl = this.shadowRoot!.querySelector("paper-input")!;
const inputEl = this.shadowRoot!.querySelector("ha-textfield")!;
const value = inputEl.value || "";
this.error = undefined;
@@ -315,6 +313,10 @@ export class HcConnect extends LitElement {
.spacer {
flex: 1;
}
ha-textfield {
width: 100%;
}
`;
}
}

View File

@@ -1,7 +1,5 @@
import {
LovelaceCardConfig,
LovelaceConfig,
} from "../../../../src/data/lovelace";
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { castContext } from "../cast_context";
export const castDemoLovelace: () => LovelaceConfig = () => {

View File

@@ -1,7 +1,7 @@
import { html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mockHistory } from "../../../../demo/src/stubs/history";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import {
MockHomeAssistant,
provideHass,

View File

@@ -1,7 +1,7 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
import { HomeAssistant } from "../../../../src/types";
@@ -14,7 +14,8 @@ import "./hc-launch-screen";
class HcLovelace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
@property({ attribute: false })
public lovelaceConfig!: LovelaceConfig;
@property() public viewPath?: string | number;

View File

@@ -21,18 +21,26 @@ import {
import { atLeastVersion } from "../../../../src/common/config/version";
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
import {
fetchResources,
getLegacyLovelaceCollection,
getLovelaceCollection,
} from "../../../../src/data/lovelace";
import {
isStrategyDashboard,
LegacyLovelaceConfig,
LovelaceConfig,
} from "../../../../src/data/lovelace";
LovelaceDashboardStrategyConfig,
} from "../../../../src/data/lovelace/config/types";
import { fetchResources } from "../../../../src/data/lovelace/resource";
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
import { HassElement } from "../../../../src/state/hass-element";
import { castContext } from "../cast_context";
import "./hc-launch-screen";
const DEFAULT_STRATEGY = "original-states";
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
strategy: {
type: "original-states",
},
};
let resourcesLoaded = false;
@customElement("hc-main")
@@ -93,7 +101,7 @@ export class HcMain extends HassElement {
.lovelaceConfig=${this._lovelaceConfig}
.viewPath=${this._lovelacePath}
.urlPath=${this._urlPath}
@config-refresh=${this._generateLovelaceConfig}
@config-refresh=${this._generateDefaultLovelaceConfig}
></hc-lovelace>
`;
}
@@ -284,9 +292,20 @@ export class HcMain extends HassElement {
// configuration.
try {
await llColl.refresh();
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
this._handleNewLovelaceConfig(lovelaceConfig)
);
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
if (isStrategyDashboard(rawConfig)) {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
);
const config = await generateLovelaceDashboardStrategy(
rawConfig.strategy,
this.hass!
);
this._handleNewLovelaceConfig(config);
} else {
this._handleNewLovelaceConfig(rawConfig);
}
});
} catch (err: any) {
if (
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
@@ -300,7 +319,7 @@ export class HcMain extends HassElement {
}
// Generate a Lovelace config.
this._unsubLovelace = () => undefined;
await this._generateLovelaceConfig();
await this._generateDefaultLovelaceConfig();
}
}
if (!resourcesLoaded) {
@@ -316,15 +335,13 @@ export class HcMain extends HassElement {
this._sendStatus();
}
private async _generateLovelaceConfig() {
private async _generateDefaultLovelaceConfig() {
const { generateLovelaceDashboardStrategy } = await import(
"../../../../src/panels/lovelace/strategies/get-strategy"
);
this._handleNewLovelaceConfig(
await generateLovelaceDashboardStrategy(
{
type: DEFAULT_STRATEGY,
},
DEFAULT_CONFIG.strategy,
this.hass!
)
);

View File

@@ -1,5 +1,5 @@
import { LocalizeFunc } from "../../../src/common/translations/localize";
import { LovelaceConfig } from "../../../src/data/lovelace";
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
import { Entity } from "../../../src/fake_data/entity";
export interface DemoConfig {

View File

@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import "../../../src/components/ha-card";
import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace";
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
import {
@@ -39,7 +39,9 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<div class="picker">
<div class="label">
${this._switching
? html`<ha-circular-progress active></ha-circular-progress>`
? html`<ha-circular-progress
indeterminate
></ha-circular-progress>`
: until(
selectedDemoConfig.then(
(conf) => html`
@@ -48,8 +50,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
<a target="_blank" href=${conf.authorUrl}>
${this.hass.localize(
"ui.panel.page-demo.cards.demo.demo_by",
"name",
conf.authorName
{ name: conf.authorName }
)}
</a>
</small>

View File

@@ -74,19 +74,9 @@
<body>
<div id="ha-launch-screen">
<div class="ha-launch-screen-spacer"></div>
<svg
viewBox="0 0 240 240"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
fill="#F2F4F9"
/>
<path
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
fill="#18BCF2"
/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
</svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
</div>

View File

@@ -43,8 +43,8 @@ const generateMeanStatistics = (
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};
@@ -80,8 +80,8 @@ const generateSumStatistics = (
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};

View File

@@ -23,7 +23,7 @@ class DemoMoreInfo extends LitElement {
<state-card-content
.stateObj=${state}
.hass=${this.hass}
in-dialog
inDialog
></state-card-content>
<more-info-content

View File

@@ -1,8 +1,10 @@
import { css, html, LitElement, TemplateResult } from "lit";
import { css, html, LitElement, TemplateResult, nothing } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-chip";
import "../../../../src/components/ha-chip-set";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/chips/ha-input-chip";
import "../../../../src/components/chips/ha-filter-chip";
import "../../../../src/components/ha-svg-icon";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
@@ -10,10 +12,6 @@ const chips: {
icon?: string;
content?: string;
}[] = [
{},
{
icon: mdiHomeAssistant,
},
{
content: "Content",
},
@@ -29,31 +27,73 @@ export class DemoHaChips extends LitElement {
return html`
<ha-card header="ha-chip demo">
<div class="card-content">
${chips.map(
(chip) => html`
<ha-chip .hasIcon=${chip.icon !== undefined}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-chip>
`
)}
</div>
</ha-card>
<ha-card header="ha-chip-set demo">
<div class="card-content">
<p>Action chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-chip .hasIcon=${chip.icon !== undefined}>
<ha-assist-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-assist-chip>
`
)}
${chips.map(
(chip) => html`
<ha-assist-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-assist-chip>
`
)}
</ha-chip-set>
<p>Filter chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-filter-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-filter-chip>
`
)}
${chips.map(
(chip) => html`
<ha-filter-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-filter-chip>
`
)}
</ha-chip-set>
<p>Input chip</p>
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-input-chip .label=${chip.content}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-chip>
</ha-input-chip>
`
)}
${chips.map(
(chip) => html`
<ha-input-chip .label=${chip.content} selected>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: nothing}
</ha-input-chip>
`
)}
</ha-chip-set>
@@ -68,12 +108,10 @@ export class DemoHaChips extends LitElement {
max-width: 600px;
margin: 24px auto;
}
ha-chip {
margin-bottom: 4px;
}
.card-content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
`;
}

View File

@@ -0,0 +1,4 @@
---
title: Circular Progress
subtitle: Can be used to indicate an ongoing task.
---

View File

@@ -0,0 +1,64 @@
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-bar";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-circular-progress";
import "@material/web/progress/circular-progress";
import { HomeAssistant } from "../../../../src/types";
@customElement("demo-components-ha-circular-progress")
export class DemoHaCircularProgress extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
protected render(): TemplateResult {
return html`<ha-card header="Basic circular progress">
<div class="card-content">
<ha-circular-progress indeterminate></ha-circular-progress></div
></ha-card>
<ha-card header="Different circular progress sizes">
<div class="card-content">
<ha-circular-progress
indeterminate
size="tiny"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="small"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="medium"
></ha-circular-progress>
<ha-circular-progress
indeterminate
size="large"
></ha-circular-progress></div
></ha-card>
<ha-card header="Circular progress with an aria-label">
<div class="card-content">
<ha-circular-progress
indeterminate
aria-label="Doing something..."
></ha-circular-progress>
<ha-circular-progress
indeterminate
.ariaLabel=${"Doing something..."}
></ha-circular-progress></div
></ha-card>`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-circular-progress": DemoHaCircularProgress;
}
}

View File

@@ -11,6 +11,7 @@ const buttons: {
min?: number;
max?: number;
step?: number;
unit?: string;
class?: string;
}[] = [
{
@@ -29,6 +30,11 @@ const buttons: {
label: "Custom",
class: "custom",
},
{
id: "unit",
label: "With unit",
unit: "m",
},
];
@customElement("demo-components-ha-control-number-buttons")
@@ -50,6 +56,7 @@ export class DemoHarControlNumberButtons extends LitElement {
<pre>Config: ${JSON.stringify(config)}</pre>
<ha-control-number-buttons
.value=${this.value}
.unit=${config.unit}
.min=${config.min}
.max=${config.max}
.step=${config.step}

View File

@@ -9,6 +9,7 @@ const sliders: {
id: string;
label: string;
mode?: "start" | "end" | "cursor";
unit?: string;
class?: string;
}[] = [
{
@@ -31,18 +32,21 @@ const sliders: {
label: "Slider (start mode) and custom style",
mode: "start",
class: "custom",
unit: "mm",
},
{
id: "slider-end-custom",
label: "Slider (end mode) and custom style",
mode: "end",
class: "custom",
unit: "mm",
},
{
id: "slider-cursor-custom",
label: "Slider (cursor mode) and custom style",
mode: "cursor",
class: "custom",
unit: "mm",
},
];
@@ -93,6 +97,7 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-labelledby=${id}
.unit=${config.unit}
>
</ha-control-slider>
</div>
@@ -114,6 +119,7 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-label=${label}
.unit=${config.unit}
>
</ha-control-slider>
`;

View File

@@ -5,9 +5,22 @@ subtitle: Dialogs provide important prompts in a user flow.
# Material Design 3
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
# Highlighted guidelines
# Guidelines
## Design
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
- Destructive actions should be a red warning button.
- Alert or confirmation dialogs only have buttons and no X-icon.
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
## Content
@@ -17,14 +30,6 @@ Our dialogs are based on the latest version of Material Design. Specs and guidel
- If users become unsure, they read the description. Make sure this explains what will happen.
- Strive for minimalism.
## Buttons and X-icon
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
- Destructive actions should be a red warning button.
- Alert or confirmation dialogs only have buttons and no X-icon.
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
## Example
### Confirmation dialog

View File

@@ -18,7 +18,7 @@ The Home Assistant interface is based on Material Design. It's a design system c
We want to make it as easy for designers to contribute as it is for developers. Theres a lot a designer can contribute to:
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
- Meet us at <a href="https://www.home-assistant.io/join-chat" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
- Find the latest UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!

View File

@@ -10,7 +10,6 @@ import { computeStateDisplay } from "../../../../src/common/entity/compute_state
import "../../../../src/components/data-table/ha-data-table";
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
import "../../../../src/components/entity/state-badge";
import "../../../../src/components/ha-chip";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";

View File

@@ -2,7 +2,7 @@ import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-misc-util-long-press")

View File

@@ -0,0 +1,3 @@
---
title: Input Text
---

View File

@@ -0,0 +1,46 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("input_text", "text", "Inspiration", {
friendly_name: "Text",
mode: "text",
}),
];
@customElement("demo-more-info-input-text")
class DemoMoreInfoInputText extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-input-text": DemoMoreInfoInputText;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Lock
---

View File

@@ -0,0 +1,49 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("lock", "lock", "locked", {
friendly_name: "Lock",
device_class: "lock",
}),
getEntity("lock", "unavailable", "unavailable", {
friendly_name: "Unavailable lock",
}),
];
@customElement("demo-more-info-lock")
class DemoMoreInfoLock extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-lock": DemoMoreInfoLock;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Media Player
---

View File

@@ -0,0 +1,41 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { createMediaPlayerEntities } from "../../data/media_players";
const ENTITIES = createMediaPlayerEntities();
@customElement("demo-more-info-media-player")
class DemoMoreInfoMediaPlayer extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-media-player": DemoMoreInfoMediaPlayer;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Number
---

View File

@@ -0,0 +1,78 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("number", "box1", 0, {
friendly_name: "Box1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "box",
unit_of_measurement: "items",
}),
getEntity("number", "slider1", 0, {
friendly_name: "Slider1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "slider",
unit_of_measurement: "items",
}),
getEntity("number", "auto1", 0, {
friendly_name: "Auto1",
min: 0,
max: 1000,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
}),
getEntity("number", "auto2", 0, {
friendly_name: "Auto2",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
}),
];
@customElement("demo-more-info-number")
class DemoMoreInfoNumber extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-number": DemoMoreInfoNumber;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Scene
---

View File

@@ -0,0 +1,49 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("scene", "romantic_lights", "scening", {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene",
}),
getEntity("scene", "unavailable", "unavailable", {
friendly_name: "Romantic Scene",
}),
];
@customElement("demo-more-info-scene")
class DemoMoreInfoScene extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-scene": DemoMoreInfoScene;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Timer
---

View File

@@ -0,0 +1,46 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
const ENTITIES = [
getEntity("timer", "timer", "idle", {
friendly_name: "Timer",
duration: "0:05:00",
}),
];
@customElement("demo-more-info-timer")
class DemoMoreInfoTimer extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-timer": DemoMoreInfoTimer;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Vacuum
---

View File

@@ -0,0 +1,50 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
const ENTITIES = [
getEntity("vacuum", "first_floor_vacuum", "docked", {
friendly_name: "First floor vacuum",
supported_features:
VacuumEntityFeature.START +
VacuumEntityFeature.STOP +
VacuumEntityFeature.RETURN_HOME,
}),
];
@customElement("demo-more-info-vacuum")
class DemoMoreInfoVacuum extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-vacuum": DemoMoreInfoVacuum;
}
}

View File

@@ -49,11 +49,9 @@ export class HassioAddonRepositoryEl extends LitElement {
return html`
<div class="content">
<p class="description">
${this.supervisor.localize(
"store.no_results_found",
"repository",
repo.name
)}
${this.supervisor.localize("store.no_results_found", {
repository: repo.name,
})}
</p>
</div>
`;
@@ -86,15 +84,15 @@ export class HassioAddonRepositoryEl extends LitElement {
)
: this.supervisor.localize("addon.state.installed")
: addon.available
? this.supervisor.localize("addon.state.not_installed")
: this.supervisor.localize("addon.state.not_available")}
? this.supervisor.localize("addon.state.not_installed")
: this.supervisor.localize("addon.state.not_available")}
.iconClass=${addon.installed
? addon.update_available
? "update"
: "installed"
: !addon.available
? "not_available"
: ""}
? "not_available"
: ""}
.iconImage=${atLeastVersion(
this.hass.config.version,
0,
@@ -108,8 +106,8 @@ export class HassioAddonRepositoryEl extends LitElement {
? "update"
: "installed"
: !addon.available
? "unavailable"
: ""}
? "unavailable"
: ""}
></hassio-card-content>
</div>
</ha-card>

View File

@@ -20,7 +20,7 @@ class HassioAddonConfigDashboard extends LitElement {
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`;
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
}
const hasConfiguration =
(this.addon.options && Object.keys(this.addon.options).length) ||

View File

@@ -104,50 +104,50 @@ class HassioAddonConfig extends LitElement {
selector: { select: { options: entry.options } },
}
: entry.type === "string"
? entry.multiple
? {
name: entry.name,
required: entry.required,
selector: {
select: { options: [], multiple: true, custom_value: true },
},
}
: {
name: entry.name,
required: entry.required,
selector: {
text: {
type:
entry.format || MASKED_FIELDS.includes(entry.name)
? "password"
: "text",
? entry.multiple
? {
name: entry.name,
required: entry.required,
selector: {
select: { options: [], multiple: true, custom_value: true },
},
},
}
: entry.type === "boolean"
? {
name: entry.name,
required: entry.required,
selector: { boolean: {} },
}
: entry.type === "schema"
? {
name: entry.name,
required: entry.required,
selector: { object: {} },
}
: entry.type === "float" || entry.type === "integer"
? {
name: entry.name,
required: entry.required,
selector: {
number: {
mode: "box",
step: entry.type === "float" ? "any" : undefined,
},
},
}
: entry
}
: {
name: entry.name,
required: entry.required,
selector: {
text: {
type:
entry.format || MASKED_FIELDS.includes(entry.name)
? "password"
: "text",
},
},
}
: entry.type === "boolean"
? {
name: entry.name,
required: entry.required,
selector: { boolean: {} },
}
: entry.type === "schema"
? {
name: entry.name,
required: entry.required,
selector: { object: {} },
}
: entry.type === "float" || entry.type === "integer"
? {
name: entry.name,
required: entry.required,
selector: {
number: {
mode: "box",
step: entry.type === "float" ? "any" : undefined,
},
},
}
: entry
)
);
@@ -340,11 +340,9 @@ class HassioAddonConfig extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_reset",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_reset", {
error: extractApiErrorMessage(err),
});
}
button.progress = false;
}
@@ -381,11 +379,9 @@ class HassioAddonConfig extends LitElement {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
eventdata.success = false;
}
button.progress = false;

View File

@@ -180,11 +180,9 @@ class HassioAddonNetwork extends LitElement {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_reset",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_reset", {
error: extractApiErrorMessage(err),
});
button.actionError();
}
}
@@ -220,11 +218,9 @@ class HassioAddonNetwork extends LitElement {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
button.actionError();
}
}

View File

@@ -34,7 +34,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`;
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
}
return html`
<div class="content">
@@ -85,8 +85,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.documentation.get_documentation",
"error",
extractApiErrorMessage(err)
{ error: extractApiErrorMessage(err) }
);
}
}

View File

@@ -22,7 +22,7 @@ class HassioAddonInfoDashboard extends LitElement {
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`;
return html`<ha-circular-progress indeterminate></ha-circular-progress>`;
}
return html`

View File

@@ -31,8 +31,8 @@ import { navigate } from "../../../../src/common/navigate";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-chip";
import "../../../../src/components/ha-chip-set";
import "../../../../src/components/chips/ha-chip-set";
import "../../../../src/components/chips/ha-assist-chip";
import "../../../../src/components/ha-markdown";
import "../../../../src/components/ha-settings-row";
import "../../../../src/components/ha-svg-icon";
@@ -78,6 +78,7 @@ import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-has
import { hassioStyle } from "../../resources/hassio-style";
import "../../update-available/update-available-card";
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
const STAGE_ICON = {
stable: mdiCheckCircle,
@@ -234,28 +235,32 @@ class HassioAddonInfo extends LitElement {
<ha-chip-set class="capabilities">
${this.addon.stage !== "stable"
? html` <ha-chip
hasIcon
class=${classMap({
yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated",
})}
@click=${this._showMoreInfo}
id="stage"
>
<ha-svg-icon
slot="icon"
.path=${STAGE_ICON[this.addon.stage]}
? html`
<ha-assist-chip
filled
class=${classMap({
yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated",
})}
@click=${this._showMoreInfo}
id="stage"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
`addon.dashboard.capability.stages.${this.addon.stage}`
)
)}
>
</ha-svg-icon>
${this.supervisor.localize(
`addon.dashboard.capability.stages.${this.addon.stage}`
)}
</ha-chip>`
<ha-svg-icon
slot="icon"
.path=${STAGE_ICON[this.addon.stage]}
>
</ha-svg-icon>
</ha-assist-chip>
`
: ""}
<ha-chip
hasIcon
<ha-assist-chip
filled
class=${classMap({
green: Number(this.addon.rating) >= 6,
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
@@ -263,151 +268,197 @@ class HassioAddonInfo extends LitElement {
})}
@click=${this._showMoreInfo}
id="rating"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.rating"
)
)}
>
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
</ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.rating"
)}
</ha-chip>
</ha-assist-chip>
${this.addon.host_network
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="host_network"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.host"
)
)}
>
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.host"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.full_access
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="full_access"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.hardware"
)
)}
>
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.hardware"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.homeassistant_api
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="homeassistant_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.core"
)
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiHomeAssistant}
></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.core"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this._computeHassioApi
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="hassio_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
`addon.dashboard.capability.role.${this.addon.hassio_role}`
) || this.addon.hassio_role
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiHomeAssistant}
></ha-svg-icon>
${this.supervisor.localize(
`addon.dashboard.capability.role.${this.addon.hassio_role}`
) || this.addon.hassio_role}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.docker_api
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.docker"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="docker_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.docker"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
${this.addon.host_pid
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.host_pid"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="host_pid"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.host_pid"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
${this.addon.apparmor !== "default"
? html`
<ha-chip
hasIcon
<ha-assist-chip
filled
@click=${this._showMoreInfo}
class=${this._computeApparmorClassName}
id="apparmor"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.apparmor"
)
)}
>
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.apparmor"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.auth_api
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.auth"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="auth_api"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.auth"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
${this.addon.ingress
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="ingress"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.ingress"
)
)}
>
<ha-svg-icon
slot="icon"
.path=${mdiCursorDefaultClickOutline}
></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.ingress"
)}
</ha-chip>
</ha-assist-chip>
`
: ""}
${this.addon.signed
? html`
<ha-chip hasIcon @click=${this._showMoreInfo} id="signed">
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
${this.supervisor.localize(
"addon.dashboard.capability.label.signed"
<ha-assist-chip
filled
@click=${this._showMoreInfo}
id="signed"
.label=${capitalizeFirstLetter(
this.supervisor.localize(
"addon.dashboard.capability.label.signed"
)
)}
</ha-chip>
>
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
</ha-assist-chip>
`
: ""}
</ha-chip-set>
<div class="description light-color">
${this.addon.description}.<br />
${this.supervisor.localize(
"addon.dashboard.visit_addon_page",
"name",
html`<a href=${this.addon.url!} target="_blank" rel="noreferrer"
${this.supervisor.localize("addon.dashboard.visit_addon_page", {
name: html`<a
href=${this.addon.url!}
target="_blank"
rel="noreferrer"
>${this.addon.name}</a
>`
)}
>`,
})}
</div>
<div class="addon-container">
<div>
@@ -574,10 +625,10 @@ class HassioAddonInfo extends LitElement {
<ha-alert alert-type="warning">
${this.supervisor.localize(
"addon.dashboard.not_available_version",
"core_version_installed",
this.supervisor.core.version,
"core_version_needed",
addonStoreInfo!.homeassistant
{
core_version_installed: this.supervisor.core.version,
core_version_needed: addonStoreInfo!.homeassistant,
}
)}
</ha-alert>
`
@@ -750,12 +801,11 @@ class HassioAddonInfo extends LitElement {
id === "stage"
? this.supervisor.localize(
`addon.dashboard.capability.${id}.description`,
"icon_stable",
`<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon>`,
"icon_experimental",
`<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon>`,
"icon_deprecated",
`<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon>`
{
icon_stable: `<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon>`,
icon_experimental: `<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon>`,
icon_deprecated: `<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon>`,
}
)
: this.supervisor.localize(
`addon.dashboard.capability.${id}.description`
@@ -817,11 +867,9 @@ class HassioAddonInfo extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
}
}
@@ -839,11 +887,9 @@ class HassioAddonInfo extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
}
}
@@ -861,11 +907,9 @@ class HassioAddonInfo extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
}
}
@@ -883,11 +927,9 @@ class HassioAddonInfo extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
}
}
@@ -905,11 +947,9 @@ class HassioAddonInfo extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.failed_to_save", {
error: extractApiErrorMessage(err),
});
}
}
@@ -1185,23 +1225,35 @@ class HassioAddonInfo extends LitElement {
.description a {
color: var(--primary-color);
}
ha-chip {
text-transform: capitalize;
--ha-chip-text-color: var(--text-primary-color);
--ha-chip-background-color: var(--primary-color);
ha-assist-chip {
--md-sys-color-primary: var(--text-primary-color);
--md-sys-color-on-surface: var(--text-primary-color);
--ha-assist-chip-filled-container-color: var(--primary-color);
}
.red {
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
--ha-assist-chip-filled-container-color: var(
--label-badge-red,
#df4c1e
);
}
.blue {
--ha-chip-background-color: var(--label-badge-blue, #039be5);
--ha-assist-chip-filled-container-color: var(
--label-badge-blue,
#039be5
);
}
.green {
--ha-chip-background-color: var(--label-badge-green, #0da035);
--ha-assist-chip-filled-container-color: var(
--label-badge-green,
#0da035
);
}
.yellow {
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
--ha-assist-chip-filled-container-color: var(
--label-badge-yellow,
#f4b400
);
}
.capabilities {
margin-bottom: 16px;
@@ -1260,9 +1312,6 @@ class HassioAddonInfo extends LitElement {
}
@media (max-width: 720px) {
ha-chip {
line-height: 36px;
}
.addon-options {
max-width: 100%;
}

View File

@@ -18,7 +18,9 @@ class HassioAddonLogDashboard extends LitElement {
protected render(): TemplateResult {
if (!this.addon) {
return html` <ha-circular-progress active></ha-circular-progress> `;
return html`
<ha-circular-progress indeterminate></ha-circular-progress>
`;
}
return html`
<div class="content">

View File

@@ -72,11 +72,9 @@ class HassioAddonLogs extends LitElement {
try {
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.logs.get_logs",
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("addon.logs.get_logs", {
error: extractApiErrorMessage(err),
});
}
}

View File

@@ -1,11 +1,12 @@
import Fuse from "fuse.js";
import type { IFuseOptions } from "fuse.js";
import { StoreAddon } from "../../../src/data/supervisor/store";
export function filterAndSort(addons: StoreAddon[], filter: string) {
const options: Fuse.IFuseOptions<StoreAddon> = {
const options: IFuseOptions<StoreAddon> = {
keys: ["name", "description", "slug"],
isCaseSensitive: false,
minMatchCharLength: 2,
minMatchCharLength: Math.min(filter.length, 2),
threshold: 0.2,
};
const fuse = new Fuse(addons, options);

View File

@@ -17,8 +17,11 @@ class SupervisorFormfieldLabel extends LitElement {
${this.imageUrl
? html`<img loading="lazy" alt="" src=${this.imageUrl} class="icon" />`
: this.iconPath
? html`<ha-svg-icon .path=${this.iconPath} class="icon"></ha-svg-icon>`
: ""}
? html`<ha-svg-icon
.path=${this.iconPath}
class="icon"
></ha-svg-icon>`
: ""}
<span class="label">${this.label}</span>
${this.version
? html`<span class="version">(${this.version})</span>`

View File

@@ -68,17 +68,19 @@ class HassioAddons extends LitElement {
.iconTitle=${addon.state !== "started"
? this.supervisor.localize("dashboard.addon_stopped")
: addon.update_available!
? this.supervisor.localize(
"dashboard.addon_new_version"
)
: this.supervisor.localize("dashboard.addon_running")}
? this.supervisor.localize(
"dashboard.addon_new_version"
)
: this.supervisor.localize(
"dashboard.addon_running"
)}
.iconClass=${addon.update_available
? addon.state === "started"
? "update"
: "update stopped"
: addon.state === "started"
? "running"
: "stopped"}
? "running"
: "stopped"}
.iconImage=${atLeastVersion(
this.hass.config.version,
0,

View File

@@ -46,11 +46,9 @@ export class HassioUpdate extends LitElement {
return html`
<div class="content">
<h1>
${this.supervisor.localize(
"common.update_available",
"count",
updatesAvailable
)}
${this.supervisor.localize("common.update_available", {
count: updatesAvailable,
})}
🎉
</h1>
<div class="card-group">

View File

@@ -95,7 +95,7 @@ class HassioBackupDialog
</ha-header-bar>
</div>
${this._restoringBackup
? html`<ha-circular-progress active></ha-circular-progress>`
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`
<supervisor-backup-content
.hass=${this.hass}

View File

@@ -57,7 +57,7 @@ class HassioCreateBackupDialog extends LitElement {
)}
>
${this._creatingBackup
? html`<ha-circular-progress active></ha-circular-progress>`
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`<supervisor-backup-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}

View File

@@ -71,7 +71,11 @@ class HassioDatadiskDialog extends LitElement {
?hideActions=${this.moving}
>
${this.moving
? html` <ha-circular-progress alt="Moving" size="large" active>
? html` <ha-circular-progress
aria-label="Moving"
size="large"
indeterminate
>
</ha-circular-progress>
<p class="progress-text">
${this.dialogParams.supervisor.localize(
@@ -105,12 +109,12 @@ class HassioDatadiskDialog extends LitElement {
</ha-select>
`
: this.devices === undefined
? this.dialogParams.supervisor.localize(
"dialog.datadisk_move.loading_devices"
)
: this.dialogParams.supervisor.localize(
"dialog.datadisk_move.no_devices"
)}
? this.dialogParams.supervisor.localize(
"dialog.datadisk_move.loading_devices"
)
: this.dialogParams.supervisor.localize(
"dialog.datadisk_move.no_devices"
)}
<mwc-button
slot="secondaryAction"

View File

@@ -145,8 +145,7 @@ export class DialogHassioNetwork
? html`<p>
${this.supervisor.localize(
"dialog.network.connected_to",
"ssid",
this._interface?.wifi?.ssid
{ ssid: this._interface?.wifi?.ssid }
)}
</p>`
: ""}
@@ -156,7 +155,11 @@ export class DialogHassioNetwork
.disabled=${this._scanning}
>
${this._scanning
? html`<ha-circular-progress active size="small">
? html`<ha-circular-progress
aria-label="Scanning"
indeterminate
size="small"
>
</ha-circular-progress>`
: this.supervisor.localize("dialog.network.scan_ap")}
</mwc-button>
@@ -275,7 +278,7 @@ export class DialogHassioNetwork
</mwc-button>
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
${this._processing
? html`<ha-circular-progress active size="small">
? html`<ha-circular-progress indeterminate size="small">
</ha-circular-progress>`
: this.supervisor.localize("common.save")}
</mwc-button>

View File

@@ -158,7 +158,7 @@ class HassioRepositoriesDialog extends LitElement {
<mwc-button @click=${this._addRepository}>
${this._processing
? html`<ha-circular-progress
active
indeterminate
size="small"
></ha-circular-progress>`
: this._dialogParams!.supervisor.localize(

View File

@@ -76,17 +76,15 @@ class HassioMyRedirect extends LitElement {
const redirect = REDIRECTS[path];
if (!redirect) {
this._error = this.supervisor.localize(
"my.not_supported",
"link",
html`<a
this._error = this.supervisor.localize("my.not_supported", {
link: html`<a
target="_blank"
rel="noreferrer noopener"
href="https://my.home-assistant.io/faq.html#supported-pages"
>
${this.supervisor.localize("my.faq_link")}
</a>`
);
</a>`,
});
return;
}

View File

@@ -67,8 +67,8 @@ class HassioRouter extends HassRouterPage {
const route = hassioPanel
? this.route
: ingressPanel && this.panel.config?.ingress
? this._ingressRoute(this.panel.config?.ingress)
: this.routeTail;
? this._ingressRoute(this.panel.config?.ingress)
: this.routeTail;
el.hass = this.hass;
el.narrow = this.narrow;

View File

@@ -96,13 +96,11 @@ class HassioCoreInfo extends LitElement {
slot="primaryAction"
class="warning"
@click=${this._coreRestart}
.title=${this.supervisor.localize(
"common.restart_name",
"name",
"Core"
)}
.title=${this.supervisor.localize("common.restart_name", {
name: "Core",
})}
>
${this.supervisor.localize("common.restart_name", "name", "Core")}
${this.supervisor.localize("common.restart_name", { name: "Core" })}
</ha-progress-button>
</div>
</ha-card>
@@ -122,16 +120,12 @@ class HassioCoreInfo extends LitElement {
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: this.supervisor.localize(
"confirm.restart.title",
"name",
"Home Assistant Core"
),
text: this.supervisor.localize(
"confirm.restart.text",
"name",
"Home Assistant Core"
),
title: this.supervisor.localize("confirm.restart.title", {
name: "Home Assistant Core",
}),
text: this.supervisor.localize("confirm.restart.text", {
name: "Home Assistant Core",
}),
confirmText: this.supervisor.localize("common.restart"),
dismissText: this.supervisor.localize("common.cancel"),
});
@@ -146,11 +140,9 @@ class HassioCoreInfo extends LitElement {
} catch (err: any) {
if (this.hass.connection.connected) {
showAlertDialog(this, {
title: this.supervisor.localize(
"common.failed_to_restart_name",
"name",
"Home AssistantCore"
),
title: this.supervisor.localize("common.failed_to_restart_name", {
name: "Home Assistant Core",
}),
text: extractApiErrorMessage(err),
});
}

View File

@@ -109,19 +109,19 @@ class HassioSupervisorInfo extends LitElement {
</ha-progress-button>
`
: this.supervisor.supervisor.channel === "stable"
? html`
<ha-progress-button
@click=${this._toggleBeta}
.title=${this.supervisor.localize(
"system.supervisor.join_beta_description"
)}
>
${this.supervisor.localize(
"system.supervisor.join_beta_action"
)}
</ha-progress-button>
`
: ""}
? html`
<ha-progress-button
@click=${this._toggleBeta}
.title=${this.supervisor.localize(
"system.supervisor.join_beta_description"
)}
>
${this.supervisor.localize(
"system.supervisor.join_beta_action"
)}
</ha-progress-button>
`
: ""}
</ha-settings-row>
${this.supervisor.supervisor.supported
@@ -200,17 +200,13 @@ class HassioSupervisorInfo extends LitElement {
<ha-progress-button
class="warning"
@click=${this._supervisorRestart}
.title=${this.supervisor.localize(
"common.restart_name",
"name",
"Supervisor"
)}
.title=${this.supervisor.localize("common.restart_name", {
name: "Supervisor",
})}
>
${this.supervisor.localize(
"common.restart_name",
"name",
"Supervisor"
)}
${this.supervisor.localize("common.restart_name", {
name: "Supervisor",
})}
</ha-progress-button>
</div>
</ha-card>
@@ -292,16 +288,12 @@ class HassioSupervisorInfo extends LitElement {
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: this.supervisor.localize(
"confirm.restart.title",
"name",
"Supervisor"
),
text: this.supervisor.localize(
"confirm.restart.text",
"name",
"Supervisor"
),
title: this.supervisor.localize("confirm.restart.title", {
name: "Supervisor",
}),
text: this.supervisor.localize("confirm.restart.text", {
name: "Supervisor",
}),
confirmText: this.supervisor.localize("common.restart"),
dismissText: this.supervisor.localize("common.cancel"),
});
@@ -315,11 +307,9 @@ class HassioSupervisorInfo extends LitElement {
await restartSupervisor(this.hass);
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"common.failed_to_restart_name",
"name",
"Supervisor"
),
title: this.supervisor.localize("common.failed_to_restart_name", {
name: "Supervisor",
}),
text: extractApiErrorMessage(err),
});
} finally {
@@ -334,8 +324,7 @@ class HassioSupervisorInfo extends LitElement {
),
text: this.supervisor.localize(
"system.supervisor.share_diagonstics_description",
"line_break",
html`<br /><br />`
{ line_break: html`<br /><br />` }
),
});
}

View File

@@ -124,13 +124,10 @@ class HassioSupervisorLog extends LitElement {
this._selectedLogProvider
);
} catch (err: any) {
this._error = this.supervisor.localize(
"system.log.get_logs",
"provider",
this._selectedLogProvider,
"error",
extractApiErrorMessage(err)
);
this._error = this.supervisor.localize("system.log.get_logs", {
provider: this._selectedLogProvider,
error: extractApiErrorMessage(err),
});
}
}

View File

@@ -70,8 +70,8 @@ const changelogUrl = (
return version.includes("dev")
? "https://github.com/home-assistant/core/commits/dev"
: version.includes("b")
? "https://next.home-assistant.io/latest-release-notes/"
: "https://www.home-assistant.io/latest-release-notes/";
? "https://next.home-assistant.io/latest-release-notes/"
: "https://www.home-assistant.io/latest-release-notes/";
}
if (entry === "os") {
return version.includes("dev")
@@ -141,44 +141,51 @@ class UpdateAvailableCard extends LitElement {
})}
</p>`
: !this._updating
? html`
${this._changelogContent
? html`
<ha-faded>
<ha-markdown .content=${this._changelogContent}>
</ha-markdown>
</ha-faded>
`
: ""}
<div class="versions">
<p>
${this.supervisor.localize("update_available.description", {
? html`
${this._changelogContent
? html`
<ha-faded>
<ha-markdown .content=${this._changelogContent}>
</ha-markdown>
</ha-faded>
`
: ""}
<div class="versions">
<p>
${this.supervisor.localize(
"update_available.description",
{
name: this._name,
version: this._version,
newest_version: this._version_latest,
}
)}
</p>
</div>
${["core", "addon"].includes(this._updateType)
? html`
<ha-formfield
.label=${this.supervisor.localize(
"update_available.create_backup"
)}
>
<ha-checkbox checked></ha-checkbox>
</ha-formfield>
`
: ""}
`
: html`<ha-circular-progress
aria-label="Updating"
size="large"
indeterminate
>
</ha-circular-progress>
<p class="progress-text">
${this.supervisor.localize("update_available.updating", {
name: this._name,
version: this._version,
newest_version: this._version_latest,
version: this._version_latest,
})}
</p>
</div>
${["core", "addon"].includes(this._updateType)
? html`
<ha-formfield
.label=${this.supervisor.localize(
"update_available.create_backup"
)}
>
<ha-checkbox checked></ha-checkbox>
</ha-formfield>
`
: ""}
`
: html`<ha-circular-progress alt="Updating" size="large" active>
</ha-circular-progress>
<p class="progress-text">
${this.supervisor.localize("update_available.updating", {
name: this._name,
version: this._version_latest,
})}
</p>`}
</p>`}
</div>
${this._version !== this._version_latest && !this._updating
? html`

View File

@@ -25,42 +25,41 @@
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@babel/runtime": "7.23.2",
"@braintree/sanitize-url": "6.0.4",
"@codemirror/autocomplete": "6.10.2",
"@codemirror/commands": "6.3.0",
"@codemirror/language": "6.9.1",
"@babel/runtime": "7.23.5",
"@braintree/sanitize-url": "7.0.0",
"@codemirror/autocomplete": "6.11.1",
"@codemirror/commands": "6.3.2",
"@codemirror/language": "6.9.3",
"@codemirror/legacy-modes": "6.3.3",
"@codemirror/search": "6.5.4",
"@codemirror/state": "6.3.1",
"@codemirror/view": "6.21.3",
"@codemirror/search": "6.5.5",
"@codemirror/state": "6.3.3",
"@codemirror/view": "6.22.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.11.0",
"@formatjs/intl-displaynames": "6.6.0",
"@formatjs/intl-datetimeformat": "6.12.0",
"@formatjs/intl-displaynames": "6.6.4",
"@formatjs/intl-getcanonicallocales": "2.3.0",
"@formatjs/intl-listformat": "7.5.0",
"@formatjs/intl-locale": "3.4.0",
"@formatjs/intl-numberformat": "8.8.0",
"@formatjs/intl-pluralrules": "5.2.7",
"@formatjs/intl-relativetimeformat": "11.2.7",
"@fullcalendar/core": "6.1.9",
"@fullcalendar/daygrid": "6.1.9",
"@fullcalendar/interaction": "6.1.9",
"@fullcalendar/list": "6.1.9",
"@fullcalendar/luxon3": "6.1.9",
"@fullcalendar/timegrid": "6.1.9",
"@lezer/highlight": "1.1.6",
"@formatjs/intl-listformat": "7.5.3",
"@formatjs/intl-locale": "3.4.3",
"@formatjs/intl-numberformat": "8.9.0",
"@formatjs/intl-pluralrules": "5.2.10",
"@formatjs/intl-relativetimeformat": "11.2.10",
"@fullcalendar/core": "6.1.10",
"@fullcalendar/daygrid": "6.1.10",
"@fullcalendar/interaction": "6.1.10",
"@fullcalendar/list": "6.1.10",
"@fullcalendar/luxon3": "6.1.10",
"@fullcalendar/timegrid": "6.1.10",
"@lezer/highlight": "1.2.0",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.4",
"@lit-labs/observers": "2.0.1",
"@lit-labs/virtualizer": "2.0.7",
"@lit-labs/motion": "1.0.6",
"@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.11",
"@lrnwebcomponents/simple-tooltip": "7.0.18",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
"@material/mwc-base": "0.27.0",
"@material/mwc-button": "0.27.0",
"@material/mwc-checkbox": "0.27.0",
"@material/mwc-circular-progress": "0.27.0",
"@material/mwc-dialog": "0.27.0",
"@material/mwc-drawer": "0.27.0",
"@material/mwc-fab": "0.27.0",
@@ -84,9 +83,6 @@
"@material/web": "=1.0.1",
"@mdi/js": "7.3.67",
"@mdi/svg": "7.3.67",
"@polymer/iron-flex-layout": "3.0.1",
"@polymer/iron-input": "3.0.1",
"@polymer/iron-resizable-behavior": "3.0.1",
"@polymer/paper-input": "3.2.1",
"@polymer/paper-item": "3.0.1",
"@polymer/paper-listbox": "3.0.1",
@@ -94,8 +90,8 @@
"@polymer/paper-toast": "3.0.1",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.2.0",
"@vaadin/vaadin-themable-mixin": "24.2.0",
"@vaadin/combo-box": "24.2.5",
"@vaadin/vaadin-themable-mixin": "24.2.5",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -103,46 +99,46 @@
"@webcomponents/scoped-custom-element-registry": "0.0.9",
"@webcomponents/webcomponentsjs": "2.8.0",
"app-datepicker": "5.1.1",
"chart.js": "4.4.0",
"chart.js": "4.4.1",
"comlink": "4.4.1",
"core-js": "3.33.1",
"core-js": "3.33.3",
"cropperjs": "1.6.1",
"date-fns": "2.30.0",
"date-fns-tz": "2.0.0",
"deep-clone-simple": "1.1.1",
"deep-freeze": "0.0.1",
"fuse.js": "6.6.2",
"element-internals-polyfill": "1.3.9",
"fuse.js": "7.0.0",
"google-timezones-json": "1.2.0",
"hls.js": "1.4.12",
"hls.js": "1.4.13",
"home-assistant-js-websocket": "9.1.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.5.4",
"intl-messageformat": "10.5.8",
"js-yaml": "4.1.0",
"leaflet": "1.9.4",
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.4.3",
"marked": "9.1.2",
"luxon": "3.4.4",
"marked": "11.0.1",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
"punycode": "2.3.0",
"punycode": "2.3.1",
"qr-scanner": "1.4.2",
"qrcode": "1.5.3",
"resize-observer-polyfill": "1.5.1",
"roboto-fontface": "0.10.0",
"rrule": "2.7.2",
"sortablejs": "1.15.0",
"rrule": "2.8.1",
"sortablejs": "1.15.1",
"stacktrace-js": "2.0.2",
"superstruct": "1.0.3",
"tinykeys": "2.1.0",
"tsparticles-engine": "2.12.0",
"tsparticles-preset-links": "2.12.0",
"ua-parser-js": "1.0.36",
"ua-parser-js": "1.0.37",
"unfetch": "5.0.0",
"vis-data": "7.1.7",
"vis-network": "9.1.8",
"vue": "2.7.14",
"vis-data": "7.1.9",
"vis-network": "9.1.9",
"vue": "2.7.15",
"vue2-daterange-picker": "0.6.8",
"weekstart": "2.0.0",
"workbox-cacheable-response": "7.0.0",
@@ -154,14 +150,15 @@
"xss": "1.0.14"
},
"devDependencies": {
"@babel/core": "7.23.2",
"@babel/plugin-proposal-decorators": "7.23.2",
"@babel/plugin-transform-runtime": "7.23.2",
"@babel/preset-env": "7.23.2",
"@babel/preset-typescript": "7.23.2",
"@bundle-stats/plugin-webpack-filter": "4.7.7",
"@babel/core": "7.23.5",
"@babel/helper-define-polyfill-provider": "0.4.3",
"@babel/plugin-proposal-decorators": "7.23.5",
"@babel/plugin-transform-runtime": "7.23.4",
"@babel/preset-env": "7.23.5",
"@babel/preset-typescript": "7.23.3",
"@bundle-stats/plugin-webpack-filter": "4.8.3",
"@koa/cors": "4.0.0",
"@lokalise/node-api": "12.0.0",
"@lokalise/node-api": "12.1.0",
"@octokit/auth-oauth-device": "6.0.1",
"@octokit/plugin-retry": "6.0.1",
"@octokit/rest": "20.0.2",
@@ -170,46 +167,44 @@
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-json": "6.0.1",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.4",
"@types/babel__plugin-transform-runtime": "7.9.4",
"@types/chromecast-caf-receiver": "6.0.11",
"@types/chromecast-caf-sender": "1.0.7",
"@types/esprima": "4.0.5",
"@rollup/plugin-replace": "5.0.5",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.12",
"@types/chromecast-caf-sender": "1.0.8",
"@types/glob": "8.1.0",
"@types/html-minifier-terser": "7.0.1",
"@types/js-yaml": "4.0.8",
"@types/leaflet": "1.9.7",
"@types/leaflet-draw": "1.0.9",
"@types/luxon": "3.3.3",
"@types/mocha": "10.0.3",
"@types/qrcode": "1.5.4",
"@types/serve-handler": "6.1.3",
"@types/sortablejs": "1.15.4",
"@types/tar": "6.1.7",
"@types/ua-parser-js": "0.7.38",
"@types/html-minifier-terser": "7.0.2",
"@types/js-yaml": "4.0.9",
"@types/leaflet": "1.9.8",
"@types/leaflet-draw": "1.0.11",
"@types/luxon": "3.3.7",
"@types/mocha": "10.0.6",
"@types/qrcode": "1.5.5",
"@types/serve-handler": "6.1.4",
"@types/sortablejs": "1.15.7",
"@types/tar": "6.1.10",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.8.0",
"@typescript-eslint/eslint-plugin": "6.13.2",
"@typescript-eslint/parser": "6.13.2",
"@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3",
"babel-plugin-template-html-minifier": "4.1.0",
"chai": "4.3.10",
"del": "7.1.0",
"eslint": "8.52.0",
"eslint": "8.55.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "17.1.0",
"eslint-config-prettier": "9.0.0",
"eslint-import-resolver-webpack": "0.13.7",
"eslint-config-prettier": "9.1.0",
"eslint-import-resolver-webpack": "0.13.8",
"eslint-plugin-disable": "2.0.3",
"eslint-plugin-import": "2.28.1",
"eslint-plugin-lit": "1.9.1",
"eslint-plugin-lit-a11y": "4.1.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-lit": "1.10.1",
"eslint-plugin-lit-a11y": "4.1.1",
"eslint-plugin-unused-imports": "3.0.0",
"eslint-plugin-wc": "2.0.4",
"esprima": "4.0.1",
"fancy-log": "2.0.0",
"fs-extra": "11.1.1",
"fs-extra": "11.2.0",
"glob": "10.3.10",
"gulp": "4.0.2",
"gulp-flatmap": "1.0.2",
@@ -221,8 +216,8 @@
"husky": "8.0.3",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.0.2",
"lit-analyzer": "2.0.0-pre.3",
"lint-staged": "15.2.0",
"lit-analyzer": "2.0.1",
"lodash.template": "4.5.0",
"magic-string": "0.30.5",
"map-stream": "0.0.7",
@@ -230,19 +225,19 @@
"object-hash": "3.0.0",
"open": "9.1.0",
"pinst": "3.0.0",
"prettier": "3.0.3",
"prettier": "3.1.0",
"rollup": "2.79.1",
"rollup-plugin-string": "3.0.0",
"rollup-plugin-terser": "7.0.2",
"rollup-plugin-visualizer": "5.9.2",
"rollup-plugin-visualizer": "5.10.0",
"serve-handler": "6.1.5",
"sinon": "17.0.0",
"sinon": "17.0.1",
"source-map-url": "0.4.1",
"systemjs": "6.14.2",
"tar": "6.2.0",
"terser-webpack-plugin": "5.3.9",
"ts-lit-plugin": "2.0.0-pre.1",
"typescript": "5.2.2",
"ts-lit-plugin": "2.0.1",
"typescript": "5.3.3",
"vinyl-buffer": "1.0.1",
"vinyl-source-stream": "2.0.0",
"webpack": "5.89.0",
@@ -257,9 +252,11 @@
"resolutions": {
"@polymer/polymer": "patch:@polymer/polymer@3.5.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
"@material/mwc-button@^0.25.3": "^0.27.0",
"lit@^2.7.4 || ^3.0.0": "^2.7.4",
"lit": "2.8.0",
"clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3",
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
},
"packageManager": "yarn@3.6.4"
"packageManager": "yarn@4.0.2"
}

View File

@@ -1,23 +1 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="480.000000pt" height="480.000000pt" viewBox="0 0 480.000000 480.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,480.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2313 4666 c-23 -7 -56 -23 -75 -34 -47 -30 -2059 -2048 -2095 -2102
-45 -67 -77 -135 -109 -230 l-29 -85 0 -995 0 -995 27 -51 c31 -59 93 -118
152 -145 39 -18 83 -19 1001 -19 l960 0 -406 405 c-395 395 -406 406 -433 395
-15 -5 -63 -10 -107 -10 -429 0 -566 577 -181 767 67 34 86 38 164 42 105 4
165 -13 246 -67 113 -74 175 -190 176 -327 1 -44 -3 -96 -7 -115 l-8 -35 316
-315 315 -315 0 1160 -1 1160 -51 35 c-260 177 -226 567 62 704 82 39 209 48
293 21 239 -78 354 -352 242 -575 -32 -63 -89 -125 -141 -156 l-44 -26 0 -811
0 -812 315 315 c218 217 313 320 309 330 -14 35 -16 134 -4 190 26 122 111
227 230 284 82 39 209 48 293 21 115 -38 214 -130 258 -242 19 -46 23 -78 24
-153 0 -86 -3 -101 -32 -163 -40 -84 -118 -163 -198 -202 -49 -23 -77 -29
-150 -33 -50 -2 -108 1 -130 7 l-40 11 -437 -438 -438 -437 0 -307 0 -308 998
0 c981 0 998 1 1042 21 58 26 115 81 148 144 l27 50 0 995 0 995 -33 95 c-72
209 -6 135 -1147 1278 -840 843 -1040 1037 -1082 1059 -64 31 -159 39 -220 19z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="640" viewBox="0 0 240 240"><path d="M120.001 0c-3.787 0-7.573 1.499-10.444 4.49L12.211 105.905a25.921 25.921 0 0 0-2.098 2.501 35.25 35.25 0 0 0-1.96 2.942c-3.01 5.021-5.285 11.318-6.074 16.898-.03.21-.088.429-.11.636a27.355 27.355 0 0 0-.213 3.317v93.023a14.78 14.78 90 0 0 14.78 14.78h90.92L67.422 198.29a20.2 20.2 90 1 1 12.542-13.06l31.17 32.474V98.726a20.2 20.2 90 1 1 17.734 0v83.44l31.001-32.299a20.2 20.2 90 1 1 12.267 13.357l-43.269 45.082V240h94.9a14.479 14.479 90 0 0 14.478-14.479v-93.314c0-1.059-.069-2.168-.214-3.314-.7-5.73-3.06-12.327-6.183-17.537a35.801 35.801 0 0 0-1.955-2.937 26.271 26.271 0 0 0-2.102-2.506L130.444 4.486C127.573 1.494 123.786-.002 120.001 0"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 747 B

View File

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

View File

@@ -1,5 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"configMigration": true,
"extends": [
":ignoreModulesAndTests",
":label(Dependencies)",
@@ -10,7 +11,7 @@
"group:recommended",
"npm:unpublishSafe"
],
"enabledManagers": ["npm"],
"enabledManagers": ["npm", "nvm"],
"postUpdateOptions": ["yarnDedupeHighest"],
"lockFileMaintenance": {
"description": ["Run after patch releases but before next beta"],
@@ -28,11 +29,22 @@
"matchPackageNames": ["vue"],
"allowedVersions": "< 3"
},
{
"description": "Group MDI packages",
"groupName": "Material Design Icons",
"matchPackageNames": ["@mdi/js", "@mdi/svg"]
},
{
"description": "Group tsparticles engine and presets",
"groupName": "tsparticles",
"matchPackageNames": ["tsparticles-engine"],
"matchPackagePrefixes": ["tsparticles-preset-"]
},
{
"description": "Group and temporarily disable WDS packages",
"groupName": "Web Dev Server",
"matchPackagePrefixes": ["@web/dev-server"],
"enabled": false
}
]
}

View File

@@ -40,4 +40,5 @@ docker run \
--file /opt/src/${LOCAL_FILE} \
--lang-iso ${LANG_ISO} \
--convert-placeholders=false \
--replace-modified=true
--replace-modified=true \
# --cleanup-mode=true

View File

@@ -8,13 +8,20 @@ import "../components/ha-alert";
import "../components/ha-checkbox";
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
import "../components/ha-formfield";
import "../components/ha-markdown";
import { AuthProvider, autocompleteLoginFields } from "../data/auth";
import {
AuthProvider,
autocompleteLoginFields,
createLoginFlow,
deleteLoginFlow,
redirectWithAuthCode,
submitLoginFlow,
} from "../data/auth";
import {
DataEntryFlowStep,
DataEntryFlowStepForm,
} from "../data/data_entry_flow";
import "./ha-auth-form";
import { fireEvent } from "../common/dom/fire_event";
type State = "loading" | "error" | "step";
@@ -30,18 +37,18 @@ export class HaAuthFlow extends LitElement {
@property() public localize!: LocalizeFunc;
@property({ attribute: false }) public step?: DataEntryFlowStep;
@property({ type: Boolean }) private storeToken = false;
@state() private _state: State = "loading";
@state() private _stepData?: Record<string, any>;
@state() private _step?: DataEntryFlowStep;
@state() private _errorMessage?: string;
@state() private _submitting = false;
@state() private _storeToken = false;
createRenderRoot() {
return this;
}
@@ -49,27 +56,29 @@ export class HaAuthFlow extends LitElement {
willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (!changedProps.has("_step")) {
if (!changedProps.has("step")) {
return;
}
if (!this._step) {
if (!this.step) {
this._stepData = undefined;
return;
}
const oldStep = changedProps.get("_step") as HaAuthFlow["_step"];
this._state = "step";
const oldStep = changedProps.get("step") as HaAuthFlow["step"];
if (
!oldStep ||
this._step.flow_id !== oldStep.flow_id ||
(this._step.type === "form" &&
this.step.flow_id !== oldStep.flow_id ||
(this.step.type === "form" &&
oldStep.type === "form" &&
this._step.step_id !== oldStep.step_id)
this.step.step_id !== oldStep.step_id)
) {
this._stepData =
this._step.type === "form"
? computeInitialHaFormData(this._step.data_schema)
this.step.type === "form"
? computeInitialHaFormData(this.step.data_schema)
: undefined;
}
}
@@ -77,14 +86,28 @@ export class HaAuthFlow extends LitElement {
protected render() {
return html`
<style>
ha-auth-flow .action {
margin: 24px 0 8px;
text-align: center;
}
ha-auth-flow .store-token {
margin-top: 10px;
margin-left: -16px;
}
a.forgot-password {
color: var(--primary-color);
text-decoration: none;
font-size: 0.875rem;
}
.space-between {
display: flex;
justify-content: space-between;
align-items: center;
}
form {
text-align: center;
max-width: 336px;
width: 100%;
}
ha-auth-form {
display: block;
margin-top: 16px;
}
</style>
<form>${this._renderForm()}</form>
`;
@@ -118,7 +141,7 @@ export class HaAuthFlow extends LitElement {
this._providerChanged(this.authProvider);
}
if (!changedProps.has("_step") || this._step?.type !== "form") {
if (!changedProps.has("step") || this.step?.type !== "form") {
return;
}
@@ -132,20 +155,31 @@ export class HaAuthFlow extends LitElement {
}
private _renderForm() {
const showBack =
this.step?.type === "form" &&
this.authProvider?.users &&
!["select_mfa_module", "mfa"].includes(this.step.step_id);
switch (this._state) {
case "step":
if (this._step == null) {
if (this.step == null) {
return nothing;
}
return html`
${this._renderStep(this._step)}
<div class="action">
${this._renderStep(this.step)}
<div class="action ${showBack ? "space-between" : ""}">
${showBack
? html`<mwc-button @click=${this._localFlow}>
${this.localize("ui.panel.page-authorize.form.previous")}
</mwc-button>`
: nothing}
<mwc-button
raised
@click=${this._handleSubmit}
.disabled=${this._submitting}
>
${this._step.type === "form"
${this.step.type === "form"
? this.localize("ui.panel.page-authorize.form.next")
: this.localize("ui.panel.page-authorize.form.start_over")}
</mwc-button>
@@ -154,11 +188,9 @@ export class HaAuthFlow extends LitElement {
case "error":
return html`
<ha-alert alert-type="error">
${this.localize(
"ui.panel.page-authorize.form.error",
"error",
this._errorMessage
)}
${this.localize("ui.panel.page-authorize.form.error", {
error: this._errorMessage,
})}
</ha-alert>
<div class="action">
<mwc-button raised @click=${this._startOver}>
@@ -182,24 +214,18 @@ export class HaAuthFlow extends LitElement {
case "abort":
return html`
${this.localize("ui.panel.page-authorize.abort_intro")}:
<ha-markdown
allowsvg
breaks
.content=${this.localize(
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
)}
></ha-markdown>
${this.localize(
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
)}
`;
case "form":
return html`
${this._computeStepDescription(step)
? html`
<ha-markdown
breaks
.content=${this._computeStepDescription(step)}
></ha-markdown>
`
: nothing}
<h1>
${!["select_mfa_module", "mfa"].includes(step.step_id)
? this.localize("ui.panel.page-authorize.welcome_home")
: this.localize("ui.panel.page-authorize.just_checking")}
</h1>
${this._computeStepDescription(step)}
<ha-auth-form
.data=${this._stepData}
.schema=${autocompleteLoginFields(step.data_schema)}
@@ -212,15 +238,28 @@ export class HaAuthFlow extends LitElement {
${this.clientId === genClientId() &&
!["select_mfa_module", "mfa"].includes(step.step_id)
? html`
<ha-formfield
class="store-token"
.label=${this.localize("ui.panel.page-authorize.store_token")}
>
<ha-checkbox
.checked=${this._storeToken}
@change=${this._storeTokenChanged}
></ha-checkbox>
</ha-formfield>
<div class="space-between">
<ha-formfield
class="store-token"
.label=${this.localize(
"ui.panel.page-authorize.store_token"
)}
>
<ha-checkbox
.checked=${this.storeToken}
@change=${this._storeTokenChanged}
></ha-checkbox>
</ha-formfield>
<a
class="forgot-password"
href="https://www.home-assistant.io/docs/locked_out/#forgot-password"
target="_blank"
rel="noreferrer noopener"
>${this.localize(
"ui.panel.page-authorize.forgot_password"
)}</a
>
</div>
`
: ""}
`;
@@ -230,15 +269,12 @@ export class HaAuthFlow extends LitElement {
}
private _storeTokenChanged(e: CustomEvent<HTMLInputElement>) {
this._storeToken = (e.currentTarget as HTMLInputElement).checked;
this.storeToken = (e.currentTarget as HTMLInputElement).checked;
}
private async _providerChanged(newProvider?: AuthProvider) {
if (this._step && this._step.type === "form") {
fetch(`/auth/login_flow/${this._step.flow_id}`, {
method: "DELETE",
credentials: "same-origin",
}).catch((err) => {
if (this.step && this.step.type === "form") {
deleteLoginFlow(this.step.flow_id).catch((err) => {
// eslint-disable-next-line no-console
console.error("Error delete obsoleted auth flow", err);
});
@@ -253,26 +289,26 @@ export class HaAuthFlow extends LitElement {
}
try {
const response = await fetch("/auth/login_flow", {
method: "POST",
credentials: "same-origin",
body: JSON.stringify({
client_id: this.clientId,
handler: [newProvider.type, newProvider.id],
redirect_uri: this.redirectUri,
}),
});
const response = await createLoginFlow(this.clientId, this.redirectUri, [
newProvider.type,
newProvider.id,
]);
const data = await response.json();
if (response.ok) {
// allow auth provider bypass the login form
if (data.type === "create_entry") {
this._redirect(data.result);
redirectWithAuthCode(
this.redirectUri!,
data.result,
this.oauth2State,
this.storeToken
);
return;
}
this._step = data;
this.step = data;
this._state = "step";
} else {
this._state = "error";
@@ -286,27 +322,6 @@ export class HaAuthFlow extends LitElement {
}
}
private _redirect(authCode: string) {
// OAuth 2: 3.1.2 we need to retain query component of a redirect URI
let url = this.redirectUri!;
if (!url.includes("?")) {
url += "?";
} else if (!url.endsWith("&")) {
url += "&";
}
url += `code=${encodeURIComponent(authCode)}`;
if (this.oauth2State) {
url += `&state=${encodeURIComponent(this.oauth2State)}`;
}
if (this._storeToken) {
url += `&storeToken=true`;
}
document.location.assign(url);
}
private _stepDataChanged(ev: CustomEvent) {
this._stepData = ev.detail.value;
}
@@ -314,13 +329,7 @@ export class HaAuthFlow extends LitElement {
private _computeStepDescription(step: DataEntryFlowStepForm) {
const resourceKey =
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description` as const;
const args: string[] = [];
const placeholders = step.description_placeholders || {};
Object.keys(placeholders).forEach((key) => {
args.push(key);
args.push(placeholders[key]);
});
return this.localize(resourceKey, ...args);
return this.localize(resourceKey, step.description_placeholders);
}
private _computeLabelCallback(step: DataEntryFlowStepForm) {
@@ -349,10 +358,10 @@ export class HaAuthFlow extends LitElement {
private async _handleSubmit(ev: Event) {
ev.preventDefault();
if (this._step == null) {
if (this.step == null) {
return;
}
if (this._step.type !== "form") {
if (this.step.type !== "form") {
this._providerChanged(this.authProvider);
return;
}
@@ -361,11 +370,7 @@ export class HaAuthFlow extends LitElement {
const postData = { ...this._stepData, client_id: this.clientId };
try {
const response = await fetch(`/auth/login_flow/${this._step.flow_id}`, {
method: "POST",
credentials: "same-origin",
body: JSON.stringify(postData),
});
const response = await submitLoginFlow(this.step.flow_id, postData);
const newStep = await response.json();
@@ -376,10 +381,15 @@ export class HaAuthFlow extends LitElement {
}
if (newStep.type === "create_entry") {
this._redirect(newStep.result);
redirectWithAuthCode(
this.redirectUri!,
newStep.result,
this.oauth2State,
this.storeToken
);
return;
}
this._step = newStep;
this.step = newStep;
this._state = "step";
} catch (err: any) {
// eslint-disable-next-line no-console
@@ -390,6 +400,10 @@ export class HaAuthFlow extends LitElement {
this._submitting = false;
}
}
private _localFlow() {
fireEvent(this, "default-login-flow", { value: false });
}
}
declare global {

View File

@@ -41,8 +41,8 @@ export class HaAuthFormString extends HaFormString {
!this.isPassword
? this.stringType
: this.unmaskedPassword
? "text"
: "password"
? "text"
: "password"
}
.label=${this.label}
.value=${this.data || ""}

View File

@@ -13,6 +13,7 @@ import {
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { registerServiceWorker } from "../util/register-service-worker";
import "./ha-auth-flow";
import "./ha-local-auth-flow";
import("./ha-pick-auth-provider");
@@ -39,6 +40,8 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
@state() private _error?: string;
@state() private _forceDefaultLogin = false;
constructor() {
super();
const query = extractSearchParamsObject() as AuthUrlSearchParams;
@@ -60,6 +63,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
ha-authorize ha-alert {
display: block;
margin: 16px 0;
background-color: var(--primary-background-color, #fafafa);
}
</style>
<ha-alert alert-type="error"
@@ -68,19 +72,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
`;
}
if (!this._authProviders) {
return html`
<style>
ha-authorize p {
font-size: 14px;
line-height: 20px;
}
</style>
<p>${this.localize("ui.panel.page-authorize.initializing")}</p>
`;
}
const inactiveProviders = this._authProviders.filter(
const inactiveProviders = this._authProviders?.filter(
(prv) => prv !== this._authProvider
);
@@ -89,21 +81,83 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
return html`
<style>
ha-pick-auth-provider {
display: block;
margin-top: 48px;
}
ha-auth-flow {
display: block;
margin-top: 24px;
}
ha-auth-flow,
ha-local-auth-flow {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
ha-alert {
display: block;
margin: 16px 0;
background-color: var(--primary-background-color, #fafafa);
}
p {
font-size: 14px;
line-height: 20px;
}
.card-content {
background: var(
--ha-card-background,
var(--card-background-color, white)
);
box-shadow: var(--ha-card-box-shadow, none);
box-sizing: border-box;
border-radius: var(--ha-card-border-radius, 12px);
border-width: var(--ha-card-border-width, 1px);
border-style: solid;
border-color: var(
--ha-card-border-color,
var(--divider-color, #e0e0e0)
);
color: var(--primary-text-color);
position: relative;
padding: 16px;
}
.action {
margin: 16px 0 8px;
display: flex;
width: 100%;
max-width: 336px;
justify-content: center;
}
.space-between {
justify-content: space-between;
}
.footer {
padding-top: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
ha-language-picker {
width: 200px;
border-radius: 4px;
overflow: hidden;
--ha-select-height: 40px;
--mdc-select-fill-color: none;
--mdc-select-label-ink-color: var(--primary-text-color, #212121);
--mdc-select-ink-color: var(--primary-text-color, #212121);
--mdc-select-idle-line-color: transparent;
--mdc-select-hover-line-color: transparent;
--mdc-select-dropdown-icon-color: var(--primary-text-color, #212121);
--mdc-shape-small: 0;
}
.footer a {
text-decoration: none;
color: var(--primary-text-color);
margin-right: 16px;
}
h1 {
font-size: 28px;
font-weight: 400;
margin-top: 16px;
margin-bottom: 16px;
}
</style>
${!this._ownInstance
@@ -120,33 +174,61 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
>`,
})}
</ha-alert>`
: html`<p>${this.localize("ui.panel.page-authorize.authorizing")}</p>`}
${inactiveProviders.length > 0
? html`<p>
${this.localize("ui.panel.page-authorize.logging_in_with", {
authProviderName: html`<b>${this._authProvider!.name}</b>`,
})}
</p>`
: nothing}
<ha-auth-flow
.clientId=${this.clientId}
.redirectUri=${this.redirectUri}
.oauth2State=${this.oauth2State}
.authProvider=${this._authProvider}
.localize=${this.localize}
></ha-auth-flow>
${inactiveProviders.length > 0
? html`
<ha-pick-auth-provider
.localize=${this.localize}
.clientId=${this.clientId}
.authProviders=${inactiveProviders}
@pick-auth-provider=${this._handleAuthProviderPick}
></ha-pick-auth-provider>
`
: ""}
<div
class="card-content"
@default-login-flow=${this._handleDefaultLoginFlow}
>
${!this._authProvider
? html`<p>
${this.localize("ui.panel.page-authorize.initializing")}
</p> `
: !this._forceDefaultLogin &&
this._authProvider!.users &&
this.clientId != null &&
this.redirectUri != null
? html`<ha-local-auth-flow
.clientId=${this.clientId}
.redirectUri=${this.redirectUri}
.oauth2State=${this.oauth2State}
.authProvider=${this._authProvider}
.authProviders=${this._authProviders}
.localize=${this.localize}
.ownInstance=${this._ownInstance}
></ha-local-auth-flow>`
: html`<ha-auth-flow
.clientId=${this.clientId}
.redirectUri=${this.redirectUri}
.oauth2State=${this.oauth2State}
.authProvider=${this._authProvider}
.localize=${this.localize}
></ha-auth-flow>
${inactiveProviders!.length > 0
? html`
<ha-pick-auth-provider
.localize=${this.localize}
.clientId=${this.clientId}
.authProviders=${inactiveProviders}
@pick-auth-provider=${this._handleAuthProviderPick}
></ha-pick-auth-provider>
`
: ""}`}
</div>
<div class="footer">
<ha-language-picker
.value=${this.language}
.label=${""}
nativeName
@value-changed=${this._languageChanged}
></ha-language-picker>
<a
href="https://www.home-assistant.io/docs/authentication/"
target="_blank"
rel="noreferrer noopener"
>${this.localize("ui.panel.page-authorize.help")}</a
>
</div>
`;
}
@@ -199,12 +281,18 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
);
}
if (window.innerWidth > 450) {
import("../resources/particles");
}
// If we are logging into the instance that is hosting this auth form
// we will register the service worker to start preloading.
if (url.host === location.host) {
this._ownInstance = true;
registerServiceWorker(this, false);
}
import("../components/ha-language-picker");
}
protected updated(changedProps: PropertyValues) {
@@ -245,7 +333,22 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
}
}
private _handleDefaultLoginFlow(ev) {
this._forceDefaultLogin = ev.detail.value;
}
private async _handleAuthProviderPick(ev) {
this._authProvider = ev.detail;
}
private _languageChanged(ev: CustomEvent) {
const language = ev.detail.value;
this.language = language;
try {
localStorage.setItem("selectedLanguage", JSON.stringify(language));
} catch (err: any) {
// Ignore
}
}
}

View File

@@ -0,0 +1,485 @@
/* eslint-disable lit/prefer-static-styles */
import "@material/mwc-button";
import { mdiEye, mdiEyeOff } from "@mdi/js";
import { html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-alert";
import "../components/ha-button";
import "../components/ha-icon-button";
import "../components/user/ha-person-badge";
import {
AuthProvider,
createLoginFlow,
deleteLoginFlow,
redirectWithAuthCode,
submitLoginFlow,
} from "../data/auth";
import { DataEntryFlowStep } from "../data/data_entry_flow";
import { BasePerson, listUserPersons } from "../data/person";
import "./ha-auth-textfield";
import type { HaAuthTextField } from "./ha-auth-textfield";
@customElement("ha-local-auth-flow")
export class HaLocalAuthFlow extends LitElement {
@property({ attribute: false }) public authProvider?: AuthProvider;
@property({ attribute: false }) public authProviders?: AuthProvider[];
@property() public clientId?: string;
@property() public redirectUri?: string;
@property() public oauth2State?: string;
@property({ type: Boolean }) public ownInstance = false;
@property() public localize!: LocalizeFunc;
@state() private _error?: string;
@state() private _step?: DataEntryFlowStep;
@state() private _submitting = false;
@state() private _persons?: Record<string, BasePerson>;
@state() private _selectedUser?: string;
@state() private _unmaskedPassword = false;
createRenderRoot() {
return this;
}
willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (!this.hasUpdated) {
this._load();
}
}
protected render() {
if (!this.authProvider?.users || !this._persons) {
return nothing;
}
const userIds = Object.keys(this.authProvider.users).filter(
(userId) => userId in this._persons!
);
return html`
<style>
.content {
max-width: 560px;
}
.persons {
margin-top: 24px;
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: center;
}
.persons.force-small {
max-width: 350px;
}
.person {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
text-align: center;
cursor: pointer;
width: 80px;
}
.person[role="button"] {
outline: none;
padding: 8px;
border-radius: 4px;
}
.person[role="button"]:focus-visible {
background: rgba(var(--rgb-primary-color), 0.1);
}
.person p {
margin-bottom: 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
}
ha-person-badge {
width: 80px;
height: 80px;
--person-badge-font-size: 2em;
}
form {
width: 100%;
}
ha-auth-textfield {
display: block !important;
position: relative;
}
ha-auth-textfield ha-icon-button {
position: absolute;
top: 4px;
right: 4px;
z-index: 9;
}
.login-form {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 336px;
margin-top: 24px;
}
.login-form .person {
cursor: default;
width: auto;
}
.login-form .person p {
font-size: 28px;
margin-top: 24px;
margin-bottom: 32px;
line-height: normal;
}
.login-form ha-person-badge {
width: 120px;
height: 120px;
--person-badge-font-size: 3em;
}
ha-list-item {
margin-top: 16px;
}
ha-button {
--mdc-typography-button-text-transform: none;
}
.forgot-password-container {
text-align: right;
padding: 8px 0 16px 0;
}
a.forgot-password {
color: var(--primary-color);
text-decoration: none;
font-size: 0.875rem;
}
button {
color: var(--primary-color);
background: none;
border: none;
padding: 8px;
font: inherit;
font-size: 0.875rem;
text-align: left;
cursor: pointer;
outline: none;
border-radius: 4px;
}
button:focus-visible {
background: rgba(var(--rgb-primary-color), 0.1);
}
</style>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
${this._step
? html`<ha-auth-flow
.clientId=${this.clientId}
.redirectUri=${this.redirectUri}
.oauth2State=${this.oauth2State}
.step=${this._step}
storeToken
.localize=${this.localize}
></ha-auth-flow>`
: this._selectedUser
? html`<div class="login-form">
<div class="person">
<ha-person-badge
.person=${this._persons[this._selectedUser]}
></ha-person-badge>
<p>${this._persons[this._selectedUser].name}</p>
</div>
<form>
<input
type="hidden"
name="username"
autocomplete="username"
readonly
.value=${this.authProvider.users[this._selectedUser]}
/>
<ha-auth-textfield
.type=${this._unmaskedPassword ? "text" : "password"}
autocomplete="current-password"
id="password"
name="password"
.label=${this.localize(
"ui.panel.page-authorize.form.providers.homeassistant.step.init.data.password"
)}
required
autoValidate
iconTrailing
validationMessage="Required"
>
<ha-icon-button
toggles
.label=${this.localize(
this._unmaskedPassword
? "ui.panel.page-authorize.form.hide_password"
: "ui.panel.page-authorize.form.show_password"
) ||
(this._unmaskedPassword
? "Hide password"
: "Show password")}
@click=${this._toggleUnmaskedPassword}
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
></ha-icon-button>
</ha-auth-textfield>
<div class="forgot-password-container">
<a
class="forgot-password"
href="https://www.home-assistant.io/docs/locked_out/#forgot-password"
target="_blank"
rel="noreferrer noopener"
>${this.localize(
"ui.panel.page-authorize.forgot_password"
)}</a
>
</div>
<div class="action space-between">
<mwc-button
@click=${this._restart}
.disabled=${this._submitting}
>
${this.localize("ui.panel.page-authorize.form.previous")}
</mwc-button>
<mwc-button
raised
@click=${this._handleSubmit}
.disabled=${this._submitting}
>
${this.localize("ui.panel.page-authorize.form.next")}
</mwc-button>
</div>
</form>
</div>`
: html`<h1>
${this.localize("ui.panel.page-authorize.welcome_home")}
</h1>
${this.localize("ui.panel.page-authorize.who_is_logging_in")}
<div
class="persons ${userIds.length < 10 && userIds.length % 4 === 1
? "force-small"
: ""}"
>
${userIds.map((userId) => {
const person = this._persons![userId];
return html`<div
class="person"
.userId=${userId}
@click=${this._personSelected}
@keyup=${this._handleKeyUp}
role="button"
tabindex="0"
>
<ha-person-badge .person=${person}></ha-person-badge>
<p>${person.name}</p>
</div>`;
})}
</div>
<div class="action">
<button @click=${this._otherLogin} tabindex="0">
${this.localize("ui.panel.page-authorize.other_options")}
</button>
</div>`}
`;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this.addEventListener("keypress", (ev) => {
if (ev.key === "Enter") {
this._handleSubmit(ev);
}
});
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("_selectedUser") && this._selectedUser) {
const passwordElement = this.renderRoot.querySelector(
"#password"
) as HaAuthTextField;
passwordElement.updateComplete.then(() => {
passwordElement.focus();
});
}
}
private async _load() {
try {
this._persons = await listUserPersons();
} catch {
this._persons = {};
this._error = "Failed to fetch persons";
}
}
private _restart() {
this._selectedUser = undefined;
this._error = undefined;
}
private _toggleUnmaskedPassword() {
this._unmaskedPassword = !this._unmaskedPassword;
}
private _handleKeyUp(ev: KeyboardEvent) {
if (ev.key === "Enter" || ev.key === " ") {
this._personSelected(ev);
}
}
private async _personSelected(ev) {
const userId = ev.currentTarget.userId;
if (
this.ownInstance &&
this.authProviders?.find((prv) => prv.type === "trusted_networks")
) {
try {
const flowResponse = await createLoginFlow(
this.clientId,
this.redirectUri,
["trusted_networks", null]
);
const data = await flowResponse.json();
if (data.type === "create_entry") {
redirectWithAuthCode(
this.redirectUri!,
data.result,
this.oauth2State,
true
);
return;
}
try {
if (!data.data_schema[0].options.find((opt) => opt[0] === userId)) {
throw new Error("User not available");
}
const postData = { user: userId, client_id: this.clientId };
const response = await submitLoginFlow(data.flow_id, postData);
if (response.ok) {
const result = await response.json();
if (result.type === "create_entry") {
redirectWithAuthCode(
this.redirectUri!,
result.result,
this.oauth2State,
true
);
return;
}
} else {
throw new Error("Invalid response");
}
} catch {
deleteLoginFlow(data.flow_id).catch((err) => {
// eslint-disable-next-line no-console
console.error("Error delete obsoleted auth flow", err);
});
}
} catch {
// Ignore
}
}
this._selectedUser = userId;
}
private async _handleSubmit(ev: Event) {
ev.preventDefault();
if (!this.authProvider?.users || !this._selectedUser) {
return;
}
this._error = undefined;
this._submitting = true;
const flowResponse = await createLoginFlow(
this.clientId,
this.redirectUri,
["homeassistant", null]
);
const data = await flowResponse.json();
const postData = {
username: this.authProvider.users[this._selectedUser],
password: (this.renderRoot.querySelector("#password") as HaAuthTextField)
.value,
client_id: this.clientId,
};
try {
const response = await submitLoginFlow(data.flow_id, postData);
const newStep = await response.json();
if (response.status === 403) {
this._error = newStep.message;
return;
}
if (newStep.type === "create_entry") {
redirectWithAuthCode(
this.redirectUri!,
newStep.result,
this.oauth2State,
true
);
return;
}
if (newStep.errors.base) {
this._error = this.localize(
`ui.panel.page-authorize.form.providers.homeassistant.error.${newStep.errors.base}`
);
throw new Error(this._error);
}
this._step = newStep;
} catch {
deleteLoginFlow(data.flow_id).catch((err) => {
// eslint-disable-next-line no-console
console.error("Error delete obsoleted auth flow", err);
});
if (!this._error) {
this._error = this.localize(
"ui.panel.page-authorize.form.unknown_error"
);
}
} finally {
this._submitting = false;
}
}
private _otherLogin() {
fireEvent(this, "default-login-flow", { value: true });
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-local-auth-flow": HaLocalAuthFlow;
}
interface HASSDomEvents {
"default-login-flow": { value: boolean };
}
}

View File

@@ -21,7 +21,11 @@ export class HaPickAuthProvider extends LitElement {
protected render() {
return html`
<p>${this.localize("ui.panel.page-authorize.pick_auth_provider")}:</p>
<h3>
<span
>${this.localize("ui.panel.page-authorize.pick_auth_provider")}</span
>
</h3>
<mwc-list>
${this.authProviders.map(
(provider) => html`
@@ -35,8 +39,8 @@ export class HaPickAuthProvider extends LitElement {
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
`
)}</mwc-list
>
)}
</mwc-list>
`;
}
@@ -45,12 +49,34 @@ export class HaPickAuthProvider extends LitElement {
}
static styles = css`
p {
margin-top: 0;
h3 {
margin: 0 -16px;
position: relative;
z-index: 1;
text-align: center;
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
h3:before {
border-top: 1px solid var(--divider-color);
content: "";
margin: 0 auto;
position: absolute;
top: 50%;
left: 0;
right: 0;
bottom: 0;
width: 100%;
z-index: -1;
}
h3 span {
background: var(--card-background-color);
padding: 0 15px;
}
mwc-list {
margin: 0 -16px;
--mdc-list-side-padding: 16px;
margin: 16px -16px 0;
--mdc-list-side-padding: 24px;
}
`;
}

View File

@@ -58,10 +58,10 @@ const matchMaxScale = (
return outputColors.map((value) => Math.round(value * factor));
};
const mired2kelvin = (miredTemperature: number) =>
export const mired2kelvin = (miredTemperature: number) =>
Math.floor(1000000 / miredTemperature);
const kelvin2mired = (kelvintTemperature: number) =>
export const kelvin2mired = (kelvintTemperature: number) =>
Math.floor(1000000 / kelvintTemperature);
export const rgbww2rgb = (

View File

@@ -10,10 +10,10 @@ const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
page.component
? isComponentLoaded(hass, page.component)
: page.components
? page.components.some((integration) =>
isComponentLoaded(hass, integration)
)
: true;
? page.components.some((integration) =>
isComponentLoaded(hass, integration)
)
: true;
const isCore = (page: PageNavigation) => page.core;
const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced;

View File

@@ -203,6 +203,7 @@ export const DOMAINS_WITH_CARD = [
"select",
"timer",
"text",
"update",
"vacuum",
"water_heater",
];

View File

@@ -1,33 +0,0 @@
/**
* Update root's child element to be newElementTag replacing another existing child if any.
* Copy attributes into the child element.
*/
export default function dynamicContentUpdater(root, newElementTag, attributes) {
const rootEl = root;
let customEl;
if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) {
customEl = rootEl.lastChild;
} else {
if (rootEl.lastChild) {
rootEl.removeChild(rootEl.lastChild);
}
// Creating an element with upper case works fine in Chrome, but in FF it doesn't immediately
// become a defined Custom Element. Polymer does that in some later pass.
customEl = document.createElement(newElementTag.toLowerCase());
}
if (customEl.setProperties) {
customEl.setProperties(attributes);
} else {
// If custom element definition wasn't loaded yet - setProperties would be
// missing, but no harm in setting attributes one-by-one then.
Object.keys(attributes).forEach((key) => {
customEl[key] = attributes[key];
});
}
if (customEl.parentNode === null) {
rootEl.appendChild(customEl);
}
}

View File

@@ -4,5 +4,5 @@ export const mainWindow =
window.name === MAIN_WINDOW_NAME
? window
: parent.name === MAIN_WINDOW_NAME
? parent
: top!;
? parent
: top!;

View File

@@ -1,5 +1,6 @@
import { HassConfig, HassEntity } from "home-assistant-js-websocket";
import {
DOMAIN_ATTRIBUTES_FORMATERS,
DOMAIN_ATTRIBUTES_UNITS,
TEMPERATURE_ATTRIBUTES,
} from "../../data/entity_attributes";
@@ -14,7 +15,7 @@ import { formatNumber } from "../number/format_number";
import { capitalizeFirstLetter } from "../string/capitalize-first-letter";
import { isDate } from "../string/is_date";
import { isTimestamp } from "../string/is_timestamp";
import { blankBeforePercent } from "../translations/blank_before_percent";
import { blankBeforeUnit } from "../translations/blank_before_unit";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
import { computeStateDomain } from "./compute_state_domain";
@@ -38,37 +39,28 @@ export const computeAttributeValueDisplay = (
// Number value, return formatted number
if (typeof attributeValue === "number") {
const formattedValue = formatNumber(attributeValue, locale);
const domain = computeStateDomain(stateObj);
const formatter = DOMAIN_ATTRIBUTES_FORMATERS[domain]?.[attribute];
const formattedValue = formatter
? formatter(attributeValue, locale)
: formatNumber(attributeValue, locale);
let unit = DOMAIN_ATTRIBUTES_UNITS[domain]?.[attribute] as
| string
| undefined;
if (domain === "light" && attribute === "brightness") {
const percentage = Math.round((attributeValue / 255) * 100);
return `${percentage}${blankBeforePercent(locale)}%`;
}
if (domain === "weather") {
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
}
if (unit === "%") {
return `${formattedValue}${blankBeforePercent(locale)}${unit}`;
}
if (unit === "°") {
return `${formattedValue}${unit}`;
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
unit = config.unit_system.temperature;
}
if (unit) {
return `${formattedValue} ${unit}`;
}
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
return `${formattedValue} ${config.unit_system.temperature}`;
return `${formattedValue}${blankBeforeUnit(unit, locale)}${unit}`;
}
return formattedValue;

View File

@@ -2,14 +2,10 @@ import { HassConfig, HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { FrontendLocaleData, TimeZone } from "../../data/translation";
import {
updateIsInstallingFromAttributes,
UPDATE_SUPPORT_PROGRESS,
} from "../../data/update";
import { HomeAssistant } from "../../types";
import {
formatDuration,
UNIT_TO_MILLISECOND_CONVERT,
formatDuration,
} from "../datetime/duration";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
@@ -19,10 +15,9 @@ import {
getNumberFormatOptions,
isNumericFromAttributes,
} from "../number/format_number";
import { blankBeforePercent } from "../translations/blank_before_percent";
import { blankBeforeUnit } from "../translations/blank_before_unit";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
import { supportsFeatureFromAttributes } from "./supports-feature";
export const computeStateDisplaySingleEntity = (
localize: LocalizeFunc,
@@ -108,16 +103,20 @@ export const computeStateDisplayFromEntityAttributes = (
// fallback to default
}
}
const unit = !attributes.unit_of_measurement
? ""
: attributes.unit_of_measurement === "%"
? blankBeforePercent(locale) + "%"
: ` ${attributes.unit_of_measurement}`;
return `${formatNumber(
const value = formatNumber(
state,
locale,
getNumberFormatOptions({ state, attributes } as HassEntity, entity)
)}${unit}`;
);
const unit = attributes.unit_of_measurement;
if (unit) {
return `${value}${blankBeforeUnit(unit)}${unit}`;
}
return value;
}
const domain = computeDomain(entityId);
@@ -204,27 +203,6 @@ export const computeStateDisplayFromEntityAttributes = (
}
}
if (domain === "update") {
// When updating, and entity does not support % show "Installing"
// When updating, and entity does support % show "Installing (xx%)"
// When update available, show the version
// When the latest version is skipped, show the latest version
// When update is not available, show "Up-to-date"
// When update is not available and there is no latest_version show "Unavailable"
return state === "on"
? updateIsInstallingFromAttributes(attributes)
? supportsFeatureFromAttributes(attributes, UPDATE_SUPPORT_PROGRESS) &&
typeof attributes.in_progress === "number"
? localize("ui.card.update.installing_with_progress", {
progress: attributes.in_progress,
})
: localize("ui.card.update.installing")
: attributes.latest_version
: attributes.skipped_version === attributes.latest_version
? attributes.latest_version ?? localize("state.default.unavailable")
: localize("ui.card.update.up_to_date");
}
return (
(entity?.translation_key &&
localize(

View File

@@ -100,8 +100,8 @@ export const domainIconWithoutDefault = (
return compareState === "unavailable"
? mdiRobotConfused
: compareState === "off"
? mdiRobotOff
: mdiRobot;
? mdiRobotOff
: mdiRobot;
case "binary_sensor":
return binarySensorIcon(compareState, stateObj);

View File

@@ -0,0 +1,15 @@
import { FrontendLocaleData } from "../../data/translation";
import { blankBeforePercent } from "./blank_before_percent";
export const blankBeforeUnit = (
unit: string,
localeOptions?: FrontendLocaleData
): string => {
if (unit === "°") {
return "";
}
if (localeOptions && unit === "%") {
return blankBeforePercent(localeOptions);
}
return " ";
};

View File

@@ -1,4 +1,5 @@
import IntlMessageFormat from "intl-messageformat";
import type { HTMLTemplateResult } from "lit";
import { polyfillLocaleData } from "../../resources/locale-data-polyfill";
import { Resources, TranslationDict } from "../../types";
@@ -40,9 +41,13 @@ export type FlattenObjectKeys<
: `${Key}`
: never;
// Later, don't return string when HTML is passed, and don't allow undefined
export type LocalizeFunc<Keys extends string = LocalizeKeys> = (
key: Keys,
...args: any[]
values?: Record<
string,
string | number | HTMLTemplateResult | null | undefined
>
) => string;
interface FormatType {
@@ -124,6 +129,7 @@ export const computeLocalize = async <Keys extends string = LocalizeKeys>(
argObject = args[0];
} else {
for (let i = 0; i < args.length; i += 2) {
// @ts-expect-error in some places the old format (key, value, key, value) is used
argObject[args[i]] = args[i + 1];
}
}

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