Compare commits

..

220 Commits

Author SHA1 Message Date
Paulus Schoutsen
0ec7973e95 Exclude viz network from babel 2021-05-31 13:36:24 -07:00
GitHub Action
07f4e5ac5c Translation update 2021-05-31 03:47:12 +00:00
Paulus Schoutsen
db82a90414 Bumped version to 20210531.0 2021-05-30 20:17:09 -07:00
Bram Kragten
51a693badf Convert ha-store-auth-card to Lit/TS/ha-card (#9300) 2021-05-30 20:16:45 -07:00
Bram Kragten
2aa8f5b352 Dev states: replace pattern in word by wildcard search (#9288) 2021-05-30 20:11:53 -07:00
Bram Kragten
93b3b8f985 Fix editor structs (#9286) 2021-05-30 20:08:46 -07:00
Bram Kragten
92c8bd80b5 Catch translation errors (#9299) 2021-05-30 17:02:03 +02:00
Philip Allgaier
528af0157d Move entity attribution out of attribute expansion panel (#9296) 2021-05-30 16:06:22 +02:00
Bram Kragten
10a77b6278 Update translations 2021-05-30 16:02:03 +02:00
GitHub Action
03bbf6a582 Translation update 2021-05-30 03:38:49 +00:00
GitHub Action
63fcb649d2 Translation update 2021-05-29 03:21:09 +00:00
Bram Kragten
4f60a92b92 Fix default themes (#9290)
* Fix default themes

* Simplify pick theme row
2021-05-28 20:02:28 -07:00
Bram Kragten
0419c1a41f Fix icon loading (#9289) 2021-05-28 19:54:28 -07:00
Joakim Sørensen
2d5ae78521 Add selection to snapshot table for mass deletion (#9284)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-28 15:49:40 +02:00
Joakim Sørensen
959134df02 Better secrets support in add-on configuration (#9275) 2021-05-28 14:37:16 +02:00
Bram Kragten
a9f9fc4ce2 Bumped version to 20210528.0 2021-05-28 12:26:38 +02:00
dependabot[bot]
cfb370a3c8 Bump dns-packet from 1.3.1 to 1.3.4 (#9281)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-28 12:25:46 +02:00
Bram Kragten
353435c8d5 Fix icon db loading (#9280) 2021-05-28 11:54:20 +02:00
Bram Kragten
c8c85d096b Show less ticks in charts (#9279) 2021-05-28 10:16:37 +02:00
GitHub Action
19c9c8f227 Translation update 2021-05-28 03:03:41 +00:00
Bram Kragten
6ea2a29eea Hide attribute measurement and editable attributes (#9272) 2021-05-27 11:48:27 +02:00
Joakim Sørensen
59f3f819a6 Revert name change from selectedTheme to selectedThemeSettings (#9273) 2021-05-27 10:21:58 +02:00
GitHub Action
93e8f52880 Translation update 2021-05-27 02:45:51 +00:00
Joakim Sørensen
02810efcc4 Replace Hass_io_ prefix for snapshot downloads (#9270) 2021-05-26 21:56:27 +02:00
Bram Kragten
4b9be7ce16 Fix entity filtering in dev states (#9268) 2021-05-26 17:27:45 +02:00
Bram Kragten
f3ec09e480 Bumped version to 20210526.0 2021-05-26 16:58:31 +02:00
Bram Kragten
8291a84e3e Hide network config when not loaded (#9265) 2021-05-26 07:53:54 -07:00
J. Nick Koston
b0e1f0f73a Add network configuration (#9210)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-26 16:44:15 +02:00
Bram Kragten
a66b966e7d Fix a bunch of updates triggering updated (#9260) 2021-05-26 16:29:50 +02:00
Philip Allgaier
5f56040c64 Add friendly_name to dev tools "Entity" column + fuzzy search (#7582) 2021-05-26 15:29:51 +02:00
Bram Kragten
eaccd22267 Fix chartjs deprecation warnings (#9261) 2021-05-26 15:05:01 +02:00
Bram Kragten
27845a7345 Fix logbook height (#9258) 2021-05-26 12:44:10 +02:00
Bram Kragten
f7ef8180e4 Guard for undefined item in quick bar (#9259) 2021-05-26 12:43:59 +02:00
Bram Kragten
5958eb9a55 Minor dependency bumps (#9249) 2021-05-26 12:04:39 +02:00
Bram Kragten
3ef2912b60 Fix typo in translation key 2021-05-26 11:19:09 +02:00
Joakim Sørensen
fa9c6a765a Replace closing with closed in dialogs (#9257) 2021-05-26 11:10:27 +02:00
Bram Kragten
c4a8899780 Bump idb-keyval (#9248)
https://github.com/jakearchibald/idb-keyval#updating-from-3x
2021-05-26 10:22:38 +02:00
Bram Kragten
3cc4628d03 Bump test dependencies (#9244) 2021-05-26 10:02:02 +02:00
GitHub Action
b6c5223221 Translation update 2021-05-26 02:25:19 +00:00
Philip Allgaier
cbd6d4251c Prevent shrinking of percent value in supervisor metrics (#9033) 2021-05-26 00:24:30 +02:00
Bram Kragten
fdcbb5b432 Bump js-yaml (#9245) 2021-05-26 00:13:58 +02:00
Bram Kragten
de09e31815 Fix resetting theme, only fallback to light when theme doesnt support… (#9253) 2021-05-26 00:11:17 +02:00
Philip Allgaier
f55e911313 Prevent formatting for unknown attribute (#9252) 2021-05-26 00:08:41 +02:00
Bram Kragten
465a91dbf3 Fix circulair progress producing scrollbars (#9247) 2021-05-25 23:59:24 +02:00
Bram Kragten
835a7833ae Bump memoize one (#9243) 2021-05-25 23:53:58 +02:00
Bram Kragten
179717d40c Fix rollup build (#9246) 2021-05-25 23:51:31 +02:00
Philip Allgaier
3d4d789f7f Detect and format date & timestamp attributes (#9074) 2021-05-25 22:39:21 +02:00
Bram Kragten
d97fb19f05 Ingress: Wait for dialog to close before navigating (#9250) 2021-05-25 22:11:52 +02:00
Joakim Sørensen
0dd3757df2 Refresh snapshot create/restore dialogs (#9223)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-25 21:29:32 +02:00
Philip Allgaier
c32a4546f3 Translate NC account connection state (#9167) 2021-05-25 18:12:56 +02:00
Raman Gupta
1bb025ccd0 Add log level changed message when user changes Z-Wave JS log level (#9238)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-25 17:59:00 +02:00
Philip Allgaier
2b8033a97f Prevent cutting off of attributes in more-info light (#9219)
* Prevent cutting off of attributes in more-info light

* Update src/dialogs/more-info/controls/more-info-light.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-25 17:58:20 +02:00
Joakim Sørensen
21a3a8c594 Navigate cleanup (#9202) 2021-05-25 17:46:36 +02:00
Philip Allgaier
1026e90296 Put attributes in more-info into a foldable section (#9220)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-25 17:44:02 +02:00
Bram Kragten
0eca602e61 Use comboBoxRenderer from lit-vaadin-helpers (#9201) 2021-05-25 13:27:49 +02:00
Philip Allgaier
7f75ca81f1 Add support for custom themes to use dark mode (#8347) 2021-05-25 13:26:35 +02:00
Bram Kragten
8af05e2726 Optimise data table and device dashboard (#9217) 2021-05-25 13:12:44 +02:00
Bram Kragten
0a478ee1da Fix tabs styling (#9241) 2021-05-25 12:05:20 +02:00
GitHub Action
a4bdc5a05f Translation update 2021-05-25 02:00:53 +00:00
Philip Allgaier
d425767dae Ensure timer row uses correct state translation keys (#9143)
* Ensure timer row uses correct state translation keys

* Changes from review

* Update src/panels/lovelace/entity-rows/hui-timer-entity-row.ts

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

* Move logic to data/timer

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-24 23:27:00 +02:00
Philip Allgaier
c78382c119 Make it clear that those are the installed add-ons (#9228) 2021-05-24 23:20:22 +02:00
Philip Allgaier
ee15ddfbc3 Show correct number of disabled integrations (#9232) 2021-05-24 22:47:28 +02:00
Philip Allgaier
0af14eb77e Add refresh button to state dev tools (#9231) 2021-05-24 22:37:29 +02:00
GitHub Action
583cc4bc8a Translation update 2021-05-24 02:01:00 +00:00
GitHub Action
2ee92f48e6 Translation update 2021-05-23 02:04:38 +00:00
Franck Nijhof
d05e02ab3e Add the Supervisor as an ignorable discovery source (#9229) 2021-05-22 19:26:27 +02:00
Joakim Sørensen
abb9f8e233 Remove padding when narrow (#9209) 2021-05-22 06:12:51 -07:00
GitHub Action
f873ef9b59 Translation update 2021-05-22 01:52:23 +00:00
Philip Allgaier
1255b56522 Add missing translations to voice command dialog (#9221) 2021-05-21 10:11:31 +02:00
Bram Kragten
fd9bb4d8cc Make chrome work-around work in iframes (#9200) 2021-05-21 09:09:31 +02:00
GitHub Action
9328576b55 Translation update 2021-05-21 01:53:57 +00:00
Philip Allgaier
70a1edd1dd Allow users to select time format for UI rendering (#9042)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-20 16:23:53 +02:00
Brynley McDonald
87e4c209f4 Add icon to Entities card schema (#9208) 2021-05-20 15:29:32 +02:00
Bram Kragten
3d0a5642cc Only apply on Safari 14.0, and not 14.0.1 2021-05-20 14:23:50 +02:00
GitHub Action
e211d812ad Translation update 2021-05-20 01:50:44 +00:00
GitHub Action
0dcf673b87 Translation update 2021-05-19 01:50:13 +00:00
Paulus Schoutsen
cb14e1f20c Bumped version to 20210518.0 2021-05-18 15:12:08 -07:00
Bram Kragten
52087c0e30 Fix _initialize (#9206)
* Fix _initialize

* Update ha-demo.ts
2021-05-18 15:11:24 -07:00
Paulus Schoutsen
1b9286db76 Fix lit warnings (#9204)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-18 15:01:43 -07:00
Bram Kragten
bc92c0b052 Upgrade to Lit 2 (#9199) 2021-05-18 07:37:53 -07:00
Joakim Sørensen
245bb639f2 Fix URL to jinja template docs (#9198) 2021-05-18 10:22:03 +02:00
GitHub Action
8d81ed58c8 Translation update 2021-05-18 01:57:40 +00:00
Paulus Schoutsen
7890ca85a8 Bumped version to 20210517.0 2021-05-17 16:06:19 -07:00
Paulus Schoutsen
07bab7b264 Drop app-route (#9196) 2021-05-17 16:05:58 -07:00
Paulus Schoutsen
5730c14dc1 Make hassio backwards compat (#9195) 2021-05-17 23:59:46 +02:00
Paulus Schoutsen
f8e8b5ad18 Trace fixes (#9192)
* Show sub-conditions

* Better show errors in timeline

* Add rendered nodes

* Fix some rendering issues with sub-conditions in timeline

* Improve condition rendering
2021-05-17 20:19:47 +02:00
Charles Garwood
fd2728c02c Fix Z-Wave JS add node wizard and add interview status (#9145) 2021-05-17 17:15:01 +02:00
Bram Kragten
7e2bf920e1 Correct types for script automation editors (#9184) 2021-05-17 07:57:43 -07:00
Bram Kragten
1f65328f2d Make slider default for number selector (#9190) 2021-05-17 07:55:12 -07:00
Bram Kragten
4f731baa00 Add guard for non color lights (#9186) 2021-05-17 16:24:39 +02:00
Bram Kragten
5abb3dd8c1 Use default behaviour for service target (#8650) 2021-05-17 14:12:16 +02:00
GitHub Action
0a672c55c5 Translation update 2021-05-17 01:54:21 +00:00
GitHub Action
a6b2299c74 Translation update 2021-05-16 01:59:23 +00:00
Paulus Schoutsen
37cc6709d4 If we have a link, make it a link (#9181) 2021-05-15 12:25:28 +02:00
Paulus Schoutsen
f4ffbe67e2 Remove shadowroot from ha-markdown-element (#9187) 2021-05-15 12:23:33 +02:00
GitHub Action
9f32d72a41 Translation update 2021-05-15 01:51:54 +00:00
Bram Kragten
64a117d8ac Fix yarn.lock 2021-05-14 22:32:41 +02:00
Matt Emerick-Law
ebf0bdc840 Add duplicate scene functionality (#9175) 2021-05-14 21:44:03 +02:00
Julien Roy
cc0a120bf6 Switch update and openChangelog button (#9174)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-14 16:55:38 +02:00
Bram Kragten
fe2fe7468f Bump lodash (#9135) 2021-05-14 11:50:50 +02:00
Joakim Sørensen
b12a10ccb5 Add snapshot contents as secondary info for partial snapshots (#9166) 2021-05-14 11:49:52 +02:00
Bram Kragten
2ad2a4b198 Bump lokalize deps + support object format for args (#9155)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-05-13 22:47:47 -07:00
GitHub Action
6a62f05657 Translation update 2021-05-14 01:54:56 +00:00
GitHub Action
4910f60ec4 Translation update 2021-05-13 01:54:52 +00:00
Bram Kragten
d35168e88f Bump chart.js (#9160)
Replaces #9159
2021-05-12 17:09:21 -07:00
GitHub Action
01b3d2aca9 Translation update 2021-05-12 01:47:33 +00:00
dependabot[bot]
29e8d1cff0 Bump hosted-git-info from 2.7.1 to 2.8.9 (#9162)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.7.1 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.7.1...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-11 11:24:05 +02:00
Michael
4e1d10cc08 Improve UX for counter and input_number helpers (#9061) 2021-05-11 10:52:44 +02:00
GitHub Action
3575d94ca1 Translation update 2021-05-11 01:46:41 +00:00
Bram Kragten
d91546b532 Bump home-assistant-js-websocket (#9156) 2021-05-11 00:46:59 +02:00
Bram Kragten
9f554f4917 Silence babel warnings (#9158) 2021-05-10 22:57:42 +02:00
Bram Kragten
d4720a9244 Align state info in center (#9153) 2021-05-10 22:22:54 +02:00
Philip Allgaier
5c466712db Fix missing customElement import after Lit 2.0 bump (#9157) 2021-05-10 22:11:08 +02:00
Joakim Sørensen
6dc7e852ae Use hass-tabs-subpage-data-table for supervisor snapshots (#9103)
* Use hass-tabs-subpage-data-table for supervisor snapshots

* comments

* type

* cleanup

* change translations

* Update hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts

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

* reset

* fix after rebase

* internalProperty -> state

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-10 18:17:16 +02:00
dependabot[bot]
785f614bd9 Bump lodash from 4.17.15 to 4.17.21 (#9154)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-10 16:12:38 +02:00
uvjustin
0a8e27249d Bump hls.js from v1.0.1 to v1.0.3 (#9147) 2021-05-10 11:06:16 +02:00
GitHub Action
15ee87ee67 Translation update 2021-05-10 01:45:53 +00:00
GitHub Action
12612a16df Translation update 2021-05-09 01:44:02 +00:00
Philip Allgaier
4f449e2600 Adjust token relative_date wording (#9138) 2021-05-08 17:30:31 +02:00
Paulus Schoutsen
7f49f039fd Close new automation dialog before moving to next step (#9071)
* Close new automatin dialog before next step

* Update dialog-thingtalk.ts

* Fix

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-08 13:53:07 +02:00
Bram Kragten
88dc65bc4e Dont require manifest for the overflow menu (#9129) 2021-05-08 13:08:28 +02:00
Paulus Schoutsen
6edebe18ad Use grid for sensor cards on 2nd view of teaching birds demo (#9132) 2021-05-08 13:07:58 +02:00
GitHub Action
38b3a9205d Translation update 2021-05-08 01:42:37 +00:00
Joakim Sørensen
4b796b4929 Add supervisor_ingress support to my (#9087) 2021-05-07 15:08:57 -07:00
Bram Kragten
83cabcac28 Add tsc and eslint to pre-commit (#9131) 2021-05-07 15:07:05 -07:00
Thomas Lovén
d308c5d9b9 Add manual limit selection to graph header/footer (#9126)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-05-07 15:06:11 -07:00
Bram Kragten
9f032a61a9 Add compatibility for Lit 2.0 (#8878) 2021-05-07 13:16:14 -07:00
Bram Kragten
0f58214ba1 Bump leaflet + fix location editor (#9118) 2021-05-07 13:11:30 -07:00
Bram Kragten
c48a60cce6 Bump superstruct (#9119)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2021-05-07 13:10:35 -07:00
Bram Kragten
cd3ffceeff Fix for when the value doesnt get changed by the backend when we send it (#9105) 2021-05-07 14:33:33 +02:00
Bram Kragten
9be4a00169 Update UI when service schema change (#9120) 2021-05-07 11:28:37 +02:00
Bram Kragten
a9c7a39a47 Fix positioning of preload checkbox (#9115) 2021-05-07 11:16:25 +02:00
Philip Allgaier
abcdd60a21 Convert GPS to uppercase in attribute name (#9124) 2021-05-07 11:14:03 +02:00
Philip Allgaier
a94f85a100 Fix alignment of Entities card header toggle (#9123) 2021-05-07 11:13:48 +02:00
GitHub Action
9755bf723f Translation update 2021-05-07 01:42:34 +00:00
Philip Allgaier
a71ebcf47e Bump js-xss to 1.0.9 (#9121) 2021-05-06 19:51:08 +02:00
GitHub Action
72695631cd Translation update 2021-05-06 01:39:12 +00:00
Bram Kragten
2af211b543 Guard for undefined values in attribute filtering (#9089) 2021-05-05 10:32:24 +02:00
Joakim Sørensen
6e5e2625d6 Show supervisor addon configuration error (#8950)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-05 10:26:11 +02:00
Joakim Sørensen
23c1c2f5eb Bump wheels to 3.8-alpine3.12 (#9098) 2021-05-05 08:19:58 +02:00
GitHub Action
da85ee5d01 Translation update 2021-05-05 01:39:25 +00:00
Joakim Sørensen
d408e8653c Return instead of trhow (#9094) 2021-05-04 23:03:57 +02:00
Bram Kragten
cc76ccc3c9 Bumped version to 20210504.0 2021-05-04 23:01:56 +02:00
Bram Kragten
105a00d3e4 Use hs color when outside of wheel (#9088) 2021-05-04 22:51:28 +02:00
Philip Allgaier
2c08cba8cc Draw a <hr> above the attributes in more-info (#9090) 2021-05-04 22:04:27 +02:00
Philip Allgaier
344b11a204 Prevent not needed <hr> in more-info-light (#9080) 2021-05-04 14:18:02 +02:00
GitHub Action
1ff5bf0fd5 Translation update 2021-05-04 01:43:58 +00:00
Bram Kragten
c29cf7f77c Bumped version to 20210503.0 2021-05-03 15:47:44 +02:00
Bram Kragten
193cb46d60 Config flow: Show next when not last step (#9078) 2021-05-03 15:47:03 +02:00
Bram Kragten
9dc864d486 Break word if possible instead if break anywhere (#9076) 2021-05-02 20:42:04 -07:00
Bram Kragten
cee166839a Bump Lit element (#9068) 2021-05-02 20:41:00 -07:00
GitHub Action
1a60a3c728 Translation update 2021-05-03 01:49:21 +00:00
Paulus Schoutsen
5d946778cb Show setting for auto connect seperately if currently connected (#9072)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-05-02 14:45:53 +02:00
Paulus Schoutsen
ac5f85820f Integration Card: Fix Safari header break and deal with long errors (#9070) 2021-05-02 10:54:40 +02:00
GitHub Action
716e100a28 Translation update 2021-05-02 01:53:59 +00:00
Charles Garwood
7b8cb16c12 Add "subscribed to log" message on Z-Wave JS log subscription (#9062) 2021-05-01 14:31:59 +02:00
Philip Allgaier
00d46424a3 Translate new setup time info header + add blank before unit (#9067) 2021-05-01 14:31:43 +02:00
Philip Allgaier
2a5f940744 Ensure weather icon is centered in forecast (#9065) 2021-05-01 14:31:01 +02:00
Paulus Schoutsen
13cc016b36 Fix some hassio things (#9059) 2021-05-01 14:30:33 +02:00
GitHub Action
a8d49c27c8 Translation update 2021-05-01 01:48:36 +00:00
Bram Kragten
a8522e91b5 Bumped version to 20210430.0 2021-04-30 21:17:22 +02:00
Bram Kragten
5754f4463d Bump babel and eslint (#9049) 2021-04-30 12:15:31 -07:00
Bram Kragten
d4118ade0f Bump lit-html (#9053)
Adds support for Lit 2.0 directives
2021-04-30 12:12:20 -07:00
Bram Kragten
6d80f15a98 Bump codemirror (#9052) 2021-04-30 11:34:58 -07:00
Bram Kragten
f8aa472409 Only show warning when google is enabled (#9054) 2021-04-30 11:34:45 -07:00
Bram Kragten
df22fd00ca Check if temperature available in forecast (#9055) 2021-04-30 11:34:31 -07:00
David F. Mulcahey
ce2743a982 Move ZHA config panel section translations to backend (#9018) 2021-04-30 18:59:06 +02:00
Paulus Schoutsen
92b32458ad Fix grid card size when square (#9056) 2021-04-30 18:57:20 +02:00
Joakim Sørensen
d57e8a45d3 Break primary anywhere (#9050) 2021-04-30 15:02:01 +02:00
Joakim Sørensen
551d3ffdf3 Use haStyleScrollbar for integration card lists (#9051) 2021-04-30 15:01:31 +02:00
GitHub Action
7add6eb736 Translation update 2021-04-30 01:45:22 +00:00
rmogstad
a28616d535 Fix browser language detection for region specific languages (#8982) (#9026) 2021-04-30 01:11:03 +02:00
Paulus Schoutsen
a288fd370f Fix gallery element definitions (#9046) 2021-04-30 01:10:29 +02:00
Paulus Schoutsen
acd335e249 Set columns to 4 in demo 2021-04-29 14:06:52 -07:00
Bram Kragten
da0bfa1945 Bumped version to 20210429.0 2021-04-29 21:56:36 +02:00
Bram Kragten
3c61d709b5 Fix RGBWW colors (#9039) 2021-04-29 21:53:22 +02:00
Joakim Sørensen
ffc92a7b63 Fix overlap on integration card (#9037) 2021-04-29 12:36:07 -07:00
Paulus Schoutsen
af0c7b5a50 Fix grid card size (#9044)
* Fix grid card size

* Remove console

* Rename
2021-04-29 12:31:46 -07:00
Bram Kragten
1904c4d057 Fix race in theme setting (#9027)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-04-29 21:04:31 +02:00
Bram Kragten
542f169b36 Fix saving Lovelace config (#9041) 2021-04-29 09:05:58 -07:00
Bram Kragten
65a30bf60c Fix onboarding dark styles (#9040) 2021-04-29 08:55:45 -07:00
Bram Kragten
2e51da32f0 Handle when choose is not an array (#9028) 2021-04-29 08:53:58 -07:00
Philip Allgaier
0562242043 Format "IP" and "MAC" attribute names (#9034) 2021-04-29 15:55:51 +02:00
GitHub Action
debcdefc21 Translation update 2021-04-29 00:48:48 +00:00
Paulus Schoutsen
0de3f3a332 Merge remote-tracking branch 'origin/master' into dev 2021-04-28 10:29:18 -07:00
Paulus Schoutsen
4fcb4d449e Bumped version to 20210428.0 2021-04-28 10:28:50 -07:00
Bram Kragten
408fe25abd Update workbox, add expiration on cache (#9020) 2021-04-28 10:26:30 -07:00
Paulus Schoutsen
236e5e0b25 Show subtrace steps when selecting a node (#9023)
* Show subtrace steps when selecting a node

* Limit logic to just child conditions
2021-04-28 10:23:30 -07:00
Charles Garwood
ebe0caba83 MVP Z-Wave JS Log Viewer (#9008)
* Add element to subscribe to ZJS logs

* set log level and adjust styling

* review comments

* add ZWaveJS to function names

* use flexbox

* Review comments

* import

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-28 13:00:13 -04:00
Philip Allgaier
9d33c0cfaf Fix unit support in gauge editor (#9021) 2021-04-28 18:19:51 +02:00
Paulus Schoutsen
7962130a0c Show entities on area page (#8980)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2021-04-28 17:01:43 +02:00
David F. Mulcahey
9690434cac Reconfigure ZHA device take 2 (#8990)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-28 12:30:09 +02:00
GitHub Action
7304544c37 Translation update 2021-04-28 00:48:54 +00:00
Bram Kragten
5a3408c242 Bumped version to 20210427.0 2021-04-28 00:22:22 +02:00
Bram Kragten
16996f25af Update demo lights (#9011) 2021-04-28 00:21:23 +02:00
J. Nick Koston
0c12586019 Display cumulative setup seconds on the config info page (#8720)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2021-04-27 23:38:29 +02:00
Joakim Sørensen
93a1adaa56 Remove analytics translations (#9002) 2021-04-27 22:44:28 +02:00
Charles Garwood
83e65e2cc6 Add UI for Z-Wave JS Device Reinterview (#8957) 2021-04-27 13:30:15 -07:00
Bram Kragten
36586b798e Use supported_color_modes to determine what UI elements to show (#8961) 2021-04-27 10:43:25 -07:00
Paulus Schoutsen
20c351949f Strategy: Update name -> type (#9006) 2021-04-27 10:04:52 -07:00
Bram Kragten
b63bd92d81 Bump codemirror with iOS fix (#8992) 2021-04-27 16:29:20 +02:00
Bram Kragten
17d3755152 Bumped version to 20210407.3 2021-04-09 20:05:32 +02:00
Paulus Schoutsen
d7c0c2ea72 Fix failed conditions reason (#8870) 2021-04-09 20:05:17 +02:00
Charles Garwood
7c823c98ae Add units to Z-Wave JS Node Config inputs (#8869)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-04-09 20:04:56 +02:00
Bram Kragten
97508a6f31 Update value of date input (#8865) 2021-04-09 20:04:29 +02:00
Paulus Schoutsen
2507a41b6e Pass narrow (#8864) 2021-04-09 20:04:03 +02:00
Paulus Schoutsen
9b628546c1 Remove owner guard from analytics (#8842) 2021-04-08 21:00:00 +02:00
Bram Kragten
d0837fada8 Bumped version to 20210407.2 2021-04-08 20:56:03 +02:00
Paulus Schoutsen
520647d72f Add logbook note (#8843)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-08 20:55:10 +02:00
Bram Kragten
51c888845c Handle choose being null (#8859)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-04-08 20:54:51 +02:00
Bram Kragten
e4606219bc Check if logbook component loaded when fetching trace (#8861) 2021-04-08 20:54:34 +02:00
Philip Allgaier
716335df2c Use number format setting for attribute rows (#8844) 2021-04-08 20:54:14 +02:00
Philip Allgaier
ad4f90c502 Mention unique ID requirement in trace button tooltip (#8853) 2021-04-08 20:53:57 +02:00
Donnie
a1bdfa7560 Fix spinner regression and remove unnecessary twoline config (#8847) 2021-04-08 20:53:43 +02:00
Joakim Sørensen
34ca807044 Merge pull request #8841 from home-assistant/dev 2021-04-07 16:36:31 +02:00
Bram Kragten
8af55efdb3 Merge pull request #8836 from home-assistant/dev 2021-04-07 10:43:35 +02:00
Paulus Schoutsen
6393072e68 Merge pull request #8826 from home-assistant/dev 2021-04-06 09:49:27 -07:00
Bram Kragten
9e1a8b646b Merge pull request #8803 from home-assistant/dev
20210402.1
2021-04-02 21:15:13 +02:00
Paulus Schoutsen
c810e541ea Merge pull request #8795 from home-assistant/dev 2021-04-01 15:33:43 -07:00
Bram Kragten
66432608ed Merge pull request #8774 from home-assistant/dev 2021-03-31 17:27:43 +02:00
898 changed files with 17285 additions and 15810 deletions

View File

@@ -4,8 +4,7 @@
"plugin:@typescript-eslint/recommended",
"plugin:wc/recommended",
"plugin:lit/recommended",
"prettier",
"prettier/@typescript-eslint"
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
@@ -29,9 +28,7 @@
"__BUILD__": false,
"__VERSION__": false,
"__STATIC_PATH__": false,
"Polymer": true,
"webkitSpeechRecognition": false,
"ResizeObserver": false
"Polymer": true
},
"env": {
"browser": true,
@@ -85,6 +82,25 @@
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/naming-convention": [
0,
{
"selector": "default",
"format": ["camelCase", "snake_case"],
"leadingUnderscore": "allow",
"trailingUnderscore": "allow"
},
{
"selector": ["variable"],
"format": ["camelCase", "snake_case", "UPPER_CASE"],
"leadingUnderscore": "allow",
"trailingUnderscore": "allow"
},
{
"selector": "typeLike",
"format": ["PascalCase"]
}
],
"lit/attribute-value-entities": 0
},
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],

View File

@@ -37,9 +37,11 @@ jobs:
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos
- name: Run eslint
run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore
run: yarn run lint:eslint
- name: Run tsc
run: ./node_modules/.bin/tsc
run: yarn run lint:types
- name: Run prettier
run: yarn run lint:prettier
test:
runs-on: ubuntu-latest
steps:

View File

@@ -6,8 +6,8 @@ on:
- published
env:
WHEELS_TAG: 3.7-alpine3.11
PYTHON_VERSION: 3.7
WHEELS_TAG: 3.8-alpine3.12
PYTHON_VERSION: 3.8
NODE_VERSION: 12.1
jobs:

4
.mocharc.cjs Normal file
View File

@@ -0,0 +1,4 @@
module.exports = {
require: "test-mocha/testconf.js",
timeout: 10000,
};

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const env = require("./env.js");
const paths = require("./paths.js");
@@ -53,13 +54,13 @@ module.exports.babelOptions = ({ latestBuild }) => ({
babelrc: false,
presets: [
!latestBuild && [
require("@babel/preset-env").default,
"@babel/preset-env",
{
useBuiltIns: "entry",
corejs: "3.6",
},
],
require("@babel/preset-typescript").default,
"@babel/preset-typescript",
].filter(Boolean),
plugins: [
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
@@ -72,14 +73,9 @@ module.exports.babelOptions = ({ latestBuild }) => ({
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
[
require("@babel/plugin-proposal-decorators").default,
{ decoratorsBeforeExport: true },
],
[
require("@babel/plugin-proposal-class-properties").default,
{ loose: true },
],
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
["@babel/plugin-proposal-private-methods", { loose: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
].filter(Boolean),
});
@@ -87,6 +83,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
module.exports.babelExclude = () => [
require.resolve("@mdi/js/mdi.js"),
require.resolve("hls.js"),
require.resolve("vis-network/peer/esm/vis-network.js"),
];
const outputPath = (outputRoot, latestBuild) =>

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require("fs");
const path = require("path");
const paths = require("./paths.js");

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
module.exports = {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const commonjs = require("@rollup/plugin-commonjs");
@@ -32,88 +33,78 @@ const createRollupConfig = ({
publicPath,
dontHash,
isWDS,
}) => {
return {
/**
* @type { import("rollup").InputOptions }
*/
inputOptions: {
input: entry,
// Some entry points contain no JavaScript. This setting silences a warning about that.
// https://rollupjs.org/guide/en/#preserveentrysignatures
preserveEntrySignatures: false,
plugins: [
ignore({
files: bundle.emptyPackages({ latestBuild }),
}) => ({
/**
* @type { import("rollup").InputOptions }
*/
inputOptions: {
input: entry,
// Some entry points contain no JavaScript. This setting silences a warning about that.
// https://rollupjs.org/guide/en/#preserveentrysignatures
preserveEntrySignatures: false,
plugins: [
ignore({
files: bundle.emptyPackages({ latestBuild }),
}),
resolve({
extensions,
preferBuiltins: false,
browser: true,
rootDir: paths.polymer_dir,
}),
commonjs(),
json(),
babel({
...bundle.babelOptions({ latestBuild }),
extensions,
exclude: bundle.babelExclude(),
babelHelpers: isWDS ? "inline" : "bundled",
}),
string({
// Import certain extensions as strings
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
}),
replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })),
!isWDS &&
manifest({
publicPath,
}),
resolve({
extensions,
preferBuiltins: false,
browser: true,
rootDir: paths.polymer_dir,
!isWDS && worker(),
!isWDS && dontHashPlugin({ dontHash }),
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
!isWDS &&
isStatsBuild &&
visualizer({
// https://github.com/btd/rollup-plugin-visualizer#options
open: true,
sourcemap: true,
}),
commonjs({
namedExports: {
"js-yaml": ["safeDump", "safeLoad"],
},
}),
json(),
babel({
...bundle.babelOptions({ latestBuild }),
extensions,
exclude: bundle.babelExclude(),
babelHelpers: isWDS ? "inline" : "bundled",
}),
string({
// Import certain extensions as strings
include: [path.join(paths.polymer_dir, "node_modules/**/*.css")],
}),
replace(
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
),
!isWDS &&
manifest({
publicPath,
}),
!isWDS && worker(),
!isWDS && dontHashPlugin({ dontHash }),
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
!isWDS &&
isStatsBuild &&
visualizer({
// https://github.com/btd/rollup-plugin-visualizer#options
open: true,
sourcemap: true,
}),
].filter(Boolean),
},
/**
* @type { import("rollup").OutputOptions }
*/
outputOptions: {
// https://rollupjs.org/guide/en/#outputdir
dir: outputPath,
// https://rollupjs.org/guide/en/#outputformat
format: latestBuild ? "es" : "systemjs",
// https://rollupjs.org/guide/en/#outputexternallivebindings
externalLiveBindings: false,
// https://rollupjs.org/guide/en/#outputentryfilenames
// https://rollupjs.org/guide/en/#outputchunkfilenames
// https://rollupjs.org/guide/en/#outputassetfilenames
entryFileNames:
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
chunkFileNames:
isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
assetFileNames:
isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
// https://rollupjs.org/guide/en/#outputsourcemap
sourcemap: isProdBuild ? true : "inline",
},
};
};
].filter(Boolean),
},
/**
* @type { import("rollup").OutputOptions }
*/
outputOptions: {
// https://rollupjs.org/guide/en/#outputdir
dir: outputPath,
// https://rollupjs.org/guide/en/#outputformat
format: latestBuild ? "es" : "systemjs",
// https://rollupjs.org/guide/en/#outputexternallivebindings
externalLiveBindings: false,
// https://rollupjs.org/guide/en/#outputentryfilenames
// https://rollupjs.org/guide/en/#outputchunkfilenames
// https://rollupjs.org/guide/en/#outputassetfilenames
entryFileNames:
isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js",
chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js",
assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js",
// https://rollupjs.org/guide/en/#outputsourcemap
sourcemap: isProdBuild ? true : "inline",
},
});
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
return createRollupConfig(
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) =>
createRollupConfig(
bundle.config.app({
isProdBuild,
latestBuild,
@@ -121,31 +112,24 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
isWDS,
})
);
};
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
return createRollupConfig(
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
createRollupConfig(
bundle.config.demo({
isProdBuild,
latestBuild,
isStatsBuild,
})
);
};
const createCastConfig = ({ isProdBuild, latestBuild }) => {
return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
};
const createCastConfig = ({ isProdBuild, latestBuild }) =>
createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild }));
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
};
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
return createRollupConfig(
bundle.config.gallery({ isProdBuild, latestBuild })
);
};
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
module.exports = {
createAppConfig,

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const fs = require("fs");

View File

@@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const webpack = require("webpack");
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const paths = require("./paths.js");
const bundle = require("./bundle");
const bundle = require("./bundle.js");
const log = require("fancy-log");
class LogStartCompilePlugin {
@@ -94,6 +95,7 @@ const createWebpackConfig = ({
? path.resolve(context, resource)
: require.resolve(resource);
} catch (err) {
// eslint-disable-next-line no-console
console.error(
"Error in Home Assistant ignore plugin",
resource,
@@ -115,7 +117,7 @@ const createWebpackConfig = ({
new webpack.NormalModuleReplacementPlugin(
new RegExp(
require.resolve(
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
"@lit-labs/virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
)
),
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
@@ -124,6 +126,11 @@ const createWebpackConfig = ({
].filter(Boolean),
resolve: {
extensions: [".ts", ".js", ".json"],
alias: {
"lit/decorators$": "lit/decorators.js",
"lit/directive$": "lit/directive.js",
"lit/polyfill-support$": "lit/polyfill-support.js",
},
},
output: {
filename: ({ chunk }) => {
@@ -144,33 +151,24 @@ const createWebpackConfig = ({
};
};
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
return createWebpackConfig(
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
createWebpackConfig(
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild })
);
};
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
return createWebpackConfig(
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
createWebpackConfig(
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
);
};
const createCastConfig = ({ isProdBuild, latestBuild }) => {
return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
};
const createCastConfig = ({ isProdBuild, latestBuild }) =>
createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
const createHassioConfig = ({ isProdBuild, latestBuild }) => {
return createWebpackConfig(
bundle.config.hassio({ isProdBuild, latestBuild })
);
};
const createHassioConfig = ({ isProdBuild, latestBuild }) =>
createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild }));
const createGalleryConfig = ({ isProdBuild, latestBuild }) => {
return createWebpackConfig(
bundle.config.gallery({ isProdBuild, latestBuild })
);
};
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
module.exports = {
createAppConfig,

View File

@@ -1,16 +1,9 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { CastManager } from "../../../../src/cast/cast_manager";
import {
castSendShowLovelaceView,
@@ -32,7 +25,6 @@ import {
import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout";
import "@material/mwc-button/mwc-button";
@customElement("hc-cast")
class HcCast extends LitElement {
@@ -42,9 +34,9 @@ class HcCast extends LitElement {
@property() public castManager!: CastManager;
@internalProperty() private askWrite = false;
@state() private askWrite = false;
@internalProperty() private lovelaceConfig?: LovelaceConfig | null;
@state() private lovelaceConfig?: LovelaceConfig | null;
protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) {
@@ -54,9 +46,7 @@ class HcCast extends LitElement {
const error =
this.castManager.castState === "NO_DEVICES_AVAILABLE"
? html`
<p>
There were no suitable Chromecast devices to cast to found.
</p>
<p>There were no suitable Chromecast devices to cast to found.</p>
`
: undefined;
@@ -206,7 +196,7 @@ class HcCast extends LitElement {
}
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
.center-item {
display: flex;

View File

@@ -11,15 +11,8 @@ import {
getAuth,
getAuthOptions,
} from "home-assistant-js-websocket";
import {
css,
CSSResult,
customElement,
html,
LitElement,
TemplateResult,
internalProperty,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
import {
@@ -60,19 +53,19 @@ const INTRO = html`
@customElement("hc-connect")
export class HcConnect extends LitElement {
@internalProperty() private loading = false;
@state() private loading = false;
// If we had stored credentials but we cannot connect,
// show a screen asking retry or logout.
@internalProperty() private cannotConnect = false;
@state() private cannotConnect = false;
@internalProperty() private error?: string | TemplateResult;
@state() private error?: string | TemplateResult;
@internalProperty() private auth?: Auth;
@state() private auth?: Auth;
@internalProperty() private connection?: Connection;
@state() private connection?: Connection;
@internalProperty() private castManager?: CastManager | null;
@state() private castManager?: CastManager | null;
private openDemo = false;
@@ -86,9 +79,7 @@ export class HcConnect extends LitElement {
</div>
<div class="card-actions">
<a href="/">
<mwc-button>
Retry
</mwc-button>
<mwc-button> Retry </mwc-button>
</a>
<div class="spacer"></div>
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
@@ -299,7 +290,7 @@ export class HcConnect extends LitElement {
}
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
.card-content a {
color: var(--primary-color);

View File

@@ -4,15 +4,8 @@ import {
getUser,
HassUser,
} from "home-assistant-js-websocket";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-card";
@customElement("hc-layout")
@@ -69,7 +62,7 @@ class HcLayout extends LitElement {
}
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
:host {
display: flex;

View File

@@ -1,10 +1,5 @@
import {
customElement,
html,
internalProperty,
property,
TemplateResult,
} from "lit-element";
import { html, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mockHistory } from "../../../../demo/src/stubs/history";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import {
@@ -21,7 +16,7 @@ import "./hc-lovelace";
class HcDemo extends HassElement {
@property({ attribute: false }) public lovelacePath!: string;
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
@state() private _lovelaceConfig?: LovelaceConfig;
protected render(): TemplateResult {
if (!this._lovelaceConfig) {
@@ -38,10 +33,10 @@ class HcDemo extends HassElement {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._initialize();
this._initializeHass();
}
private async _initialize() {
private async _initializeHass() {
const initial: Partial<MockHomeAssistant> = {
// Override updateHass so that the correct hass lifecycle methods are called
updateHass: (hassUpdate: Partial<HomeAssistant>) =>

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { HomeAssistant } from "../../../../src/types";
@customElement("hc-launch-screen")
@@ -29,7 +22,7 @@ class HcLaunchScreen extends LitElement {
`;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
:host {
display: block;

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
@@ -91,7 +84,7 @@ class HcLovelace extends LitElement {
return undefined;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
:host {
min-height: 100vh;

View File

@@ -3,12 +3,8 @@ import {
getAuth,
UnsubscribeFunc,
} from "home-assistant-js-websocket";
import {
customElement,
html,
internalProperty,
TemplateResult,
} from "lit-element";
import { html, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { CAST_NS } from "../../../../src/cast/const";
import {
ConnectMessage,
@@ -36,13 +32,13 @@ let resourcesLoaded = false;
@customElement("hc-main")
export class HcMain extends HassElement {
@internalProperty() private _showDemo = false;
@state() private _showDemo = false;
@internalProperty() private _lovelaceConfig?: LovelaceConfig;
@state() private _lovelaceConfig?: LovelaceConfig;
@internalProperty() private _lovelacePath: string | number | null = null;
@state() private _lovelacePath: string | number | null = null;
@internalProperty() private _error?: string;
@state() private _error?: string;
private _unsubLovelace?: UnsubscribeFunc;

View File

@@ -246,11 +246,15 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
"light.living_room_lights": {
entity_id: "light.living_room_lights",
state: "off",
state: "on",
attributes: {
min_mireds: 111,
max_mireds: 400,
brightness: 175,
color_temp: 300,
supported_color_modes: ["brightness", "color_temp"],
friendly_name: "Living Room Lights",
color_mode: "color_temp",
supported_features: 55,
},
},
@@ -263,13 +267,27 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
},
"light.kitchen_lights": {
entity_id: "light.kitchen_lights",
state: "on",
attributes: {
min_mireds: 111,
max_mireds: 400,
brightness: 200,
rgb_color: [255, 175, 96],
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "rgb",
friendly_name: "Kitchen Lights",
supported_features: 55,
},
},
"light.lifx5": {
entity_id: "light.lifx5",
state: "off",
attributes: {
friendly_name: "Kitchen Lights",
supported_color_modes: ["brightness"],
friendly_name: "Garage Lights",
supported_features: 1,
},
},
"sensor.plexspy": {
entity_id: "sensor.plexspy",
state: "0",
@@ -482,16 +500,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
icon: "hademo:history",
},
},
"light.lifx5": {
entity_id: "light.lifx5",
state: "on",
attributes: {
min_mireds: 111,
max_mireds: 400,
friendly_name: "Garage Lights",
supported_features: 55,
},
},
"sensor.alok_to_home": {
entity_id: "sensor.alok_to_home",
state: "41",

View File

@@ -1114,6 +1114,9 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
min_mireds: 153,
max_mireds: 500,
brightness: 63,
color_temp: 200,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
friendly_name: "Upstairs lights",
supported_features: 63,
custom_ui_state_card: "state-card-custom-ui",
@@ -1125,6 +1128,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
friendly_name: "Walk in closet lights",
supported_features: 41,
supported_color_modes: ["brightness", "color_temp"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce",
},
@@ -1136,6 +1140,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
brightness: 254,
friendly_name: "Outdoor lights",
supported_features: 41,
supported_color_modes: ["brightness"],
color_mode: "brightness",
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce",
},
@@ -1148,6 +1154,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500,
brightness: 128,
color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"],
friendly_name: "Downstairs lights",
supported_features: 63,
@@ -1307,6 +1315,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
min_mireds: 153,
max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false,
friendly_name: "Bedside Lamp",
supported_features: 63,
@@ -1320,6 +1329,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
min_mireds: 153,
max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false,
friendly_name: "Floorlamp Reading Light",
supported_features: 43,
@@ -1335,6 +1345,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500,
brightness: 128,
color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"],
is_deconz_group: false,
friendly_name: "Hallway window light",
@@ -1349,6 +1361,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
brightness: 77,
is_deconz_group: false,
supported_color_modes: ["brightness"],
friendly_name: "Isa Ceiling Light",
supported_features: 41,
custom_ui_state_card: "state-card-custom-ui",
@@ -1363,6 +1376,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500,
brightness: 150,
color_temp: 366,
supported_color_modes: ["brightness", "color_temp"],
color_mode: "color_temp",
effect_list: ["colorloop"],
is_deconz_group: false,
friendly_name: "Floorlamp",
@@ -1377,6 +1392,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
friendly_name: "Bedroom Ceiling Light",
supported_features: 41,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light",
},
@@ -1387,6 +1403,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
friendly_name: "Nightlight",
supported_features: 17,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:lamp",
},
@@ -1753,6 +1770,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.2,
friendly_name: "Upstairs Hallway Light",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light",
},
@@ -1768,6 +1786,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0,
friendly_name: "Dining Room Light",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light",
},
@@ -1783,6 +1802,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0,
friendly_name: "Living room Spotlights",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light",
},
@@ -1799,6 +1819,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.5,
friendly_name: "Passage Lights",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light",
},
@@ -1843,6 +1864,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 37.4,
friendly_name: "Kitchen Lights",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light",
},

View File

@@ -440,57 +440,43 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
type: "horizontal-stack",
},
{
type: "grid",
columns: 2,
cards: [
{
cards: [
{
graph: "line",
type: "sensor",
entity: "sensor.temperature_bedroom",
},
{
graph: "line",
type: "sensor",
name: "S's room",
entity: "sensor.temperature_stefan",
},
],
type: "horizontal-stack",
graph: "line",
type: "sensor",
entity: "sensor.temperature_bedroom",
},
{
cards: [
{
graph: "line",
type: "sensor",
entity: "sensor.temperature_passage",
},
{
graph: "line",
type: "sensor",
name: "Bathroom",
entity: "sensor.temperature_downstairs_bathroom",
},
],
type: "horizontal-stack",
graph: "line",
type: "sensor",
name: "S's room",
entity: "sensor.temperature_stefan",
},
{
cards: [
{
graph: "line",
type: "sensor",
entity: "sensor.temperature_storage",
},
{
graph: "line",
type: "sensor",
name: "Refrigerator",
entity: "sensor.refrigerator",
},
],
type: "horizontal-stack",
graph: "line",
type: "sensor",
entity: "sensor.temperature_passage",
},
{
graph: "line",
type: "sensor",
name: "Bathroom",
entity: "sensor.temperature_downstairs_bathroom",
},
{
graph: "line",
type: "sensor",
entity: "sensor.temperature_storage",
},
{
graph: "line",
type: "sensor",
name: "Refrigerator",
entity: "sensor.refrigerator",
},
],
type: "vertical-stack",
},
{
entities: [

View File

@@ -1,5 +1,5 @@
/* eslint-disable */
import { LitElement } from "lit-element";
import { LitElement } from "lit";
import "./card-tools";
class CardModder extends LitElement {

View File

@@ -1,5 +1,5 @@
/* eslint-disable */
import { html, LitElement } from "lit-element";
import { html, LitElement } from "lit";
if (!window.cardTools) {
const version = 0.2;

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { CastManager } from "../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../src/cast/receiver_messages";
import "../../../src/components/ha-icon";
@@ -20,7 +13,7 @@ import { HomeAssistant } from "../../../src/types";
class CastDemoRow extends LitElement implements LovelaceRow {
public hass!: HomeAssistant;
@internalProperty() private _castManager?: CastManager | null;
@state() private _castManager?: CastManager | null;
public setConfig(_config: CastConfig): void {
// No config possible.
@@ -73,7 +66,7 @@ class CastDemoRow extends LitElement implements LovelaceRow {
this.style.display = this._castManager ? "" : "none";
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
:host {
display: flex;

View File

@@ -1,14 +1,7 @@
import "@material/mwc-button";
import {
css,
CSSResult,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { until } from "lit-html/directives/until";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { 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";
@@ -26,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@internalProperty() private _switching?: boolean;
@state() private _switching?: boolean;
private _hidden = localStorage.hide_demo_card;
@@ -113,7 +106,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
}
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
css`
a {

View File

@@ -22,9 +22,9 @@ import { mockTemplate } from "./stubs/template";
import { mockTranslations } from "./stubs/translations";
class HaDemo extends HomeAssistantAppEl {
protected async _initialize() {
protected async _initializeHass() {
const initial: Partial<MockHomeAssistant> = {
panelUrl: (this as any).panelUrl,
panelUrl: (this as any)._panelUrl,
// Override updateHass so that the correct hass lifecycle methods are called
updateHass: (hassUpdate: Partial<HomeAssistant>) =>
this._updateHass(hassUpdate),
@@ -70,7 +70,7 @@ class HaDemo extends HomeAssistantAppEl {
}
e.preventDefault();
navigate(this, href);
navigate(href);
},
{ capture: true }
);

View File

@@ -3,8 +3,6 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockTranslations = (hass: MockHomeAssistant) => {
hass.mockWS(
"frontend/get_translations",
(/* msg: {language: string, category: string} */) => {
return { resources: {} };
}
(/* msg: {language: string, category: string} */) => ({ resources: {} })
);
};

View File

@@ -1,7 +1,7 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { safeLoad } from "js-yaml";
import { load } from "js-yaml";
import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
class DemoCard extends PolymerElement {
@@ -15,6 +15,10 @@ class DemoCard extends PolymerElement {
margin: 0 0 20px;
color: var(--primary-color);
}
h2 small {
font-size: 0.5em;
color: var(--primary-text-color);
}
#card {
max-width: 400px;
width: 100vw;
@@ -34,7 +38,12 @@ class DemoCard extends PolymerElement {
}
}
</style>
<h2>[[config.heading]]</h2>
<h2>
[[config.heading]]
<template is="dom-if" if="[[_size]]">
<small>(size [[_size]])</small>
</template>
</h2>
<div class="root">
<div id="card"></div>
<template is="dom-if" if="[[showConfig]]">
@@ -55,6 +64,9 @@ class DemoCard extends PolymerElement {
observer: "_configChanged",
},
showConfig: Boolean,
_size: {
type: Number,
},
};
}
@@ -68,8 +80,19 @@ class DemoCard extends PolymerElement {
card.removeChild(card.lastChild);
}
const el = this._createCardElement(safeLoad(config.config)[0]);
const el = this._createCardElement(load(config.config)[0]);
card.appendChild(el);
this._getSize(el);
}
async _getSize(el) {
await customElements.whenDefined(el.localName);
if (!("getCardSize" in el)) {
this._size = undefined;
return;
}
this._size = await el.getCardSize();
}
_createCardElement(cardConfig) {

View File

@@ -1,12 +1,6 @@
import { safeDump } from "js-yaml";
import {
customElement,
html,
css,
LitElement,
TemplateResult,
property,
} from "lit-element";
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeAction } from "../../../src/data/script_i18n";
import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -62,7 +56,7 @@ export class DemoAutomationDescribeAction extends LitElement {
(conf) => html`
<div class="action">
<span>${describeAction(this.hass, conf as any)}</span>
<pre>${safeDump(conf)}</pre>
<pre>${dump(conf)}</pre>
</div>
`
)}

View File

@@ -1,11 +1,6 @@
import { safeDump } from "js-yaml";
import {
customElement,
html,
css,
LitElement,
TemplateResult,
} from "lit-element";
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeCondition } from "../../../src/data/automation_i18n";
@@ -31,7 +26,7 @@ export class DemoAutomationDescribeCondition extends LitElement {
(conf) => html`
<div class="condition">
<span>${describeCondition(conf as any)}</span>
<pre>${safeDump(conf)}</pre>
<pre>${dump(conf)}</pre>
</div>
`
)}

View File

@@ -1,11 +1,6 @@
import { safeDump } from "js-yaml";
import {
customElement,
html,
css,
LitElement,
TemplateResult,
} from "lit-element";
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeTrigger } from "../../../src/data/automation_i18n";
@@ -34,7 +29,7 @@ export class DemoAutomationDescribeTrigger extends LitElement {
(conf) => html`
<div class="trigger">
<span>${describeTrigger(conf as any)}</span>
<pre>${safeDump(conf)}</pre>
<pre>${dump(conf)}</pre>
</div>
`
)}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
css,
LitElement,
TemplateResult,
property,
} from "lit-element";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";

View File

@@ -1,12 +1,4 @@
import {
customElement,
html,
css,
LitElement,
TemplateResult,
internalProperty,
property,
} from "lit-element";
import { html, css, LitElement, TemplateResult } from "lit";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";
@@ -15,6 +7,7 @@ import { HomeAssistant } from "../../../src/types";
import { DemoTrace } from "../data/traces/types";
import { basicTrace } from "../data/traces/basic_trace";
import { motionLightTrace } from "../data/traces/motion-light-trace";
import { customElement, property, state } from "lit/decorators";
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
@@ -22,7 +15,7 @@ const traces: DemoTrace[] = [basicTrace, motionLightTrace];
export class DemoAutomationTrace extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
@internalProperty() private _selected = {};
@state() private _selected = {};
protected render(): TemplateResult {
if (!this.hass) {

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -93,4 +87,8 @@ class DemoAlarmPanelEntity extends LitElement {
}
}
customElements.define("demo-hui-alarm-panel-card", DemoAlarmPanelEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -75,4 +69,8 @@ class DemoConditional extends LitElement {
}
}
customElements.define("demo-hui-conditional-card", DemoConditional);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-conditional-card": DemoConditional;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -239,4 +233,8 @@ class DemoEntities extends LitElement {
}
}
customElements.define("demo-hui-entities-card", DemoEntities);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-entities-card": DemoEntities;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -91,4 +85,8 @@ class DemoButtonEntity extends LitElement {
}
}
customElements.define("demo-hui-entity-button-card", DemoButtonEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-entity-button-card": DemoButtonEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -132,4 +126,8 @@ class DemoEntityFilter extends LitElement {
}
}
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-entity-filter-card": DemoEntityFilter;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -129,4 +123,8 @@ class DemoGaugeEntity extends LitElement {
}
}
customElements.define("demo-hui-gauge-card", DemoGaugeEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-gauge-card": DemoGaugeEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -186,7 +180,7 @@ const CONFIGS = [
name:
- light.kitchen_lights
- entity: lock.kitchen_door
name:
name:
- light.ceiling_lights
`,
},
@@ -194,7 +188,7 @@ const CONFIGS = [
heading: "Custom tap action",
config: `
- type: glance
columns: 4
columns: 4
entities:
- entity: lock.kitchen_door
name: Custom
@@ -232,4 +226,8 @@ class DemoGlanceEntity extends LitElement {
}
}
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-glance-card": DemoGlanceEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -49,6 +43,110 @@ const ENTITIES = [
];
const CONFIGS = [
{
heading: "Default Grid",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Non-square Grid with 2 columns",
config: `
- type: grid
columns: 2
square: false
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
{
heading: "Default Grid with title",
config: `
- type: grid
title: Kitchen
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Columns 4",
config: `
- type: grid
columns: 4
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
{
heading: "Columns 2",
config: `
- type: grid
columns: 2
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
`,
},
{
heading: "Columns 1",
config: `
- type: grid
columns: 1
cards:
- type: entity
entity: light.kitchen_lights
`,
},
{
heading: "Size for single card",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
`,
},
{
heading: "Vertical Stack",
config: `
@@ -99,45 +197,9 @@ const CONFIGS = [
entity: light.bed_light
`,
},
{
heading: "Default Grid",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Non-square Grid with 2 columns",
config: `
- type: grid
columns: 2
square: false
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
];
@customElement("demo-hui-stack-card")
@customElement("demo-hui-grid-and-stack-card")
class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -155,4 +217,8 @@ class DemoStack extends LitElement {
}
}
customElements.define("demo-hui-stack-card", DemoStack);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-grid-and-stack-card": DemoStack;
}
}

View File

@@ -1,4 +1,5 @@
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import { html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../components/demo-cards";
const CONFIGS = [
@@ -42,4 +43,8 @@ class DemoIframe extends LitElement {
}
}
customElements.define("demo-hui-iframe-card", DemoIframe);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-iframe-card": DemoIframe;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -85,4 +79,8 @@ class DemoLightEntity extends LitElement {
}
}
customElements.define("demo-hui-light-card", DemoLightEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-light-card": DemoLightEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -183,4 +177,8 @@ class DemoMap extends LitElement {
}
}
customElements.define("demo-hui-map-card", DemoMap);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-map-card": DemoMap;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -276,4 +270,8 @@ class DemoMarkdown extends LitElement {
}
}
customElements.define("demo-hui-markdown-card", DemoMarkdown);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-markdown-card": DemoMarkdown;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -180,4 +174,8 @@ class DemoHuiMediaControlCard extends LitElement {
}
}
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-media-control-card": DemoHuiMediaControlCard;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -77,4 +71,8 @@ class DemoHuiMediaPlayerRow extends LitElement {
}
}
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -147,4 +141,8 @@ class DemoPictureElements extends LitElement {
}
}
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-picture-elements-card": DemoPictureElements;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -102,4 +96,8 @@ class DemoPictureEntity extends LitElement {
}
}
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-picture-entity-card": DemoPictureEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -143,4 +137,8 @@ class DemoPictureGlance extends LitElement {
}
}
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-picture-glance-card": DemoPictureGlance;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createPlantEntities } from "../data/plants";
@@ -52,4 +46,8 @@ export class DemoPlantEntity extends LitElement {
}
}
customElements.define("demo-hui-plant-card", DemoPlantEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-plant-card": DemoPlantEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -48,4 +42,8 @@ class DemoShoppingListEntity extends LitElement {
}
}
customElements.define("demo-hui-shopping-list-card", DemoShoppingListEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-shopping-list-card": DemoShoppingListEntity;
}
}

View File

@@ -1,11 +1,5 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -96,4 +90,8 @@ class DemoThermostatEntity extends LitElement {
}
}
customElements.define("demo-hui-thermostat-card", DemoThermostatEntity);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-thermostat-card": DemoThermostatEntity;
}
}

View File

@@ -1,12 +1,4 @@
import {
customElement,
html,
css,
internalProperty,
LitElement,
TemplateResult,
property,
} from "lit-element";
import { html, css, LitElement, TemplateResult } from "lit";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch";
@@ -23,7 +15,8 @@ import type {
} from "../../../src/panels/config/integrations/ha-config-integrations";
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
import { classMap } from "lit-html/directives/class-map";
import { classMap } from "lit/directives/class-map";
import { customElement, property, state } from "lit/decorators";
const createConfigEntry = (
title: string,
@@ -61,6 +54,9 @@ const nameAsDomainEntry = createConfigEntry("ESPHome");
const longNameEntry = createConfigEntry(
"Entry with a super long name that is going to the next line"
);
const longNonBreakingNameEntry = createConfigEntry(
"EntryWithASuperLongNameThatDoesNotBreak"
);
const configPanelEntry = createConfigEntry("Config Panel", {
domain: "mqtt",
localized_domain_name: "MQTT",
@@ -83,7 +79,8 @@ const setupRetryReasonEntry = createConfigEntry("Setup Retry", {
});
const setupRetryReasonMissingKeyEntry = createConfigEntry("Setup Retry", {
state: "setup_retry",
reason: "resolve_error",
reason:
"HTTPSConnectionpool: Max retries exceeded with NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x9eedfc10>: Failed to establish a new connection: [Errno 113] Host is unreachable')",
});
const failedUnloadEntry = createConfigEntry("Failed Unload", {
state: "failed_unload",
@@ -141,6 +138,7 @@ const configEntries: Array<{
{ items: [optionsFlowEntry] },
{ items: [nameAsDomainEntry] },
{ items: [longNameEntry] },
{ items: [longNonBreakingNameEntry] },
{ items: [setupErrorEntry] },
{ items: [migrationErrorEntry] },
{ items: [setupRetryEntry] },
@@ -154,6 +152,7 @@ const configEntries: Array<{
setupErrorEntry,
migrationErrorEntry,
longNameEntry,
longNonBreakingNameEntry,
setupRetryEntry,
failedUnloadEntry,
notLoadedEntry,
@@ -214,9 +213,9 @@ const createDeviceRegistryEntries = (
export class DemoIntegrationCard extends LitElement {
@property({ attribute: false }) hass?: HomeAssistant;
@internalProperty() isCustomIntegration = false;
@state() isCustomIntegration = false;
@internalProperty() isCloud = false;
@state() isCloud = false;
protected render(): TemplateResult {
if (!this.hass) {

View File

@@ -1,21 +1,11 @@
import {
customElement,
html,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../src/components/ha-card";
import {
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
LightColorModes,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity";
@@ -32,7 +22,8 @@ const ENTITIES = [
getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light",
brightness: 200,
supported_features: SUPPORT_BRIGHTNESS,
supported_color_modes: [LightColorModes.BRIGHTNESS],
color_mode: LightColorModes.BRIGHTNESS,
}),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
@@ -40,20 +31,96 @@ const ENTITIES = [
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
],
color_mode: LightColorModes.COLOR_TEMP,
}),
getEntity("light", "color_effectslight", "on", {
friendly_name: "Color Effets Light",
getEntity("light", "color_hs_light", "on", {
friendly_name: "Color HS Light",
brightness: 255,
hs_color: [30, 100],
white_value: 36,
supported_features:
SUPPORT_BRIGHTNESS +
SUPPORT_EFFECT +
SUPPORT_FLASH +
SUPPORT_COLOR +
SUPPORT_TRANSITION +
SUPPORT_WHITE_VALUE,
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.HS,
],
color_mode: LightColorModes.HS,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgb_ct_light", "on", {
friendly_name: "Color RGB + CT Light",
brightness: 255,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGB,
],
color_mode: LightColorModes.COLOR_TEMP,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_RGB_light", "on", {
friendly_name: "Color Effets Light",
brightness: 255,
rgb_color: [30, 100, 255],
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
color_mode: LightColorModes.RGB,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbw_light", "on", {
friendly_name: "Color RGBW Light",
brightness: 255,
rgbw_color: [30, 100, 255, 125],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGBW,
],
color_mode: LightColorModes.RGBW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbww_light", "on", {
friendly_name: "Color RGBWW Light",
brightness: 255,
rgbww_color: [30, 100, 255, 125, 10],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGBWW,
],
color_mode: LightColorModes.RGBWW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_xy_light", "on", {
friendly_name: "Color XY Light",
brightness: 255,
xy_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.XY,
],
color_mode: LightColorModes.XY,
effect_list: ["random", "colorloop"],
}),
];

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button";
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import { html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";

View File

@@ -1,12 +1,6 @@
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
import {
css,
CSSResultArray,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate";
@@ -47,9 +41,7 @@ class HassioAddonRepositoryEl extends LitElement {
const repo = this.repo;
let _addons = this.addons;
if (!this.hass.userData?.showAdvanced) {
_addons = _addons.filter((addon) => {
return !addon.advanced;
});
_addons = _addons.filter((addon) => !addon.advanced);
}
const addons = this._getAddons(_addons, this.filter);
@@ -68,9 +60,7 @@ class HassioAddonRepositoryEl extends LitElement {
}
return html`
<div class="content">
<h1>
${repo.name}
</h1>
<h1>${repo.name}</h1>
<div class="card-group">
${addons.map(
(addon) => html`
@@ -130,10 +120,10 @@ class HassioAddonRepositoryEl extends LitElement {
}
private _addonTapped(ev) {
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}`);
}
static get styles(): CSSResultArray {
static get styles(): CSSResultGroup {
return [
hassioStyle,
css`

View File

@@ -4,13 +4,13 @@ import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import {
css,
CSSResult,
internalProperty,
CSSResultGroup,
html,
LitElement,
property,
PropertyValues,
} from "lit-element";
import { html, TemplateResult } from "lit-html";
TemplateResult,
} from "lit";
import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -58,7 +58,7 @@ class HassioAddonStore extends LitElement {
@property({ attribute: false }) public route!: Route;
@internalProperty() private _filter?: string;
@state() private _filter?: string;
public async refreshData() {
await reloadHassioAddons(this.hass);
@@ -86,9 +86,7 @@ class HassioAddonStore extends LitElement {
main-page
supervisor
>
<span slot="header">
${this.supervisor.localize("panel.store")}
</span>
<span slot="header"> ${this.supervisor.localize("panel.store")} </span>
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@@ -140,7 +138,7 @@ class HassioAddonStore extends LitElement {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
const repositoryUrl = extractSearchParam("repository_url");
navigate(this, "/hassio/store", true);
navigate("/hassio/store", { replace: true });
if (repositoryUrl) {
this._manageRepositories(repositoryUrl);
}
@@ -154,8 +152,8 @@ class HassioAddonStore extends LitElement {
repositories: HassioAddonRepository[],
addons: HassioAddonInfo[],
filter?: string
) => {
return repositories.sort(sortRepos).map((repo) => {
) =>
repositories.sort(sortRepos).map((repo) => {
const filteredAddons = addons.filter(
(addon) => addon.repository === repo.slug
);
@@ -171,8 +169,7 @@ class HassioAddonStore extends LitElement {
></hassio-addon-repository>
`
: html``;
});
}
})
);
private _handleAction(ev: CustomEvent<ActionDetail>) {
@@ -221,7 +218,7 @@ class HassioAddonStore extends LitElement {
this._filter = e.detail.value;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
hassio-addon-repository {
margin-top: 24px;

View File

@@ -4,15 +4,13 @@ import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
css,
CSSResult,
customElement,
CSSResultGroup,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, state } from "lit/decorators";
import "web-animations-js/web-animations-next-lite.min";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card";
@@ -39,15 +37,15 @@ class HassioAddonAudio extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@state() private _error?: string;
@internalProperty() private _inputDevices?: HassioHardwareAudioDevice[];
@state() private _inputDevices?: HassioHardwareAudioDevice[];
@internalProperty() private _outputDevices?: HassioHardwareAudioDevice[];
@state() private _outputDevices?: HassioHardwareAudioDevice[];
@internalProperty() private _selectedInput!: null | string;
@state() private _selectedInput!: null | string;
@internalProperty() private _selectedOutput!: null | string;
@state() private _selectedOutput!: null | string;
protected render(): TemplateResult {
return html`
@@ -69,13 +67,13 @@ class HassioAddonAudio extends LitElement {
.selected=${this._selectedInput!}
>
${this._inputDevices &&
this._inputDevices.map((item) => {
return html`
this._inputDevices.map(
(item) => html`
<paper-item device=${item.device || ""}>
${item.name}
</paper-item>
`;
})}
`
)}
</paper-listbox>
</paper-dropdown-menu>
<paper-dropdown-menu
@@ -90,13 +88,13 @@ class HassioAddonAudio extends LitElement {
.selected=${this._selectedOutput!}
>
${this._outputDevices &&
this._outputDevices.map((item) => {
return html`
this._outputDevices.map(
(item) => html`
<paper-item device=${item.device || ""}
>${item.name}</paper-item
>
`;
})}
`
)}
</paper-listbox>
</paper-dropdown-menu>
</div>
@@ -109,7 +107,7 @@ class HassioAddonAudio extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -70,7 +63,7 @@ class HassioAddonConfigDashboard extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -3,18 +3,16 @@ import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import { DEFAULT_SCHEMA, Type } from "js-yaml";
import {
css,
CSSResult,
customElement,
CSSResultGroup,
html,
internalProperty,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
@@ -30,6 +28,7 @@ import {
HassioAddonDetails,
HassioAddonSetOptionParams,
setHassioAddonOption,
validateHassioAddonOption,
} from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -41,6 +40,13 @@ import { hassioStyle } from "../../resources/hassio-style";
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
new Type("!secret", {
kind: "scalar",
construct: (data) => `!secret ${data}`,
}),
]);
@customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@@ -53,31 +59,27 @@ class HassioAddonConfig extends LitElement {
@property({ type: Boolean }) private _valid = true;
@internalProperty() private _canShowSchema = false;
@state() private _canShowSchema = false;
@internalProperty() private _showOptional = false;
@state() private _showOptional = false;
@internalProperty() private _error?: string;
@state() private _error?: string;
@internalProperty() private _options?: Record<string, unknown>;
@state() private _options?: Record<string, unknown>;
@internalProperty() private _yamlMode = false;
@state() private _yamlMode = false;
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
public computeLabel = (entry: HaFormSchema): string => {
return (
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
?.name ||
this.addon.translations.en?.configuration?.[entry.name].name ||
entry.name
);
};
public computeLabel = (entry: HaFormSchema): string =>
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
?.name ||
this.addon.translations.en?.configuration?.[entry.name].name ||
entry.name;
private _filteredShchema = memoizeOne(
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
return schema.filter((entry) => entry.name in options || entry.required);
}
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
schema.filter((entry) => entry.name in options || entry.required)
);
protected render(): TemplateResult {
@@ -132,6 +134,7 @@ class HassioAddonConfig extends LitElement {
></ha-form>`
: html` <ha-yaml-editor
@value-changed=${this._configChanged}
.schema=${ADDON_YAML_SCHEMA}
></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode ||
@@ -266,36 +269,45 @@ class HassioAddonConfig extends LitElement {
private async _saveTapped(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
const eventdata = {
success: true,
response: undefined,
path: "options",
};
button.progress = true;
this._error = undefined;
try {
const validation = await validateHassioAddonOption(
this.hass,
this.addon.slug,
this._editor?.value
);
if (!validation.valid) {
throw Error(validation.message);
}
await setHassioAddonOption(this.hass, this.addon.slug, {
options: this._yamlMode ? this._editor?.value : this._options,
});
this._configHasChanged = false;
const eventdata = {
success: true,
response: undefined,
path: "options",
};
fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
this._error = this.supervisor.localize(
"addon.configuration.options.failed_to_save",
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
eventdata.success = false;
}
button.progress = false;
fireEvent(this, "hass-api-called", eventdata);
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,15 +1,13 @@
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
customElement,
CSSResultGroup,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card";
@@ -43,9 +41,9 @@ class HassioAddonNetwork extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@state() private _error?: string;
@internalProperty() private _config?: NetworkItem[];
@state() private _config?: NetworkItem[];
public connectedCallback(): void {
super.connectedCallback();
@@ -79,12 +77,10 @@ class HassioAddonNetwork extends LitElement {
"addon.configuration.network.host"
)}
</th>
<th>
${this.supervisor.localize("common.description")}
</th>
<th>${this.supervisor.localize("common.description")}</th>
</tr>
${this._config!.map((item) => {
return html`
${this._config!.map(
(item) => html`
<tr>
<td>${item.container}</td>
<td>
@@ -100,8 +96,8 @@ class HassioAddonNetwork extends LitElement {
</td>
<td>${this._computeDescription(item)}</td>
</tr>
`;
})}
`
)}
</tbody>
</table>
</div>
@@ -124,25 +120,20 @@ class HassioAddonNetwork extends LitElement {
}
}
private _computeDescription = (item: NetworkItem): string => {
return (
this.addon.translations[this.hass.language]?.network?.[item.container]
?.description ||
this.addon.translations.en?.network?.[item.container]?.description ||
item.description
);
};
private _computeDescription = (item: NetworkItem): string =>
this.addon.translations[this.hass.language]?.network?.[item.container]
?.description ||
this.addon.translations.en?.network?.[item.container]?.description ||
item.description;
private _setNetworkConfig(): void {
const network = this.addon.network || {};
const description = this.addon.network_description || {};
const items: NetworkItem[] = Object.keys(network).map((key) => {
return {
container: key,
host: network[key],
description: description[key],
};
});
const items: NetworkItem[] = Object.keys(network).map((key) => ({
container: key,
host: network[key],
description: description[key],
}));
this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1));
}
@@ -223,7 +214,7 @@ class HassioAddonNetwork extends LitElement {
button.progress = false;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,14 +1,5 @@
import "../../../../src/components/ha-card";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-markdown";
import {
@@ -21,6 +12,7 @@ import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { customElement, property, state } from "lit/decorators";
@customElement("hassio-addon-documentation-tab")
class HassioAddonDocumentationDashboard extends LitElement {
@@ -30,9 +22,9 @@ class HassioAddonDocumentationDashboard extends LitElement {
@property({ attribute: false }) public addon?: HassioAddonDetails;
@internalProperty() private _error?: string;
@state() private _error?: string;
@internalProperty() private _content?: string;
@state() private _content?: string;
public async connectedCallback(): Promise<void> {
super.connectedCallback();
@@ -57,7 +49,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -4,16 +4,8 @@ import {
mdiInformationVariant,
mdiMathLog,
} from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
@@ -52,7 +44,7 @@ class HassioAddonDashboard extends LitElement {
@property({ type: Boolean }) public narrow!: boolean;
@internalProperty() _error?: string;
@state() _error?: string;
private _computeTail = memoizeOne((route: Route) => {
const dividerPos = route.path.indexOf("/", 1);
@@ -133,7 +125,7 @@ class HassioAddonDashboard extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,
@@ -183,7 +175,7 @@ class HassioAddonDashboard extends LitElement {
if (!validAddon) {
this._error = this.supervisor.localize("my.error_addon_not_found");
} else {
navigate(this, `/hassio/addon/${requestedAddon}`, true);
navigate(`/hassio/addon/${requestedAddon}`, { replace: true });
}
}
}
@@ -191,6 +183,10 @@ class HassioAddonDashboard extends LitElement {
}
private async _apiCalled(ev): Promise<void> {
if (!ev.detail.success) {
return;
}
const pathSplit: string[] = ev.detail.path?.split("/");
if (!pathSplit || pathSplit.length === 0) {

View File

@@ -1,4 +1,4 @@
import { customElement, property } from "lit-element";
import { customElement, property } from "lit/decorators";
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import {

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -42,7 +35,7 @@ class HassioAddonInfoDashboard extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -14,17 +14,9 @@ import {
mdiPound,
mdiShield,
} from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event";
@@ -90,9 +82,9 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
@state() private _metrics?: HassioStats;
@internalProperty() private _error?: string;
@state() private _error?: string;
private _addonStoreInfo = memoizeOne(
(slug: string, storeAddons: StoreAddon[]) =>
@@ -171,16 +163,16 @@ class HassioAddonInfo extends LitElement {
: ""}
</div>
<div class="card-actions">
<mwc-button @click=${this._updateClicked}>
${this.supervisor.localize("common.update")}
</mwc-button>
${this.addon.changelog
? html`
<mwc-button @click=${this._openChangelog}>
${this.supervisor.localize("addon.dashboard.changelog")}
</mwc-button>
`
: ""}
: html`<span></span>`}
<mwc-button @click=${this._updateClicked}>
${this.supervisor.localize("common.update")}
</mwc-button>
</div>
</ha-card>
`
@@ -261,13 +253,9 @@ class HassioAddonInfo extends LitElement {
${this.supervisor.localize(
"addon.dashboard.visit_addon_page",
"name",
html`<a
href="${this.addon.url!}"
target="_blank"
rel="noreferrer"
>
${this.addon.name}
</a>`
html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer"
>${this.addon.name}</a
>`
)}
</div>
<div class="addon-container">
@@ -566,9 +554,7 @@ class HassioAddonInfo extends LitElement {
<span slot="heading">
${this.supervisor.localize("addon.dashboard.hostname")}
</span>
<code slot="description">
${this.addon.hostname}
</code>
<code slot="description"> ${this.addon.hostname} </code>
</ha-settings-row>
${metrics.map(
(metric) =>
@@ -775,7 +761,7 @@ class HassioAddonInfo extends LitElement {
}
private _openIngress(): void {
navigate(this, `/hassio/ingress/${this.addon.slug}`);
navigate(`/hassio/ingress/${this.addon.slug}`);
}
private get _computeShowIngressUI(): boolean {
@@ -997,7 +983,7 @@ class HassioAddonInfo extends LitElement {
addons: [this.addon.slug],
homeassistant: false,
},
updateHandler: async () => await this._updateAddon(),
updateHandler: async () => this._updateAddon(),
});
}
@@ -1065,7 +1051,7 @@ class HassioAddonInfo extends LitElement {
}
private _openConfiguration(): void {
navigate(this, `/hassio/addon/${this.addon.slug}/config`);
navigate(`/hassio/addon/${this.addon.slug}/config`);
}
private async _uninstallClicked(ev: CustomEvent): Promise<void> {
@@ -1104,7 +1090,7 @@ class HassioAddonInfo extends LitElement {
button.progress = false;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -38,7 +31,7 @@ class HassioAddonLogDashboard extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,14 +1,6 @@
import "@material/mwc-button";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-card";
import {
fetchHassioAddonLogs,
@@ -29,9 +21,9 @@ class HassioAddonLogs extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@state() private _error?: string;
@internalProperty() private _content?: string;
@state() private _content?: string;
public async connectedCallback(): Promise<void> {
super.connectedCallback();
@@ -59,7 +51,7 @@ class HassioAddonLogs extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
interface State {
bold: boolean;
@@ -25,7 +18,7 @@ class HassioAnsiToHtml extends LitElement {
return html`${this._parseTextToColoredPre(this.content)}`;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
pre {
overflow-x: auto;

View File

@@ -1,13 +1,6 @@
import { mdiHelpCircle } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-relative-time";
import "../../../src/components/ha-svg-icon";
import { HomeAssistant } from "../../../src/types";
@@ -56,13 +49,13 @@ class HassioCardContent extends LitElement {
></ha-svg-icon>
`}
<div>
<div class="title">
${this.title}
</div>
<div class="title">${this.title}</div>
<div class="addition">
${this.description}
${/* treat as available when undefined */
this.available === false ? " (Not available)" : ""}
${
/* treat as available when undefined */
this.available === false ? " (Not available)" : ""
}
${this.datetime
? html`
<ha-relative-time
@@ -77,7 +70,7 @@ class HassioCardContent extends LitElement {
`;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
ha-svg-icon {
margin-right: 24px;

View File

@@ -2,13 +2,8 @@ import "@material/mwc-icon-button/mwc-icon-button";
import { mdiFolderUpload } from "@mdi/js";
import "@polymer/iron-input/iron-input";
import "@polymer/paper-input/paper-input-container";
import {
customElement,
html,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-circular-progress";
import "../../../src/components/ha-file-upload";
@@ -33,9 +28,9 @@ const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
export class HassioUploadSnapshot extends LitElement {
public hass!: HomeAssistant;
@internalProperty() public value: string | null = null;
@state() public value: string | null = null;
@internalProperty() private _uploading = false;
@state() private _uploading = false;
public render(): TemplateResult {
return html`

View File

@@ -0,0 +1,55 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-svg-icon";
@customElement("supervisor-formfield-label")
class SupervisorFormfieldLabel extends LitElement {
@property({ type: String }) public label!: string;
@property({ type: String }) public imageUrl?: string;
@property({ type: String }) public iconPath?: string;
@property({ type: String }) public version?: string;
protected render(): TemplateResult {
return html`
${this.imageUrl
? html`<img loading="lazy" .src=${this.imageUrl} class="icon" />`
: this.iconPath
? 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>`
: ""}
`;
}
static get styles(): CSSResultGroup {
return css`
:host {
cursor: pointer;
display: flex;
align-items: center;
}
.label {
margin-right: 4px;
}
.version {
color: var(--secondary-text-color);
}
.icon {
max-height: 22px;
max-width: 22px;
margin-right: 8px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-formfield-label": SupervisorFormfieldLabel;
}
}

View File

@@ -1,13 +1,6 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate";
@@ -23,13 +16,9 @@ class SupervisorMetric extends LitElement {
protected render(): TemplateResult {
const roundedValue = roundWithOneDecimal(this.value);
return html`<ha-settings-row>
<span slot="heading">
${this.description}
</span>
<span slot="heading"> ${this.description} </span>
<div slot="description" .title=${this.tooltip ?? ""}>
<span class="value">
${roundedValue} %
</span>
<span class="value"> ${roundedValue} % </span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
@@ -41,7 +30,7 @@ class SupervisorMetric extends LitElement {
</ha-settings-row>`;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
ha-settings-row {
padding: 0;
@@ -75,6 +64,7 @@ class SupervisorMetric extends LitElement {
.value {
width: 48px;
padding-right: 4px;
flex-shrink: 0;
}
`;
}

View File

@@ -0,0 +1,418 @@
import { mdiFolder, mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import { formatDate } from "../../../src/common/datetime/format_date";
import { formatDateTime } from "../../../src/common/datetime/format_date_time";
import "../../../src/components/ha-checkbox";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-radio";
import type { HaRadio } from "../../../src/components/ha-radio";
import {
HassioFullSnapshotCreateParams,
HassioPartialSnapshotCreateParams,
HassioSnapshotDetail,
} from "../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { PolymerChangedEvent } from "../../../src/polymer-types";
import { HomeAssistant } from "../../../src/types";
import "./supervisor-formfield-label";
interface CheckboxItem {
slug: string;
checked: boolean;
name: string;
}
interface AddonCheckboxItem extends CheckboxItem {
version: string;
}
const _computeFolders = (folders): CheckboxItem[] => {
const list: CheckboxItem[] = [];
if (folders.includes("homeassistant")) {
list.push({
slug: "homeassistant",
name: "Home Assistant configuration",
checked: false,
});
}
if (folders.includes("ssl")) {
list.push({ slug: "ssl", name: "SSL", checked: false });
}
if (folders.includes("share")) {
list.push({ slug: "share", name: "Share", checked: false });
}
if (folders.includes("addons/local")) {
list.push({ slug: "addons/local", name: "Local add-ons", checked: false });
}
return list.sort((a, b) => (a.name > b.name ? 1 : -1));
};
const _computeAddons = (addons): AddonCheckboxItem[] =>
addons
.map((addon) => ({
slug: addon.slug,
name: addon.name,
version: addon.version,
checked: false,
}))
.sort((a, b) => (a.name > b.name ? 1 : -1));
@customElement("supervisor-snapshot-content")
export class SupervisorSnapshotContent extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor?: Supervisor;
@property({ attribute: false }) public snapshot?: HassioSnapshotDetail;
@property() public snapshotType: HassioSnapshotDetail["type"] = "full";
@property({ attribute: false }) public folders?: CheckboxItem[];
@property({ attribute: false }) public addons?: AddonCheckboxItem[];
@property({ type: Boolean }) public homeAssistant = false;
@property({ type: Boolean }) public snapshotHasPassword = false;
@property() public snapshotName = "";
@property() public snapshotPassword = "";
public willUpdate(changedProps) {
super.willUpdate(changedProps);
if (!this.hasUpdated) {
this.folders = _computeFolders(
this.snapshot
? this.snapshot.folders
: ["homeassistant", "ssl", "share", "media", "addons/local"]
);
this.addons = _computeAddons(
this.snapshot
? this.snapshot.addons
: this.supervisor?.supervisor.addons
);
this.snapshotType = this.snapshot?.type || "full";
this.snapshotName = this.snapshot?.name || "";
this.snapshotHasPassword = this.snapshot?.protected || false;
}
}
protected render(): TemplateResult {
if (!this.supervisor) {
return html``;
}
const foldersSection =
this.snapshotType === "partial" ? this._getSection("folders") : undefined;
const addonsSection =
this.snapshotType === "partial" ? this._getSection("addons") : undefined;
return html`
${this.snapshot
? html`<div class="details">
${this.snapshot.type === "full"
? this.supervisor.localize("snapshot.full_snapshot")
: this.supervisor.localize("snapshot.partial_snapshot")}
(${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br />
${formatDateTime(new Date(this.snapshot.date), this.hass.locale)}
</div>`
: html`<paper-input
name="snapshotName"
.label=${this.supervisor.localize("snapshot.name")}
.value=${this.snapshotName}
@value-changed=${this._handleTextValueChanged}
>
</paper-input>`}
${!this.snapshot || this.snapshot.type === "full"
? html`<div class="sub-header">
${!this.snapshot
? this.supervisor.localize("snapshot.type")
: this.supervisor.localize("snapshot.select_type")}
</div>
<div class="snapshot-types">
<ha-formfield
.label=${this.supervisor.localize("snapshot.full_snapshot")}
>
<ha-radio
@change=${this._handleRadioValueChanged}
value="full"
name="snapshotType"
.checked=${this.snapshotType === "full"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.supervisor!.localize("snapshot.partial_snapshot")}
>
<ha-radio
@change=${this._handleRadioValueChanged}
value="partial"
name="snapshotType"
.checked=${this.snapshotType === "partial"}
>
</ha-radio>
</ha-formfield>
</div>`
: ""}
${this.snapshot && this.snapshotType === "partial"
? html`
${this.snapshot.homeassistant
? html`
<ha-formfield
.label=${html`<supervisor-formfield-label
label="Home Assistant"
.iconPath=${mdiHomeAssistant}
.version=${this.snapshot.homeassistant}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
.checked=${this.homeAssistant}
@click=${() => {
this.homeAssistant = !this.homeAssistant;
}}
>
</ha-checkbox>
</ha-formfield>
`
: ""}
`
: ""}
${this.snapshotType === "partial"
? html`
${foldersSection?.templates.length
? html`
<ha-formfield
.label=${html`<supervisor-formfield-label
.label=${this.supervisor.localize("snapshot.folders")}
.iconPath=${mdiFolder}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
@change=${this._toggleSection}
.checked=${foldersSection.checked}
.indeterminate=${foldersSection.indeterminate}
.section=${"folders"}
>
</ha-checkbox>
</ha-formfield>
<div class="section-content">${foldersSection.templates}</div>
`
: ""}
${addonsSection?.templates.length
? html`
<ha-formfield
.label=${html`<supervisor-formfield-label
.label=${this.supervisor.localize("snapshot.addons")}
.iconPath=${mdiPuzzle}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
@change=${this._toggleSection}
.checked=${addonsSection.checked}
.indeterminate=${addonsSection.indeterminate}
.section=${"addons"}
>
</ha-checkbox>
</ha-formfield>
<div class="section-content">${addonsSection.templates}</div>
`
: ""}
`
: ""}
${!this.snapshot
? html`<ha-formfield
.label=${this.supervisor.localize("snapshot.password_protection")}
>
<ha-checkbox
.checked=${this.snapshotHasPassword}
@change=${this._toggleHasPassword}
>
</ha-checkbox
></ha-formfield>`
: ""}
${this.snapshotHasPassword
? html`
<paper-input
.label=${this.supervisor.localize("snapshot.password")}
type="password"
name="snapshotPassword"
.value=${this.snapshotPassword}
@value-changed=${this._handleTextValueChanged}
>
</paper-input>
`
: ""}
`;
}
static get styles(): CSSResultGroup {
return css`
ha-checkbox {
--mdc-checkbox-touch-target-size: 16px;
display: block;
margin: 4px 12px 8px 0;
}
ha-formfield {
display: contents;
}
supervisor-formfield-label {
display: inline-flex;
align-items: center;
}
paper-input[type="password"] {
display: block;
margin: 4px 0 4px 16px;
}
.details {
color: var(--secondary-text-color);
}
.section-content {
display: flex;
flex-direction: column;
margin-left: 16px;
}
.security {
margin-top: 16px;
}
.snapshot-types {
display: flex;
}
.sub-header {
margin-top: 8px;
}
`;
}
public snapshotDetails():
| HassioPartialSnapshotCreateParams
| HassioFullSnapshotCreateParams {
const data: any = {};
if (!this.snapshot) {
data.name = this.snapshotName || formatDate(new Date(), this.hass.locale);
}
if (this.snapshotHasPassword) {
data.password = this.snapshotPassword;
}
if (this.snapshotType === "full") {
return data;
}
const addons = this.addons
?.filter((addon) => addon.checked)
.map((addon) => addon.slug);
const folders = this.folders
?.filter((folder) => folder.checked)
.map((folder) => folder.slug);
if (addons?.length) {
data.addons = addons;
}
if (folders?.length) {
data.folders = folders;
}
if (this.homeAssistant) {
data.homeassistant = this.homeAssistant;
}
return data;
}
private _getSection(section: string) {
const templates: TemplateResult[] = [];
const addons =
section === "addons"
? new Map(
this.supervisor!.addon.addons.map((item) => [item.slug, item])
)
: undefined;
let checkedItems = 0;
this[section].forEach((item) => {
templates.push(html`<ha-formfield
.label=${html`<supervisor-formfield-label
.label=${item.name}
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
.imageUrl=${section === "addons" &&
atLeastVersion(this.hass.config.version, 0, 105) &&
addons?.get(item.slug)?.icon
? `/api/hassio/addons/${item.slug}/icon`
: undefined}
.version=${item.version}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
.item=${item}
.checked=${item.checked}
.section=${section}
@change=${this._updateSectionEntry}
>
</ha-checkbox>
</ha-formfield>`);
if (item.checked) {
checkedItems++;
}
});
const checked = checkedItems === this[section].length;
return {
templates,
checked,
indeterminate: !checked && checkedItems !== 0,
};
}
private _handleRadioValueChanged(ev: CustomEvent) {
const input = ev.currentTarget as HaRadio;
this[input.name] = input.value;
}
private _handleTextValueChanged(ev: PolymerChangedEvent<string>) {
const input = ev.currentTarget as PaperInputElement;
this[input.name!] = ev.detail.value;
}
private _toggleHasPassword(): void {
this.snapshotHasPassword = !this.snapshotHasPassword;
}
private _toggleSection(ev): void {
const section = ev.currentTarget.section;
this[section] = (section === "addons" ? this.addons : this.folders)!.map(
(item) => ({
...item,
checked: ev.currentTarget.checked,
})
);
}
private _updateSectionEntry(ev): void {
const item = ev.currentTarget.item;
const section = ev.currentTarget.section;
this[section] = this[section].map((entry) =>
entry.slug === item.slug
? {
...entry,
checked: ev.currentTarget.checked,
}
: entry
);
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-snapshot-content": SupervisorSnapshotContent;
}
}

View File

@@ -1,13 +1,6 @@
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate";
import { compare } from "../../../src/common/string/compare";
@@ -90,7 +83,7 @@ class HassioAddons extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,
@@ -103,11 +96,11 @@ class HassioAddons extends LitElement {
}
private _addonTapped(ev: any): void {
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}/info`);
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}/info`);
}
private _openStore(): void {
navigate(this, "/hassio/store");
navigate("/hassio/store");
}
}

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles";
@@ -53,7 +46,7 @@ class HassioDashboard extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
css`

View File

@@ -1,14 +1,7 @@
import "@material/mwc-button";
import { mdiHomeAssistant } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -40,9 +33,8 @@ import { HomeAssistant } from "../../../src/types";
import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
import { hassioStyle } from "../resources/hassio-style";
const computeVersion = (key: string, version: string): string => {
return key === "os" ? version : `${key}-${version}`;
};
const computeVersion = (key: string, version: string): string =>
key === "os" ? version : `${key}-${version}`;
@customElement("hassio-update")
export class HassioUpdate extends LitElement {
@@ -50,11 +42,12 @@ export class HassioUpdate extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
private _pendingUpdates = memoizeOne((supervisor: Supervisor): number => {
return Object.keys(supervisor).filter(
(value) => supervisor[value].update_available
).length;
});
private _pendingUpdates = memoizeOne(
(supervisor: Supervisor): number =>
Object.keys(supervisor).filter(
(value) => supervisor[value].update_available
).length
);
protected render(): TemplateResult {
if (!this.supervisor) {
@@ -233,7 +226,7 @@ export class HassioUpdate extends LitElement {
});
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,13 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-markdown";
import { haStyleDialog } from "../../../../src/resources/styles";
@@ -23,7 +15,7 @@ class HassioMarkdownDialog extends LitElement {
@property() public content!: string;
@internalProperty() private _opened = false;
@state() private _opened = false;
public showDialog(params: HassioMarkdownDialogParams) {
this.title = params.title;
@@ -50,7 +42,7 @@ class HassioMarkdownDialog extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyleDialog,
hassioStyle,

View File

@@ -6,17 +6,9 @@ import "@material/mwc-tab";
import "@material/mwc-tab-bar";
import { mdiClose } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { cache } from "lit-html/directives/cache";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog";
@@ -47,38 +39,39 @@ import { HassioNetworkDialogParams } from "./show-dialog-network";
const IP_VERSIONS = ["ipv4", "ipv6"];
@customElement("dialog-hassio-network")
export class DialogHassioNetwork extends LitElement
export class DialogHassioNetwork
extends LitElement
implements HassDialog<HassioNetworkDialogParams> {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _accessPoints?: AccessPoints;
@state() private _accessPoints?: AccessPoints;
@internalProperty() private _curTabIndex = 0;
@state() private _curTabIndex = 0;
@internalProperty() private _dirty = false;
@state() private _dirty = false;
@internalProperty() private _interface?: NetworkInterface;
@state() private _interface?: NetworkInterface;
@internalProperty() private _interfaces!: NetworkInterface[];
@state() private _interfaces!: NetworkInterface[];
@internalProperty() private _params?: HassioNetworkDialogParams;
@state() private _params?: HassioNetworkDialogParams;
@internalProperty() private _processing = false;
@state() private _processing = false;
@internalProperty() private _scanning = false;
@state() private _scanning = false;
@internalProperty() private _wifiConfiguration?: WifiConfiguration;
@state() private _wifiConfiguration?: WifiConfiguration;
public async showDialog(params: HassioNetworkDialogParams): Promise<void> {
this._params = params;
this._dirty = false;
this._curTabIndex = 0;
this.supervisor = params.supervisor;
this._interfaces = params.supervisor.network.interfaces.sort((a, b) => {
return a.primary > b.primary ? -1 : 1;
});
this._interfaces = params.supervisor.network.interfaces.sort((a, b) =>
a.primary > b.primary ? -1 : 1
);
this._interface = { ...this._interfaces[this._curTabIndex] };
await this.updateComplete;
@@ -542,7 +535,7 @@ export class DialogHassioNetwork extends LitElement
this._wifiConfiguration![id] = value;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`

View File

@@ -3,16 +3,8 @@ import "@material/mwc-icon-button/mwc-icon-button";
import "@material/mwc-list/mwc-list-item";
import { mdiDelete } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-circular-progress";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-svg-icon";
@@ -39,21 +31,21 @@ class HassioRegistriesDialog extends LitElement {
username: string;
}[];
@internalProperty() private _registry?: string;
@state() private _registry?: string;
@internalProperty() private _username?: string;
@state() private _username?: string;
@internalProperty() private _password?: string;
@state() private _password?: string;
@internalProperty() private _opened = false;
@state() private _opened = false;
@internalProperty() private _addingRegistry = false;
@state() private _addingRegistry = false;
protected render(): TemplateResult {
return html`
<ha-dialog
.open=${this._opened}
@closing=${this.closeDialog}
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
@@ -108,8 +100,8 @@ class HassioRegistriesDialog extends LitElement {
</mwc-button>
`
: html`${this._registries?.length
? this._registries.map((entry) => {
return html`
? this._registries.map(
(entry) => html`
<mwc-list-item class="option" hasMeta twoline>
<span>${entry.registry}</span>
<span slot="secondary"
@@ -129,8 +121,8 @@ class HassioRegistriesDialog extends LitElement {
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button>
</mwc-list-item>
`;
})
`
)
: html`
<mwc-list-item>
<span
@@ -220,7 +212,7 @@ class HassioRegistriesDialog extends LitElement {
}
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
@@ -252,9 +244,6 @@ class HassioRegistriesDialog extends LitElement {
mwc-list-item span[slot="secondary"] {
color: var(--secondary-text-color);
}
ha-paper-dropdown-menu {
display: block;
}
`,
];
}

View File

@@ -5,17 +5,8 @@ import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
query,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress";
@@ -37,15 +28,15 @@ class HassioRepositoriesDialog extends LitElement {
@query("#repository_input", true) private _optionInput?: PaperInputElement;
@internalProperty() private _repositories?: HassioAddonRepository[];
@state() private _repositories?: HassioAddonRepository[];
@internalProperty() private _dialogParams?: HassioRepositoryDialogParams;
@state() private _dialogParams?: HassioRepositoryDialogParams;
@internalProperty() private _opened = false;
@state() private _opened = false;
@internalProperty() private _prosessing = false;
@state() private _processing = false;
@internalProperty() private _error?: string;
@state() private _error?: string;
public async showDialog(
dialogParams: HassioRepositoryDialogParams
@@ -76,7 +67,7 @@ class HassioRepositoriesDialog extends LitElement {
return html`
<ha-dialog
.open=${this._opened}
@closing=${this.closeDialog}
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
@@ -87,8 +78,8 @@ class HassioRepositoriesDialog extends LitElement {
${this._error ? html`<div class="error">${this._error}</div>` : ""}
<div class="form">
${repositories.length
? repositories.map((repo) => {
return html`
? repositories.map(
(repo) => html`
<paper-item class="option">
<paper-item-body three-line>
<div>${repo.name}</div>
@@ -105,13 +96,9 @@ class HassioRepositoriesDialog extends LitElement {
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button>
</paper-item>
`;
})
: html`
<paper-item>
No repositories
</paper-item>
`}
`
)
: html` <paper-item> No repositories </paper-item> `}
<div class="layout horizontal bottom">
<paper-input
class="flex-auto"
@@ -123,8 +110,11 @@ class HassioRepositoriesDialog extends LitElement {
@keydown=${this._handleKeyAdd}
></paper-input>
<mwc-button @click=${this._addRepository}>
${this._prosessing
? html`<ha-circular-progress active></ha-circular-progress>`
${this._processing
? html`<ha-circular-progress
active
size="small"
></ha-circular-progress>`
: this._dialogParams!.supervisor.localize(
"dialog.repositories.add"
)}
@@ -138,7 +128,7 @@ class HassioRepositoriesDialog extends LitElement {
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
@@ -160,9 +150,6 @@ class HassioRepositoriesDialog extends LitElement {
mwc-button {
margin-left: 8px;
}
ha-paper-dropdown-menu {
display: block;
}
ha-circular-progress {
display: block;
margin: 32px;
@@ -205,11 +192,9 @@ class HassioRepositoriesDialog extends LitElement {
if (!input || !input.value) {
return;
}
this._prosessing = true;
this._processing = true;
const repositories = this._filteredRepositories(this._repositories!);
const newRepositories = repositories.map((repo) => {
return repo.source;
});
const newRepositories = repositories.map((repo) => repo.source);
newRepositories.push(input.value);
try {
@@ -222,25 +207,19 @@ class HassioRepositoriesDialog extends LitElement {
} catch (err) {
this._error = extractApiErrorMessage(err);
}
this._prosessing = false;
this._processing = false;
}
private async _removeRepository(ev: Event) {
const slug = (ev.currentTarget as any).slug;
const repositories = this._filteredRepositories(this._repositories!);
const repository = repositories.find((repo) => {
return repo.slug === slug;
});
const repository = repositories.find((repo) => repo.slug === slug);
if (!repository) {
return;
}
const newRepositories = repositories
.map((repo) => {
return repo.source;
})
.filter((repo) => {
return repo !== repository.source;
});
.map((repo) => repo.source)
.filter((repo) => repo !== repository.source);
try {
await setSupervisorOption(this.hass, {

View File

@@ -0,0 +1,142 @@
import "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
createHassioFullSnapshot,
createHassioPartialSnapshot,
} from "../../../../src/data/hassio/snapshot";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import "../../components/supervisor-snapshot-content";
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot";
@customElement("dialog-hassio-create-snapshot")
class HassioCreateSnapshotDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _dialogParams?: HassioCreateSnapshotDialogParams;
@state() private _error?: string;
@state() private _creatingSnapshot = false;
@query("supervisor-snapshot-content")
private _snapshotContent!: SupervisorSnapshotContent;
public showDialog(params: HassioCreateSnapshotDialogParams) {
this._dialogParams = params;
this._creatingSnapshot = false;
}
public closeDialog() {
this._dialogParams = undefined;
this._creatingSnapshot = false;
this._error = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._dialogParams) {
return html``;
}
return html`
<ha-dialog
open
scrimClickAction
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this._dialogParams.supervisor.localize("snapshot.create_snapshot")
)}
>
${this._creatingSnapshot
? html` <ha-circular-progress active></ha-circular-progress>`
: html`<supervisor-snapshot-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}
>
</supervisor-snapshot-content>`}
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this._dialogParams.supervisor.localize("common.close")}
</mwc-button>
<mwc-button
.disabled=${this._creatingSnapshot}
slot="primaryAction"
@click=${this._createSnapshot}
>
${this._dialogParams.supervisor.localize("snapshot.create")}
</mwc-button>
</ha-dialog>
`;
}
private async _createSnapshot(): Promise<void> {
if (this._dialogParams!.supervisor.info.state !== "running") {
showAlertDialog(this, {
title: this._dialogParams!.supervisor.localize(
"snapshot.could_not_create"
),
text: this._dialogParams!.supervisor.localize(
"snapshot.create_blocked_not_running",
"state",
this._dialogParams!.supervisor.info.state
),
});
return;
}
const snapshotDetails = this._snapshotContent.snapshotDetails();
this._creatingSnapshot = true;
this._error = "";
if (
this._snapshotContent.snapshotHasPassword &&
!this._snapshotContent.snapshotPassword.length
) {
this._error = this._dialogParams!.supervisor.localize(
"snapshot.enter_password"
);
this._creatingSnapshot = false;
return;
}
try {
if (this._snapshotContent.snapshotType === "full") {
await createHassioFullSnapshot(this.hass, snapshotDetails);
} else {
await createHassioPartialSnapshot(this.hass, snapshotDetails);
}
this._dialogParams!.onCreate();
this.closeDialog();
} catch (err) {
this._error = extractApiErrorMessage(err);
}
this._creatingSnapshot = false;
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-circular-progress {
display: block;
text-align: center;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-hassio-create-snapshot": HassioCreateSnapshotDialog;
}
}

View File

@@ -1,14 +1,6 @@
import { mdiClose } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-header-bar";
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
@@ -18,11 +10,12 @@ import "../../components/hassio-upload-snapshot";
import { HassioSnapshotUploadDialogParams } from "./show-dialog-snapshot-upload";
@customElement("dialog-hassio-snapshot-upload")
export class DialogHassioSnapshotUpload extends LitElement
export class DialogHassioSnapshotUpload
extends LitElement
implements HassDialog<HassioSnapshotUploadDialogParams> {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _params?: HassioSnapshotUploadDialogParams;
@state() private _params?: HassioSnapshotUploadDialogParams;
public async showDialog(
params: HassioSnapshotUploadDialogParams
@@ -57,9 +50,7 @@ export class DialogHassioSnapshotUpload extends LitElement
>
<div slot="heading">
<ha-header-bar>
<span slot="title">
Upload snapshot
</span>
<span slot="title"> Upload snapshot </span>
<mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
@@ -79,7 +70,7 @@ export class DialogHassioSnapshotUpload extends LitElement
this.closeDialog();
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`

View File

@@ -1,20 +1,13 @@
import "@material/mwc-button";
import { mdiClose, mdiDelete, mdiDownload, mdiHistory } from "@mdi/js";
import "@polymer/paper-checkbox/paper-checkbox";
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
import "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-header-bar";
import { slugify } from "../../../../src/common/string/slugify";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-button-menu";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-svg-icon";
import { getSignedPath } from "../../../../src/data/auth";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@@ -22,96 +15,46 @@ import {
fetchHassioSnapshotInfo,
HassioSnapshotDetail,
} from "../../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../../src/dialogs/generic/show-dialog-box";
import { PolymerChangedEvent } from "../../../../src/polymer-types";
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import "../../components/supervisor-snapshot-content";
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
import { HassioSnapshotDialogParams } from "./show-dialog-hassio-snapshot";
const _computeFolders = (folders) => {
const list: Array<{ slug: string; name: string; checked: boolean }> = [];
if (folders.includes("homeassistant")) {
list.push({
slug: "homeassistant",
name: "Home Assistant configuration",
checked: true,
});
}
if (folders.includes("ssl")) {
list.push({ slug: "ssl", name: "SSL", checked: true });
}
if (folders.includes("share")) {
list.push({ slug: "share", name: "Share", checked: true });
}
if (folders.includes("addons/local")) {
list.push({ slug: "addons/local", name: "Local add-ons", checked: true });
}
return list;
};
const _computeAddons = (addons) => {
return addons.map((addon) => ({
slug: addon.slug,
name: addon.name,
version: addon.version,
checked: true,
}));
};
interface AddonItem {
slug: string;
name: string;
version: string;
checked: boolean | null | undefined;
}
interface FolderItem {
slug: string;
name: string;
checked: boolean | null | undefined;
}
@customElement("dialog-hassio-snapshot")
class HassioSnapshotDialog extends LitElement {
class HassioSnapshotDialog
extends LitElement
implements HassDialog<HassioSnapshotDialogParams> {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor?: Supervisor;
@state() private _error?: string;
@internalProperty() private _error?: string;
@state() private _snapshot?: HassioSnapshotDetail;
@internalProperty() private _onboarding = false;
@state() private _dialogParams?: HassioSnapshotDialogParams;
@internalProperty() private _snapshot?: HassioSnapshotDetail;
@state() private _restoringSnapshot = false;
@internalProperty() private _folders!: FolderItem[];
@internalProperty() private _addons!: AddonItem[];
@internalProperty() private _dialogParams?: HassioSnapshotDialogParams;
@internalProperty() private _snapshotPassword!: string;
@internalProperty() private _restoreHass = true;
@query("supervisor-snapshot-content")
private _snapshotContent!: SupervisorSnapshotContent;
public async showDialog(params: HassioSnapshotDialogParams) {
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
this._folders = _computeFolders(
this._snapshot?.folders
).sort((a: FolderItem, b: FolderItem) => (a.name > b.name ? 1 : -1));
this._addons = _computeAddons(
this._snapshot?.addons
).sort((a: AddonItem, b: AddonItem) => (a.name > b.name ? 1 : -1));
this._dialogParams = params;
this._onboarding = params.onboarding ?? false;
this.supervisor = params.supervisor;
if (!this._snapshot.homeassistant) {
this._restoreHass = false;
}
this._restoringSnapshot = false;
}
public closeDialog() {
this._snapshot = undefined;
this._dialogParams = undefined;
this._restoringSnapshot = false;
this._error = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
@@ -119,204 +62,92 @@ class HassioSnapshotDialog extends LitElement {
return html``;
}
return html`
<ha-dialog open @closing=${this._closeDialog} .heading=${true}>
<div slot="heading">
<ha-header-bar>
<span slot="title">
${this._computeName}
</span>
<mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
</ha-header-bar>
</div>
<div class="details">
${this._snapshot.type === "full"
? "Full snapshot"
: "Partial snapshot"}
(${this._computeSize})<br />
${this._formatDatetime(this._snapshot.date)}
</div>
${this._snapshot.homeassistant
? html`<div>Home Assistant:</div>
<paper-checkbox
.checked=${this._restoreHass}
@change="${(ev: Event) => {
this._restoreHass = (ev.target as PaperCheckboxElement).checked!;
}}"
>
Home Assistant ${this._snapshot.homeassistant}
</paper-checkbox>`
: ""}
${this._folders.length
? html`
<div>Folders:</div>
<paper-dialog-scrollable class="no-margin-top">
${this._folders.map((item) => {
return html`
<paper-checkbox
.checked=${item.checked}
@change="${(ev: Event) =>
this._updateFolders(
item,
(ev.target as PaperCheckboxElement).checked
)}"
>
${item.name}
</paper-checkbox>
`;
})}
</paper-dialog-scrollable>
`
: ""}
${this._addons.length
? html`
<div>Add-on:</div>
<paper-dialog-scrollable class="no-margin-top">
${this._addons.map((item) => {
return html`
<paper-checkbox
.checked=${item.checked}
@change="${(ev: Event) =>
this._updateAddons(
item,
(ev.target as PaperCheckboxElement).checked
)}"
>
${item.name}
</paper-checkbox>
`;
})}
</paper-dialog-scrollable>
`
: ""}
${this._snapshot.protected
? html`
<paper-input
autofocus=""
label="Password"
type="password"
@value-changed=${this._passwordInput}
.value=${this._snapshotPassword}
></paper-input>
`
: ""}
${this._error ? html` <p class="error">Error: ${this._error}</p> ` : ""}
<ha-dialog
open
scrimClickAction
@closed=${this.closeDialog}
.heading=${createCloseHeading(this.hass, this._computeName)}
>
${this._restoringSnapshot
? html` <ha-circular-progress active></ha-circular-progress>`
: html`<supervisor-snapshot-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}
.snapshot=${this._snapshot}
>
</supervisor-snapshot-content>`}
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
<div class="button-row" slot="primaryAction">
<mwc-button @click=${this._partialRestoreClicked}>
<ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon>
Restore Selected
</mwc-button>
${!this._onboarding
? html`
<mwc-button @click=${this._deleteClicked}>
<ha-svg-icon .path=${mdiDelete} class="icon warning">
</ha-svg-icon>
<span class="warning">Delete Snapshot</span>
</mwc-button>
`
: ""}
</div>
<div class="button-row" slot="secondaryAction">
${this._snapshot.type === "full"
? html`
<mwc-button @click=${this._fullRestoreClicked}>
<ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon>
Restore Everything
</mwc-button>
`
: ""}
${!this._onboarding
? html`<mwc-button @click=${this._downloadClicked}>
<ha-svg-icon .path=${mdiDownload} class="icon"></ha-svg-icon>
Download Snapshot
</mwc-button>`
: ""}
</div>
<mwc-button
.disabled=${this._restoringSnapshot}
slot="secondaryAction"
@click=${this._restoreClicked}
>
Restore
</mwc-button>
<ha-button-menu
fixed
slot="primaryAction"
@action=${this._handleMenuAction}
@closed=${(ev: Event) => ev.stopPropagation()}
>
<mwc-icon-button slot="trigger" alt="menu">
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item>Download Snapshot</mwc-list-item>
<mwc-list-item class="error">Delete Snapshot</mwc-list-item>
</ha-button-menu>
</ha-dialog>
`;
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
paper-checkbox {
ha-svg-icon {
color: var(--primary-text-color);
}
ha-circular-progress {
display: block;
margin: 4px;
}
mwc-button ha-svg-icon {
margin-right: 4px;
}
.button-row {
display: grid;
gap: 8px;
margin-right: 8px;
}
.details {
color: var(--secondary-text-color);
}
.warning,
.error {
color: var(--error-color);
}
.buttons li {
list-style-type: none;
}
.buttons .icon {
margin-right: 16px;
}
.no-margin-top {
margin-top: 0;
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
}
/* overrule the ha-style-dialog max-height on small screens */
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-header-bar {
--mdc-theme-primary: var(--app-header-background-color);
--mdc-theme-on-primary: var(--app-header-text-color, white);
}
text-align: center;
}
`,
];
}
private _updateFolders(item: FolderItem, value: boolean | null | undefined) {
this._folders = this._folders.map((folder) => {
if (folder.slug === item.slug) {
folder.checked = value;
}
return folder;
});
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._downloadClicked();
break;
case 1:
this._deleteClicked();
break;
}
}
private _updateAddons(item: AddonItem, value: boolean | null | undefined) {
this._addons = this._addons.map((addon) => {
if (addon.slug === item.slug) {
addon.checked = value;
}
return addon;
});
private async _restoreClicked() {
const snapshotDetails = this._snapshotContent.snapshotDetails();
this._restoringSnapshot = true;
if (this._snapshotContent.snapshotType === "full") {
await this._fullRestoreClicked(snapshotDetails);
} else {
await this._partialRestoreClicked(snapshotDetails);
}
this._restoringSnapshot = false;
}
private _passwordInput(ev: PolymerChangedEvent<string>) {
this._snapshotPassword = ev.detail.value;
}
private async _partialRestoreClicked() {
private async _partialRestoreClicked(snapshotDetails) {
if (
this.supervisor !== undefined &&
this.supervisor.info.state !== "running"
this._dialogParams?.supervisor !== undefined &&
this._dialogParams?.supervisor.info.state !== "running"
) {
await showAlertDialog(this, {
title: "Could not restore snapshot",
text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`,
text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
});
return;
}
@@ -330,41 +161,17 @@ class HassioSnapshotDialog extends LitElement {
return;
}
const addons = this._addons
.filter((addon) => addon.checked)
.map((addon) => addon.slug);
const folders = this._folders
.filter((folder) => folder.checked)
.map((folder) => folder.slug);
const data: {
homeassistant: boolean;
addons: any;
folders: any;
password?: string;
} = {
homeassistant: this._restoreHass,
addons,
folders,
};
if (this._snapshot!.protected) {
data.password = this._snapshotPassword;
}
if (!this._onboarding) {
if (!this._dialogParams?.onboarding) {
this.hass
.callApi(
"POST",
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
data
snapshotDetails
)
.then(
() => {
alert("Snapshot restored!");
this._closeDialog();
this.closeDialog();
},
(error) => {
this._error = error.body.message;
@@ -374,20 +181,20 @@ class HassioSnapshotDialog extends LitElement {
fireEvent(this, "restoring");
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
method: "POST",
body: JSON.stringify(data),
body: JSON.stringify(snapshotDetails),
});
this._closeDialog();
this.closeDialog();
}
}
private async _fullRestoreClicked() {
private async _fullRestoreClicked(snapshotDetails) {
if (
this.supervisor !== undefined &&
this.supervisor.info.state !== "running"
this._dialogParams?.supervisor !== undefined &&
this._dialogParams?.supervisor.info.state !== "running"
) {
await showAlertDialog(this, {
title: "Could not restore snapshot",
text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`,
text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
});
return;
}
@@ -402,20 +209,16 @@ class HassioSnapshotDialog extends LitElement {
return;
}
const data = this._snapshot!.protected
? { password: this._snapshotPassword }
: undefined;
if (!this._onboarding) {
if (!this._dialogParams?.onboarding) {
this.hass
.callApi(
"POST",
`hassio/snapshots/${this._snapshot!.slug}/restore/full`,
data
snapshotDetails
)
.then(
() => {
alert("Snapshot restored!");
this._closeDialog();
this.closeDialog();
},
(error) => {
this._error = error.body.message;
@@ -425,9 +228,9 @@ class HassioSnapshotDialog extends LitElement {
fireEvent(this, "restoring");
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
method: "POST",
body: JSON.stringify(data),
body: JSON.stringify(snapshotDetails),
});
this._closeDialog();
this.closeDialog();
}
}
@@ -450,7 +253,7 @@ class HassioSnapshotDialog extends LitElement {
if (this._dialogParams!.onDelete) {
this._dialogParams!.onDelete();
}
this._closeDialog();
this.closeDialog();
},
(error) => {
this._error = error.body.message;
@@ -466,7 +269,9 @@ class HassioSnapshotDialog extends LitElement {
`/api/hassio/snapshots/${this._snapshot!.slug}/download`
);
} catch (err) {
alert(`Error: ${extractApiErrorMessage(err)}`);
await showAlertDialog(this, {
text: extractApiErrorMessage(err),
});
return;
}
@@ -483,10 +288,9 @@ class HassioSnapshotDialog extends LitElement {
}
}
const name = this._computeName.replace(/[^a-z0-9]+/gi, "_");
const a = document.createElement("a");
a.href = signedPath.path;
a.download = `Hass_io_${name}.tar`;
a.download = `home_assistant_snapshot_${slugify(this._computeName)}.tar`;
this.shadowRoot!.appendChild(a);
a.click();
this.shadowRoot!.removeChild(a);
@@ -497,29 +301,6 @@ class HassioSnapshotDialog extends LitElement {
? this._snapshot.name || this._snapshot.slug
: "Unnamed snapshot";
}
private get _computeSize() {
return Math.ceil(this._snapshot!.size * 10) / 10 + " MB";
}
private _formatDatetime(datetime) {
return new Date(datetime).toLocaleDateString(navigator.language, {
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
});
}
private _closeDialog() {
this._dialogParams = undefined;
this._snapshot = undefined;
this._snapshotPassword = "";
this._folders = [];
this._addons = [];
}
}
declare global {

View File

@@ -0,0 +1,18 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
export interface HassioCreateSnapshotDialogParams {
supervisor: Supervisor;
onCreate: () => void;
}
export const showHassioCreateSnapshotDialog = (
element: HTMLElement,
dialogParams: HassioCreateSnapshotDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-create-snapshot",
dialogImport: () => import("./dialog-hassio-create-snapshot"),
dialogParams,
});
};

View File

@@ -1,4 +1,4 @@
import type { LitElement } from "lit-element";
import type { LitElement } from "lit";
import {
HassioAddonDetails,
restartHassioAddon,

View File

@@ -1,13 +1,6 @@
import "@material/mwc-button/mwc-button";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog";
@@ -27,15 +20,15 @@ import { SupervisorDialogSupervisorUpdateParams } from "./show-dialog-update";
class DialogSupervisorUpdate extends LitElement {
public hass!: HomeAssistant;
@internalProperty() private _opened = false;
@state() private _opened = false;
@internalProperty() private _createSnapshot = true;
@state() private _createSnapshot = true;
@internalProperty() private _action: "snapshot" | "update" | null = null;
@state() private _action: "snapshot" | "update" | null = null;
@internalProperty() private _error?: string;
@state() private _error?: string;
@internalProperty()
@state()
private _dialogParams?: SupervisorDialogSupervisorUpdateParams;
public async showDialog(
@@ -173,7 +166,7 @@ class DialogSupervisorUpdate extends LitElement {
this.closeDialog();
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,

View File

@@ -15,5 +15,11 @@ body {
padding: 0;
height: 100vh;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #111111;
color: #e1e1e1;
}
}
`;
document.head.appendChild(styleEl);

View File

@@ -1,7 +1,11 @@
import { customElement, html, property, PropertyValues } from "lit-element";
import { html, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { atLeastVersion } from "../../src/common/config/version";
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../src/common/dom/fire_event";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
import { mainWindow } from "../../src/common/dom/get_main_window";
import { navigate } from "../../src/common/navigate";
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { Supervisor } from "../../src/data/supervisor/supervisor";
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
@@ -46,14 +50,23 @@ export class HassioMain extends SupervisorBaseElement {
// listen on this element for navigation events, so we need to forward them.
// Joakim - April 26, 2021
// Due to changes in behavior in Google Chrome, we changed navigate to fire on the top element
top.addEventListener("location-changed", (ev) =>
// Due to changes in behavior in Google Chrome, we changed navigate to listen on the top element
mainWindow.addEventListener("location-changed", (ev) =>
// @ts-ignore
fireEvent(this, ev.type, ev.detail, {
bubbles: false,
})
);
// Paulus - May 17, 2021
// Convert the <a> tags to native nav in Home Assistant < 2021.6
document.body.addEventListener("click", (ev) => {
const href = isNavigationClick(ev);
if (href) {
navigate(href);
}
});
// Forward haptic events to parent window.
window.addEventListener("haptic", (ev) => {
// @ts-ignore
@@ -90,7 +103,7 @@ export class HassioMain extends SupervisorBaseElement {
private _applyTheme() {
let themeName: string;
let options: Partial<HomeAssistant["selectedTheme"]> | undefined;
let themeSettings: Partial<HomeAssistant["selectedTheme"]> | undefined;
if (atLeastVersion(this.hass.config.version, 0, 114)) {
themeName =
@@ -99,9 +112,9 @@ export class HassioMain extends SupervisorBaseElement {
? this.hass.themes.default_dark_theme!
: this.hass.themes.default_theme);
options = this.hass.selectedTheme;
if (themeName === "default" && options?.dark === undefined) {
options = {
themeSettings = this.hass.selectedTheme;
if (themeSettings?.dark === undefined) {
themeSettings = {
...this.hass.selectedTheme,
dark: this.hass.themes.darkMode,
};
@@ -116,7 +129,7 @@ export class HassioMain extends SupervisorBaseElement {
this.parentElement,
this.hass.themes,
themeName,
options
themeSettings
);
}
}

View File

@@ -1,11 +1,4 @@
import {
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { html, LitElement, TemplateResult } from "lit";
import { sanitizeUrl } from "@braintree/sanitize-url";
import {
createSearchParam,
@@ -20,6 +13,7 @@ import {
import { navigate } from "../../src/common/navigate";
import { HomeAssistant, Route } from "../../src/types";
import { Supervisor } from "../../src/data/supervisor/supervisor";
import { customElement, property, state } from "lit/decorators";
const REDIRECTS: Redirects = {
supervisor: {
@@ -43,6 +37,12 @@ const REDIRECTS: Redirects = {
addon: "string",
},
},
supervisor_ingress: {
redirect: "/hassio/ingress",
params: {
addon: "string",
},
},
supervisor_add_addon_repository: {
redirect: "/hassio/store",
params: {
@@ -59,7 +59,7 @@ class HassioMyRedirect extends LitElement {
@property({ attribute: false }) public route!: Route;
@internalProperty() public _error?: TemplateResult | string;
@state() public _error?: TemplateResult | string;
connectedCallback() {
super.connectedCallback();
@@ -89,7 +89,7 @@ class HassioMyRedirect extends LitElement {
return;
}
navigate(this, url, true);
navigate(url, { replace: true });
}
protected render(): TemplateResult {

View File

@@ -1,4 +1,4 @@
import { customElement, property } from "lit-element";
import { customElement, property } from "lit/decorators";
import { Supervisor } from "../../src/data/supervisor/supervisor";
import {
HassRouterPage,

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import {
Supervisor,
supervisorCollection,
@@ -46,7 +39,7 @@ class HassioPanel extends LitElement {
`;
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
:host {
--app-header-background-color: var(--sidebar-background-color);

View File

@@ -1,4 +1,4 @@
import { customElement, property } from "lit-element";
import { customElement, property } from "lit/decorators";
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { Supervisor } from "../../src/data/supervisor/supervisor";
import {
@@ -61,11 +61,10 @@ class HassioRouter extends HassRouterPage {
el.hass = this.hass;
el.narrow = this.narrow;
el.route = route;
el.supervisor = this.supervisor;
if (el.localName === "hassio-ingress-view") {
el.ingressPanel = this.panel.config && this.panel.config.ingress;
} else {
el.supervisor = this.supervisor;
}
}

View File

@@ -1,25 +1,27 @@
import { mdiMenu } from "@mdi/js";
import {
css,
CSSResult,
customElement,
CSSResultGroup,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import { nextRender } from "../../../src/common/util/render-status";
import {
fetchHassioAddonInfo,
HassioAddonDetails,
} from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
createHassioSession,
validateHassioSession,
} from "../../../src/data/hassio/ingress";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-subpage";
@@ -29,11 +31,13 @@ import { HomeAssistant, Route } from "../../../src/types";
class HassioIngressView extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property() public route!: Route;
@property() public ingressPanel = false;
@internalProperty() private _addon?: HassioAddonDetails;
@state() private _addon?: HassioAddonDetails;
@property({ type: Boolean })
public narrow = false;
@@ -80,6 +84,36 @@ class HassioIngressView extends LitElement {
: iframe}`;
}
protected async firstUpdated(): Promise<void> {
if (this.route.path === "") {
const requestedAddon = extractSearchParam("addon");
let addonInfo: HassioAddonDetails;
if (requestedAddon) {
try {
addonInfo = await fetchHassioAddonInfo(this.hass, requestedAddon);
} catch (err) {
await showAlertDialog(this, {
text: extractApiErrorMessage(err),
title: requestedAddon,
});
await nextRender();
history.back();
return;
}
if (!addonInfo.ingress) {
await showAlertDialog(this, {
text: this.supervisor.localize("my.error_addon_no_ingress"),
title: addonInfo.name,
});
await nextRender();
history.back();
} else {
navigate(`/hassio/ingress/${addonInfo.slug}`, { replace: true });
}
}
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
@@ -109,6 +143,7 @@ class HassioIngressView extends LitElement {
text: "Unable to fetch add-on info to start Ingress",
title: "Supervisor",
});
await nextRender();
history.back();
return;
}
@@ -118,6 +153,7 @@ class HassioIngressView extends LitElement {
text: "Add-on does not support Ingress",
title: addon.name,
});
await nextRender();
history.back();
return;
}
@@ -127,7 +163,8 @@ class HassioIngressView extends LitElement {
text: "Add-on is not running. Please start it first",
title: addon.name,
});
navigate(this, `/hassio/addon/${addon.slug}/info`, true);
await nextRender();
navigate(`/hassio/addon/${addon.slug}/info`, { replace: true });
return;
}
@@ -140,6 +177,7 @@ class HassioIngressView extends LitElement {
text: "Unable to create an Ingress session",
title: addon.name,
});
await nextRender();
history.back();
return;
}
@@ -162,7 +200,7 @@ class HassioIngressView extends LitElement {
fireEvent(this, "hass-toggle-menu");
}
static get styles(): CSSResult {
static get styles(): CSSResultGroup {
return css`
iframe {
display: block;

View File

@@ -1,4 +1,4 @@
import { css } from "lit-element";
import { css } from "lit";
export const hassioStyle = css`
.content {

View File

@@ -1,118 +1,179 @@
import "@material/mwc-button";
import "@material/mwc-icon-button";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import {
mdiDotsVertical,
mdiPackageVariant,
mdiPackageVariantClosed,
} from "@mdi/js";
import "@polymer/paper-checkbox/paper-checkbox";
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import "@polymer/paper-radio-button/paper-radio-button";
import "@polymer/paper-radio-group/paper-radio-group";
import type { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group";
import { mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import {
css,
CSSResultArray,
customElement,
CSSResultGroup,
html,
internalProperty,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import "../../../src/components/buttons/ha-progress-button";
import relativeTime from "../../../src/common/datetime/relative_time";
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
} from "../../../src/components/data-table/ha-data-table";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-svg-icon";
import "../../../src/components/ha-fab";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
createHassioFullSnapshot,
createHassioPartialSnapshot,
fetchHassioSnapshots,
HassioFullSnapshotCreateParams,
HassioPartialSnapshotCreateParams,
friendlyFolderName,
HassioSnapshot,
reloadHassioSnapshots,
removeSnapshot,
} from "../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-tabs-subpage";
import { PolymerChangedEvent } from "../../../src/polymer-types";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-tabs-subpage-data-table";
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import "../components/hassio-card-content";
import "../components/hassio-upload-snapshot";
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
import { showSnapshotUploadDialog } from "../dialogs/snapshot/show-dialog-snapshot-upload";
import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style";
interface CheckboxItem {
slug: string;
checked: boolean;
name?: string;
}
@customElement("hassio-snapshots")
class HassioSnapshots extends LitElement {
export class HassioSnapshots extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _snapshotName = "";
@property({ type: Object }) public route!: Route;
@internalProperty() private _snapshotPassword = "";
@property({ type: Boolean }) public narrow!: boolean;
@internalProperty() private _snapshotHasPassword = false;
@property({ type: Boolean }) public isWide!: boolean;
@internalProperty() private _snapshotType: HassioSnapshot["type"] = "full";
@state() private _selectedSnapshots: string[] = [];
@internalProperty() private _snapshots?: HassioSnapshot[] = [];
@state() private _snapshots?: HassioSnapshot[] = [];
@internalProperty() private _addonList: CheckboxItem[] = [];
@query("hass-tabs-subpage-data-table", true)
private _dataTable!: HaTabsSubpageDataTable;
@internalProperty() private _folderList: CheckboxItem[] = [
{
slug: "homeassistant",
checked: true,
},
{ slug: "ssl", checked: true },
{ slug: "share", checked: true },
{ slug: "media", checked: true },
{ slug: "addons/local", checked: true },
];
private _firstUpdatedCalled = false;
@internalProperty() private _error = "";
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
this.refreshData();
}
}
public async refreshData() {
await reloadHassioSnapshots(this.hass);
await this._updateSnapshots();
await this.fetchSnapshots();
}
private _computeSnapshotContent = (snapshot: HassioSnapshot): string => {
if (snapshot.type === "full") {
return this.supervisor.localize("snapshot.full_snapshot");
}
const content: string[] = [];
if (snapshot.content.homeassistant) {
content.push("Home Assistant");
}
if (snapshot.content.folders.length !== 0) {
for (const folder of snapshot.content.folders) {
content.push(friendlyFolderName[folder] || folder);
}
}
if (snapshot.content.addons.length !== 0) {
for (const addon of snapshot.content.addons) {
content.push(
this.supervisor.supervisor.addons.find(
(entry) => entry.slug === addon
)?.name || addon
);
}
}
return content.join(", ");
};
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.hass && this.isConnected) {
this.refreshData();
}
this._firstUpdatedCalled = true;
}
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer => ({
name: {
title: this.supervisor?.localize("snapshot.name") || "",
sortable: true,
filterable: true,
grows: true,
template: (entry: string, snapshot: any) =>
html`${entry || snapshot.slug}
<div class="secondary">${snapshot.secondary}</div>`,
},
date: {
title: this.supervisor?.localize("snapshot.created") || "",
width: "15%",
direction: "desc",
hidden: narrow,
filterable: true,
sortable: true,
template: (entry: string) =>
relativeTime(new Date(entry), this.hass.localize),
},
secondary: {
title: "",
hidden: true,
filterable: true,
},
})
);
private _snapshotData = memoizeOne((snapshots: HassioSnapshot[]) =>
snapshots.map((snapshot) => ({
...snapshot,
secondary: this._computeSnapshotContent(snapshot),
}))
);
protected render(): TemplateResult {
if (!this.supervisor) {
return html``;
}
return html`
<hass-tabs-subpage
<hass-tabs-subpage-data-table
.tabs=${supervisorTabs}
.hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.searchLabel=${this.supervisor.localize("search")}
.noDataText=${this.supervisor.localize("snapshot.no_snapshots")}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${supervisorTabs}
.columns=${this._columns(this.narrow)}
.data=${this._snapshotData(this._snapshots || [])}
id="slug"
@row-click=${this._handleRowClicked}
@selection-changed=${this._handleSelectionChanged}
clickable
selectable
hasFab
main-page
supervisor
>
<span slot="header">
${this.supervisor.localize("panel.snapshots")}
</span>
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@@ -122,174 +183,66 @@ class HassioSnapshots extends LitElement {
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item>
${this.supervisor.localize("common.reload")}
${this.supervisor?.localize("common.reload")}
</mwc-list-item>
${atLeastVersion(this.hass.config.version, 0, 116)
? html`<mwc-list-item>
${this.supervisor.localize("snapshot.upload_snapshot")}
${this.supervisor?.localize("snapshot.upload_snapshot")}
</mwc-list-item>`
: ""}
</ha-button-menu>
<div class="content">
<h1>
${this.supervisor.localize("snapshot.create_snapshot")}
</h1>
<p class="description">
${this.supervisor.localize("snapshot.description")}
</p>
<div class="card-group">
<ha-card>
<div class="card-content">
<paper-input
autofocus
.label=${this.supervisor.localize("snapshot.name")}
name="snapshotName"
.value=${this._snapshotName}
@value-changed=${this._handleTextValueChanged}
></paper-input>
${this.supervisor.localize("snapshot.type")}:
<paper-radio-group
name="snapshotType"
type="${this.supervisor.localize("snapshot.type")}"
.selected=${this._snapshotType}
@selected-changed=${this._handleRadioValueChanged}
>
<paper-radio-button name="full">
${this.supervisor.localize("snapshot.full_snapshot")}
</paper-radio-button>
<paper-radio-button name="partial">
${this.supervisor.localize("snapshot.partial_snapshot")}
</paper-radio-button>
</paper-radio-group>
${this._snapshotType === "full"
? undefined
: html`
${this.supervisor.localize("snapshot.folders")}:
${this._folderList.map(
(folder, idx) => html`
<paper-checkbox
.idx=${idx}
.checked=${folder.checked}
@checked-changed=${this._folderChecked}
>
${this.supervisor.localize(
`snapshot.folder.${folder.slug}`
)}
</paper-checkbox>
`
)}
${this.supervisor.localize("snapshot.addons")}:
${this._addonList.map(
(addon, idx) => html`
<paper-checkbox
.idx=${idx}
.checked=${addon.checked}
@checked-changed=${this._addonChecked}
>
${addon.name}
</paper-checkbox>
`
)}
`}
${this.supervisor.localize("snapshot.security")}:
<paper-checkbox
name="snapshotHasPassword"
.checked=${this._snapshotHasPassword}
@checked-changed=${this._handleCheckboxValueChanged}
>
${this.supervisor.localize("snapshot.password_protection")}
</paper-checkbox>
${this._snapshotHasPassword
${this._selectedSnapshots.length
? html`<div
class=${classMap({
"header-toolbar": this.narrow,
"table-header": !this.narrow,
})}
slot="header"
>
<p class="selected-txt">
${this.supervisor.localize("snapshot.selected", {
number: this._selectedSnapshots.length,
})}
</p>
<div class="header-btns">
${!this.narrow
? html`
<paper-input
.label=${this.supervisor.localize("snapshot.password")}
type="password"
name="snapshotPassword"
.value=${this._snapshotPassword}
@value-changed=${this._handleTextValueChanged}
></paper-input>
<mwc-button
@click=${this._deleteSelected}
class="warning"
>
${this.supervisor.localize("snapshot.delete_selected")}
</mwc-button>
`
: undefined}
${this._error !== ""
? html` <p class="error">${this._error}</p> `
: undefined}
: html`
<mwc-icon-button
id="delete-btn"
class="warning"
@click=${this._deleteSelected}
>
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
</mwc-icon-button>
<paper-tooltip animation-delay="0" for="delete-btn">
${this.supervisor.localize("snapshot.delete_selected")}
</paper-tooltip>
`}
</div>
<div class="card-actions">
<ha-progress-button
@click=${this._createSnapshot}
.title=${this.supervisor.info.state !== "running"
? this.supervisor.localize(
"snapshot.create_blocked_not_running",
"state",
this.supervisor.info.state
)
: ""}
.disabled=${this.supervisor.info.state !== "running"}
>
${this.supervisor.localize("snapshot.create")}
</ha-progress-button>
</div>
</ha-card>
</div>
</div> `
: ""}
<h1>${this.supervisor.localize("snapshot.available_snapshots")}</h1>
<div class="card-group">
${this._snapshots === undefined
? undefined
: this._snapshots.length === 0
? html`
<ha-card>
<div class="card-content">
${this.supervisor.localize("snapshot.no_snapshots")}
</div>
</ha-card>
`
: this._snapshots.map(
(snapshot) => html`
<ha-card
class="pointer"
.snapshot=${snapshot}
@click=${this._snapshotClicked}
>
<div class="card-content">
<hassio-card-content
.hass=${this.hass}
.title=${snapshot.name || snapshot.slug}
.description=${this._computeDetails(snapshot)}
.datetime=${snapshot.date}
.icon=${snapshot.type === "full"
? mdiPackageVariantClosed
: mdiPackageVariant}
icon-class="snapshot"
></hassio-card-content>
</div>
</ha-card>
`
)}
</div>
</div>
</hass-tabs-subpage>
<ha-fab
slot="fab"
@click=${this._createSnapshot}
.label=${this.supervisor.localize("snapshot.create_snapshot")}
extended
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>
</hass-tabs-subpage-data-table>
`;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this.refreshData();
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("supervisor")) {
this._addonList = this.supervisor.supervisor.addons
.map((addon) => ({
slug: addon.slug,
name: addon.name,
checked: true,
}))
.sort((a, b) => (a.name < b.name ? -1 : 1));
}
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
@@ -301,121 +254,10 @@ class HassioSnapshots extends LitElement {
}
}
private _handleTextValueChanged(ev: PolymerChangedEvent<string>) {
const input = ev.currentTarget as PaperInputElement;
this[`_${input.name}`] = ev.detail.value;
}
private _handleCheckboxValueChanged(ev) {
const input = ev.currentTarget as PaperCheckboxElement;
this[`_${input.name}`] = input.checked;
}
private _handleRadioValueChanged(ev: PolymerChangedEvent<string>) {
const input = ev.currentTarget as PaperRadioGroupElement;
this[`_${input.getAttribute("name")}`] = ev.detail.value;
}
private _folderChecked(ev) {
const { idx, checked } = ev.currentTarget!;
this._folderList = this._folderList.map((folder, curIdx) =>
curIdx === idx ? { ...folder, checked } : folder
);
}
private _addonChecked(ev) {
const { idx, checked } = ev.currentTarget!;
this._addonList = this._addonList.map((addon, curIdx) =>
curIdx === idx ? { ...addon, checked } : addon
);
}
private async _updateSnapshots() {
try {
this._snapshots = await fetchHassioSnapshots(this.hass);
this._snapshots.sort((a, b) => (a.date < b.date ? 1 : -1));
} catch (err) {
this._error = extractApiErrorMessage(err);
}
}
private async _createSnapshot(ev: CustomEvent): Promise<void> {
if (this.supervisor.info.state !== "running") {
await showAlertDialog(this, {
title: this.supervisor.localize("snapshot.could_not_create"),
text: this.supervisor.localize(
"snapshot.create_blocked_not_running",
"state",
this.supervisor.info.state
),
});
}
const button = ev.currentTarget as any;
button.progress = true;
this._error = "";
if (this._snapshotHasPassword && !this._snapshotPassword.length) {
this._error = this.supervisor.localize("snapshot.enter_password");
button.progress = false;
return;
}
await this.updateComplete;
const name =
this._snapshotName ||
new Date().toLocaleDateString(navigator.language, {
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
});
try {
if (this._snapshotType === "full") {
const data: HassioFullSnapshotCreateParams = { name };
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioFullSnapshot(this.hass, data);
} else {
const addons = this._addonList
.filter((addon) => addon.checked)
.map((addon) => addon.slug);
const folders = this._folderList
.filter((folder) => folder.checked)
.map((folder) => folder.slug);
const data: HassioPartialSnapshotCreateParams = {
name,
folders,
addons,
};
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioPartialSnapshot(this.hass, data);
}
this._updateSnapshots();
} catch (err) {
this._error = extractApiErrorMessage(err);
}
button.progress = false;
}
private _computeDetails(snapshot: HassioSnapshot) {
const type =
snapshot.type === "full"
? this.supervisor.localize("snapshot.full_snapshot")
: this.supervisor.localize("snapshot.partial_snapshot");
return snapshot.protected ? `${type}, password protected` : type;
}
private _snapshotClicked(ev) {
showHassioSnapshotDialog(this, {
slug: ev.currentTarget!.snapshot.slug,
supervisor: this.supervisor,
onDelete: () => this._updateSnapshots(),
});
private _handleSelectionChanged(
ev: HASSDomEvent<SelectionChangedEvent>
): void {
this._selectedSnapshots = ev.detail.value;
}
private _showUploadSnapshotDialog() {
@@ -424,31 +266,110 @@ class HassioSnapshots extends LitElement {
showHassioSnapshotDialog(this, {
slug,
supervisor: this.supervisor,
onDelete: () => this._updateSnapshots(),
onDelete: () => this.fetchSnapshots(),
}),
reloadSnapshot: () => this.refreshData(),
});
}
static get styles(): CSSResultArray {
private async fetchSnapshots() {
await reloadHassioSnapshots(this.hass);
this._snapshots = await fetchHassioSnapshots(this.hass);
}
private async _deleteSelected() {
const confirm = await showConfirmationDialog(this, {
title: this.supervisor.localize("snapshot.delete_snapshot_title"),
text: this.supervisor.localize("snapshot.delete_snapshot_text", {
number: this._selectedSnapshots.length,
}),
confirmText: this.supervisor.localize("snapshot.delete_snapshot_confirm"),
});
if (!confirm) {
return;
}
try {
await Promise.all(
this._selectedSnapshots.map((slug) => removeSnapshot(this.hass, slug))
);
} catch (err) {
showAlertDialog(this, {
title: this.supervisor.localize("snapshot.failed_to_delete"),
text: extractApiErrorMessage(err),
});
return;
}
await reloadHassioSnapshots(this.hass);
this._snapshots = await fetchHassioSnapshots(this.hass);
this._dataTable.clearSelection();
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const slug = ev.detail.id;
showHassioSnapshotDialog(this, {
slug,
supervisor: this.supervisor,
onDelete: () => this.fetchSnapshots(),
});
}
private _createSnapshot() {
if (this.supervisor!.info.state !== "running") {
showAlertDialog(this, {
title: this.supervisor!.localize("snapshot.could_not_create"),
text: this.supervisor!.localize(
"snapshot.create_blocked_not_running",
"state",
this.supervisor!.info.state
),
});
return;
}
showHassioCreateSnapshotDialog(this, {
supervisor: this.supervisor!,
onCreate: () => this.fetchSnapshots(),
});
}
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,
css`
paper-radio-group {
display: block;
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 58px;
border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
}
paper-radio-button {
padding: 0 0 2px 2px;
.header-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--secondary-text-color);
position: relative;
top: -4px;
}
paper-radio-button,
paper-checkbox,
paper-input[type="password"] {
display: block;
margin: 4px 0 4px 48px;
.selected-txt {
font-weight: bold;
padding-left: 16px;
color: var(--primary-text-color);
}
.pointer {
cursor: pointer;
.table-header .selected-txt {
margin-top: 20px;
}
.header-toolbar .selected-txt {
font-size: 16px;
}
.header-toolbar .header-btns {
margin-right: -12px;
}
.header-btns > mwc-button,
.header-btns > mwc-icon-button {
margin: 8px;
}
`,
];

View File

@@ -1,10 +1,6 @@
import { Collection, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
internalProperty,
LitElement,
property,
PropertyValues,
} from "lit-element";
import { LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators";
import { atLeastVersion } from "../../src/common/config/version";
import { computeLocalize } from "../../src/common/translations/localize";
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";
@@ -46,14 +42,11 @@ export class SupervisorBaseElement extends urlSyncMixin(
localize: () => "",
};
@internalProperty() private _unsubs: Record<string, UnsubscribeFunc> = {};
@state() private _unsubs: Record<string, UnsubscribeFunc> = {};
@internalProperty() private _collections: Record<
string,
Collection<unknown>
> = {};
@state() private _collections: Record<string, Collection<unknown>> = {};
@internalProperty() private _language = "en";
@state() private _language = "en";
public connectedCallback(): void {
super.connectedCallback();

View File

@@ -1,15 +1,7 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu";
@@ -39,7 +31,7 @@ class HassioCoreInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
@state() private _metrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
@@ -178,7 +170,7 @@ class HassioCoreInfo extends LitElement {
folders: ["homeassistant"],
homeassistant: true,
},
updateHandler: async () => await this._updateCore(),
updateHandler: async () => this._updateCore(),
});
}
@@ -189,7 +181,7 @@ class HassioCoreInfo extends LitElement {
});
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -2,16 +2,9 @@ import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import { safeDump } from "js-yaml";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { dump } from "js-yaml";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -97,9 +90,7 @@ class HassioHostInfo extends LitElement {
<span slot="heading">
${this.supervisor.localize("system.host.ip_address")}
</span>
<span slot="description">
${primaryIpAddress}
</span>
<span slot="description"> ${primaryIpAddress} </span>
<mwc-button
.label=${this.supervisor.localize("system.host.change")}
@click=${this._changeNetworkClicked}
@@ -242,7 +233,7 @@ class HassioHostInfo extends LitElement {
const content = await fetchHassioHardwareInfo(this.hass);
showHassioMarkdownDialog(this, {
title: this.supervisor.localize("system.host.hardware"),
content: `<pre>${safeDump(content, { indent: 2 })}</pre>`,
content: `<pre>${dump(content, { indent: 2 })}</pre>`,
});
} catch (err) {
showAlertDialog(this, {
@@ -417,7 +408,7 @@ class HassioHostInfo extends LitElement {
}
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

View File

@@ -1,13 +1,5 @@
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button";
@@ -67,7 +59,7 @@ class HassioSupervisorInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
@state() private _metrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
@@ -264,9 +256,7 @@ class HassioSupervisorInfo extends LitElement {
title: this.supervisor.localize("system.supervisor.warning"),
text: html`${this.supervisor.localize("system.supervisor.beta_warning")}
<br />
<b>
${this.supervisor.localize("system.supervisor.beta_backup")}
</b>
<b> ${this.supervisor.localize("system.supervisor.beta_backup")} </b>
<br /><br />
${this.supervisor.localize("system.supervisor.beta_release_items")}
<ul>
@@ -506,7 +496,7 @@ class HassioSupervisorInfo extends LitElement {
}
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,

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