Compare commits

..

109 Commits

Author SHA1 Message Date
Ludeeus
50047e2f10 Adjust gap 2021-07-30 10:21:27 +00:00
Ludeeus
2811541fba update 2021-06-02 21:22:34 +00:00
Ludeeus
57788cec44 style 2021-06-02 17:02:24 +00:00
Ludeeus
0aa314d9ae overlay 2021-06-02 16:57:41 +00:00
Ludeeus
f8d97735b8 update 2021-06-02 16:09:28 +00:00
Ludeeus
830136b874 icons 2021-06-02 15:42:31 +00:00
Ludeeus
dd01710784 addon 2021-06-02 15:31:05 +00:00
Ludeeus
f7d0736731 init 2021-06-02 15:10:18 +00:00
Bram Kragten
fe5f9576c6 Fix dev 2021-06-02 10:11:29 +02:00
Brynley McDonald
1b282b65b7 Add QR code to long lived access tokens dialog (#8948)
* Add QR code to long lived access tokens dialog

* Apply suggestions from code review

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

* Further changes from code review

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-06-02 09:59:22 +02:00
GitHub Action
e49664bad3 Translation update 2021-06-02 04:10:37 +00:00
Bram Kragten
2a30b55a43 Bumped version to 20210601.1 2021-06-01 21:30:51 +02:00
Paulus Schoutsen
9d0b20adce Add support for system options v2 (#9332)
* Add support for system options v2

* Update src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts

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

* Update dialog-config-entry-system-options.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-06-01 12:22:25 -07:00
Bram Kragten
acd5e1c081 Fix back button too wide on mobile (#9333) 2021-06-01 12:12:00 -07:00
Bram Kragten
cc1c5e45b2 Display error when enabling/disabling config entries (#9325) 2021-06-01 21:03:00 +02:00
Bram Kragten
038199c447 Change the type of debounce, use arrow functions (#9328) 2021-06-01 11:53:45 -07:00
Bram Kragten
8a1eab7ceb Cleanup virtualizer styles (#9327) 2021-06-01 11:51:30 -07:00
Joakim Sørensen
bc5bd35448 Filter adapters with information from the Supervisor (#9322)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-06-01 20:12:54 +02:00
Bram Kragten
1795fd56b7 Don't rotate chart axis labels (#9329) 2021-06-01 10:28:03 -07:00
Bram Kragten
4a7c33edad Bumped version to 20210601.0 2021-06-01 11:40:37 +02:00
rianadon
797f60d725 Show pressure units in weather details card (#9295) 2021-06-01 11:40:04 +02:00
Bram Kragten
2427d68aa1 Use local version 0.7 of lit-virtualizer (#9321) 2021-06-01 11:39:15 +02:00
GitHub Action
00c6b0f8ed Translation update 2021-06-01 04:14:44 +00:00
Paulus Schoutsen
7b8d4ab3d6 Update translations 2021-05-31 15:50:47 -07:00
Paulus Schoutsen
07a1a805f6 Bumped version to 20210531.1 2021-05-31 15:44:59 -07:00
Paulus Schoutsen
d8bab6aba9 Add support for disable polling system option (#9316) 2021-05-31 15:40:50 -07:00
Bram Kragten
a930e2dc75 Fix store auth disappearing (#9312) 2021-05-31 15:35:51 -07:00
J. Nick Koston
2eb35668fa Seperate addresses in network configuration (#9319) 2021-05-31 15:31:33 -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
884 changed files with 12085 additions and 8382 deletions

View File

@@ -28,9 +28,7 @@
"__BUILD__": false, "__BUILD__": false,
"__VERSION__": false, "__VERSION__": false,
"__STATIC_PATH__": false, "__STATIC_PATH__": false,
"Polymer": true, "Polymer": true
"webkitSpeechRecognition": false,
"ResizeObserver": false
}, },
"env": { "env": {
"browser": true, "browser": true,
@@ -106,5 +104,6 @@
"lit/attribute-value-entities": 0 "lit/attribute-value-entities": 0
}, },
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"], "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
"processor": "disable/disable" "processor": "disable/disable",
"ignorePatterns": ["src/resources/lit-virtualizer/*"]
} }

21
.gitignore vendored
View File

@@ -1,10 +1,17 @@
.DS_Store
.reify-cache
# build
build build
build-translations/* build-translations/*
hass_frontend/*
dist
# yarn
.yarn
yarn-error.log
node_modules/* node_modules/*
npm-debug.log npm-debug.log
.DS_Store
hass_frontend/*
.reify-cache
# Python stuff # Python stuff
*.py[cod] *.py[cod]
@@ -14,11 +21,8 @@ hass_frontend/*
# venv stuff # venv stuff
pyvenv.cfg pyvenv.cfg
pip-selfcheck.json pip-selfcheck.json
venv venv/*
.venv .venv
lib
bin
dist
# vscode # vscode
.vscode/* .vscode/*
@@ -31,9 +35,8 @@ src/cast/dev_const.ts
# Secrets # Secrets
.lokalise_token .lokalise_token
yarn-error.log
#asdf # asdf
.tool-versions .tool-versions
# Home Assistant config # Home Assistant config

4
.mocharc.cjs Normal file
View File

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

View File

@@ -52,11 +52,7 @@ const createRollupConfig = ({
browser: true, browser: true,
rootDir: paths.polymer_dir, rootDir: paths.polymer_dir,
}), }),
commonjs({ commonjs(),
namedExports: {
"js-yaml": ["safeDump", "safeLoad"],
},
}),
json(), json(),
babel({ babel({
...bundle.babelOptions({ latestBuild }), ...bundle.babelOptions({ latestBuild }),

View File

@@ -116,8 +116,9 @@ const createWebpackConfig = ({
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one // We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
new webpack.NormalModuleReplacementPlugin( new webpack.NormalModuleReplacementPlugin(
new RegExp( new RegExp(
require.resolve( path.resolve(
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js" paths.polymer_dir,
"src/resources/lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
) )
), ),
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js") path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
@@ -126,6 +127,11 @@ const createWebpackConfig = ({
].filter(Boolean), ].filter(Boolean),
resolve: { resolve: {
extensions: [".ts", ".js", ".json"], extensions: [".ts", ".js", ".json"],
alias: {
"lit/decorators$": "lit/decorators.js",
"lit/directive$": "lit/directive.js",
"lit/polyfill-support$": "lit/polyfill-support.js",
},
}, },
output: { output: {
filename: ({ chunk }) => { filename: ({ chunk }) => {

View File

@@ -1,16 +1,9 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket"; import { Auth, Connection } from "home-assistant-js-websocket";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
state,
TemplateResult,
} from "lit-element";
import { CastManager } from "../../../../src/cast/cast_manager"; import { CastManager } from "../../../../src/cast/cast_manager";
import { import {
castSendShowLovelaceView, castSendShowLovelaceView,
@@ -32,7 +25,6 @@ import {
import "../../../../src/layouts/hass-loading-screen"; import "../../../../src/layouts/hass-loading-screen";
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
import "./hc-layout"; import "./hc-layout";
import "@material/mwc-button/mwc-button";
@customElement("hc-cast") @customElement("hc-cast")
class HcCast extends LitElement { class HcCast extends LitElement {

View File

@@ -11,15 +11,8 @@ import {
getAuth, getAuth,
getAuthOptions, getAuthOptions,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
TemplateResult,
state,
} from "lit-element";
import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; import { CastManager, getCastManager } from "../../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; import { castSendShowDemo } from "../../../../src/cast/receiver_messages";
import { import {

View File

@@ -4,15 +4,8 @@ import {
getUser, getUser,
HassUser, HassUser,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
@customElement("hc-layout") @customElement("hc-layout")

View File

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

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
@customElement("hc-launch-screen") @customElement("hc-launch-screen")

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { LovelaceConfig } from "../../../../src/data/lovelace"; import { LovelaceConfig } from "../../../../src/data/lovelace";
import { Lovelace } from "../../../../src/panels/lovelace/types"; import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view"; import "../../../../src/panels/lovelace/views/hui-view";

View File

@@ -3,7 +3,8 @@ import {
getAuth, getAuth,
UnsubscribeFunc, UnsubscribeFunc,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { customElement, html, state, TemplateResult } from "lit-element"; import { html, TemplateResult } from "lit";
import { customElement, state } from "lit/decorators";
import { CAST_NS } from "../../../../src/cast/const"; import { CAST_NS } from "../../../../src/cast/const";
import { import {
ConnectMessage, ConnectMessage,

View File

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

View File

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

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
TemplateResult,
} from "lit-element";
import { CastManager } from "../../../src/cast/cast_manager"; import { CastManager } from "../../../src/cast/cast_manager";
import { castSendShowDemo } from "../../../src/cast/receiver_messages"; import { castSendShowDemo } from "../../../src/cast/receiver_messages";
import "../../../src/components/ha-icon"; import "../../../src/components/ha-icon";

View File

@@ -1,14 +1,7 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { property, state } from "lit/decorators";
CSSResultGroup, import { until } from "lit/directives/until";
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { until } from "lit-html/directives/until";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-circular-progress"; import "../../../src/components/ha-circular-progress";
import { LovelaceCardConfig } from "../../../src/data/lovelace"; import { LovelaceCardConfig } from "../../../src/data/lovelace";

View File

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

View File

@@ -1,7 +1,7 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; 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"; import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element";
class DemoCard extends PolymerElement { class DemoCard extends PolymerElement {
@@ -80,7 +80,7 @@ class DemoCard extends PolymerElement {
card.removeChild(card.lastChild); card.removeChild(card.lastChild);
} }
const el = this._createCardElement(safeLoad(config.config)[0]); const el = this._createCardElement(load(config.config)[0]);
card.appendChild(el); card.appendChild(el);
this._getSize(el); this._getSize(el);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,4 @@
import { import { html, css, LitElement, TemplateResult } from "lit";
customElement,
html,
css,
LitElement,
TemplateResult,
state,
property,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph"; import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline"; import "../../../src/components/trace/hat-trace-timeline";
@@ -15,6 +7,7 @@ import { HomeAssistant } from "../../../src/types";
import { DemoTrace } from "../data/traces/types"; import { DemoTrace } from "../data/traces/types";
import { basicTrace } from "../data/traces/basic_trace"; import { basicTrace } from "../data/traces/basic_trace";
import { motionLightTrace } from "../data/traces/motion-light-trace"; import { motionLightTrace } from "../data/traces/motion-light-trace";
import { customElement, property, state } from "lit/decorators";
const traces: DemoTrace[] = [basicTrace, motionLightTrace]; const traces: DemoTrace[] = [basicTrace, motionLightTrace];

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../demo/src/stubs/history"; import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";

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"; import "../components/demo-cards";
const CONFIGS = [ const CONFIGS = [

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockTemplate } from "../../../demo/src/stubs/template"; import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createPlantEntities } from "../data/plants"; import { createPlantEntities } from "../data/plants";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,11 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, query } from "lit/decorators";
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";

View File

@@ -1,12 +1,4 @@
import { import { html, css, LitElement, TemplateResult } from "lit";
customElement,
html,
css,
state,
LitElement,
TemplateResult,
property,
} from "lit-element";
import "../../../src/components/ha-formfield"; import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
@@ -23,7 +15,8 @@ import type {
} from "../../../src/panels/config/integrations/ha-config-integrations"; } from "../../../src/panels/config/integrations/ha-config-integrations";
import { DeviceRegistryEntry } from "../../../src/data/device_registry"; import { DeviceRegistryEntry } from "../../../src/data/device_registry";
import { EntityRegistryEntry } from "../../../src/data/entity_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 = ( const createConfigEntry = (
title: string, title: string,
@@ -35,10 +28,11 @@ const createConfigEntry = (
title, title,
source: "zeroconf", source: "zeroconf",
state: "loaded", state: "loaded",
connection_class: "local_push",
supports_options: false, supports_options: false,
supports_unload: true, supports_unload: true,
disabled_by: null, disabled_by: null,
pref_disable_new_entities: false,
pref_disable_polling: false,
reason: null, reason: null,
...override, ...override,
}); });
@@ -71,6 +65,9 @@ const configPanelEntry = createConfigEntry("Config Panel", {
const optionsFlowEntry = createConfigEntry("Options Flow", { const optionsFlowEntry = createConfigEntry("Options Flow", {
supports_options: true, supports_options: true,
}); });
const disabledPollingEntry = createConfigEntry("Disabled Polling", {
pref_disable_polling: true,
});
const setupErrorEntry = createConfigEntry("Setup Error", { const setupErrorEntry = createConfigEntry("Setup Error", {
state: "setup_error", state: "setup_error",
}); });
@@ -143,6 +140,7 @@ const configEntries: Array<{
{ items: [loadedEntry] }, { items: [loadedEntry] },
{ items: [configPanelEntry] }, { items: [configPanelEntry] },
{ items: [optionsFlowEntry] }, { items: [optionsFlowEntry] },
{ items: [disabledPollingEntry] },
{ items: [nameAsDomainEntry] }, { items: [nameAsDomainEntry] },
{ items: [longNameEntry] }, { items: [longNameEntry] },
{ items: [longNonBreakingNameEntry] }, { items: [longNonBreakingNameEntry] },

View File

@@ -1,12 +1,5 @@
import { import { html, LitElement, PropertyValues, TemplateResult } from "lit";
customElement, import { customElement, property, query } from "lit/decorators";
html,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { import {
LightColorModes, LightColorModes,

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button"; 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 "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace"; import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";

View File

@@ -1,12 +1,6 @@
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js"; import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { property } from "lit/decorators";
CSSResultGroup,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
@@ -126,7 +120,7 @@ class HassioAddonRepositoryEl extends LitElement {
} }
private _addonTapped(ev) { private _addonTapped(ev) {
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`); navigate(`/hassio/addon/${ev.currentTarget.addon.slug}`);
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {

View File

@@ -5,12 +5,12 @@ import { mdiDotsVertical } from "@mdi/js";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
state, html,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
} from "lit-element"; TemplateResult,
import { html, TemplateResult } from "lit-html"; } from "lit";
import { property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -138,7 +138,7 @@ class HassioAddonStore extends LitElement {
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
const repositoryUrl = extractSearchParam("repository_url"); const repositoryUrl = extractSearchParam("repository_url");
navigate(this, "/hassio/store", true); navigate("/hassio/store", { replace: true });
if (repositoryUrl) { if (repositoryUrl) {
this._manageRepositories(repositoryUrl); this._manageRepositories(repositoryUrl);
} }

View File

@@ -5,14 +5,12 @@ import "@polymer/paper-listbox/paper-listbox";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
customElement,
html, html,
state,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit";
import { customElement, property, state } from "lit/decorators";
import "web-animations-js/web-animations-next-lite.min"; import "web-animations-js/web-animations-next-lite.min";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";

View File

@@ -3,18 +3,16 @@ import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea"; import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import { DEFAULT_SCHEMA, Type } from "js-yaml";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
customElement,
html, html,
state,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
query,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
@@ -30,6 +28,7 @@ import {
HassioAddonDetails, HassioAddonDetails,
HassioAddonSetOptionParams, HassioAddonSetOptionParams,
setHassioAddonOption, setHassioAddonOption,
validateHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; 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 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") @customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement { class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@@ -128,6 +134,7 @@ class HassioAddonConfig extends LitElement {
></ha-form>` ></ha-form>`
: html` <ha-yaml-editor : html` <ha-yaml-editor
@value-changed=${this._configChanged} @value-changed=${this._configChanged}
.schema=${ADDON_YAML_SCHEMA}
></ha-yaml-editor>`} ></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode || ${!this._yamlMode ||
@@ -272,6 +279,14 @@ class HassioAddonConfig extends LitElement {
this._error = undefined; this._error = undefined;
try { 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, { await setHassioAddonOption(this.hass, this.addon.slug, {
options: this._yamlMode ? this._editor?.value : this._options, options: this._yamlMode ? this._editor?.value : this._options,
}); });

View File

@@ -2,14 +2,12 @@ import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
customElement,
html, html,
state,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";

View File

@@ -1,14 +1,5 @@
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css,
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-markdown"; import "../../../../src/components/ha-markdown";
import { import {
@@ -21,6 +12,7 @@ import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { customElement, property, state } from "lit/decorators";
@customElement("hassio-addon-documentation-tab") @customElement("hassio-addon-documentation-tab")
class HassioAddonDocumentationDashboard extends LitElement { class HassioAddonDocumentationDashboard extends LitElement {

View File

@@ -4,16 +4,8 @@ import {
mdiInformationVariant, mdiInformationVariant,
mdiMathLog, mdiMathLog,
} from "@mdi/js"; } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
@@ -183,7 +175,7 @@ class HassioAddonDashboard extends LitElement {
if (!validAddon) { if (!validAddon) {
this._error = this.supervisor.localize("my.error_addon_not_found"); this._error = this.supervisor.localize("my.error_addon_not_found");
} else { } else {
navigate(this, `/hassio/addon/${requestedAddon}`, true); navigate(`/hassio/addon/${requestedAddon}`, { replace: true });
} }
} }
} }

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 { HassioAddonDetails } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { import {

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";

View File

@@ -14,17 +14,9 @@ import {
mdiPound, mdiPound,
mdiShield, mdiShield,
} from "@mdi/js"; } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup, import { classMap } from "lit/directives/class-map";
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../../src/common/config/version"; import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
@@ -769,7 +761,7 @@ class HassioAddonInfo extends LitElement {
} }
private _openIngress(): void { private _openIngress(): void {
navigate(this, `/hassio/ingress/${this.addon.slug}`); navigate(`/hassio/ingress/${this.addon.slug}`);
} }
private get _computeShowIngressUI(): boolean { private get _computeShowIngressUI(): boolean {
@@ -1059,7 +1051,7 @@ class HassioAddonInfo extends LitElement {
} }
private _openConfiguration(): void { 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> { private async _uninstallClicked(ev: CustomEvent): Promise<void> {

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";

View File

@@ -1,14 +1,6 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import { import {
fetchHassioAddonLogs, fetchHassioAddonLogs,

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
interface State { interface State {
bold: boolean; bold: boolean;

View File

@@ -1,13 +1,6 @@
import { mdiHelpCircle } from "@mdi/js"; import { mdiHelpCircle } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-relative-time"; import "../../../src/components/ha-relative-time";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";

View File

@@ -2,13 +2,8 @@ import "@material/mwc-icon-button/mwc-icon-button";
import { mdiFolderUpload } from "@mdi/js"; import { mdiFolderUpload } from "@mdi/js";
import "@polymer/iron-input/iron-input"; import "@polymer/iron-input/iron-input";
import "@polymer/paper-input/paper-input-container"; import "@polymer/paper-input/paper-input-container";
import { import { html, LitElement, TemplateResult } from "lit";
customElement, import { customElement, state } from "lit/decorators";
html,
state,
LitElement,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-circular-progress"; import "../../../src/components/ha-circular-progress";
import "../../../src/components/ha-file-upload"; import "../../../src/components/ha-file-upload";

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 { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup, import { classMap } from "lit/directives/class-map";
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import "../../../src/components/ha-bar"; import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row"; import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate"; import { roundWithOneDecimal } from "../../../src/util/calculate";
@@ -71,6 +64,7 @@ class SupervisorMetric extends LitElement {
.value { .value {
width: 48px; width: 48px;
padding-right: 4px; 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 { mdiArrowUpBoldCircle, mdiPlay, mdiPuzzle, mdiStop } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
import { compare } from "../../../src/common/string/compare"; import { compare } from "../../../src/common/string/compare";
@@ -24,7 +17,36 @@ class HassioAddons extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean;
protected render(): TemplateResult { protected render(): TemplateResult {
return html`<ha-card
.header=${this.supervisor.localize("dashboard.addons")}
>
<div class="addons" ?narrow=${this.narrow}>
${this.supervisor.supervisor.addons.map(
(addon) => html`<div
class="addon"
@click=${this._addonTapped}
.addon=${addon}
>
<div class="icon">
<div class="overlay">
<ha-svg-icon
.title=${addon.state}
.path=${addon.state === "started" ? mdiPlay : mdiStop}
>
</ha-svg-icon>
</div>
${addon.icon && atLeastVersion(this.hass.config.version, 0, 105)
? html`<img src="/api/hassio/addons/${addon.slug}/icon" />`
: html`<ha-svg-icon .path=${mdiPuzzle}></ha-svg-icon>`}
</div>
<div class="name">${addon.name}</div>
</div>`
)}
</div>
</ha-card>`;
return html` return html`
<div class="content"> <div class="content">
<h1>${this.supervisor.localize("dashboard.addons")}</h1> <h1>${this.supervisor.localize("dashboard.addons")}</h1>
@@ -95,19 +117,52 @@ class HassioAddons extends LitElement {
haStyle, haStyle,
hassioStyle, hassioStyle,
css` css`
ha-card { .addons {
display: grid;
grid-template-columns: repeat(4, auto);
padding-bottom: 16px;
}
.addons[narrow] {
grid-template-columns: repeat(2, auto);
}
.addon {
text-align: center;
max-width: 100px;
padding: 0 8px;
cursor: pointer; cursor: pointer;
} }
.icon > *:not(.overlay) {
position: relative;
max-height: 60px;
max-width: 60px;
margin: auto;
--mdc-icon-size: 60px;
display: flex;
}
.icon {
margin-bottom: 4px;
}
.overlay {
position: absolute;
z-index: 2;
--mdc-icon-size: 24px;
color: var(--secondary-text-color);
background-color: var(--secondary-background-color);
opacity: 0.6;
border-radius: 100%;
margin-left: 12px;
border: 1px var(--secondary-text-color) solid;
}
`, `,
]; ];
} }
private _addonTapped(ev: any): void { 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 { private _openStore(): void {
navigate(this, "/hassio/store"); navigate("/hassio/store");
} }
} }

View File

@@ -1,17 +1,11 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style";
import "./hassio-addons"; import "./hassio-addons";
import "./hassio-update"; import "./hassio-update";
@@ -39,14 +33,17 @@ class HassioDashboard extends LitElement {
<span slot="header"> <span slot="header">
${this.supervisor.localize("panel.dashboard")} ${this.supervisor.localize("panel.dashboard")}
</span> </span>
<div class="content"> <div class="content">
<hassio-update <hassio-update
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
.narrow=${this.narrow}
></hassio-update> ></hassio-update>
<hassio-addons <hassio-addons
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
.narrow=${this.narrow}
></hassio-addons> ></hassio-addons>
</div> </div>
</hass-tabs-subpage> </hass-tabs-subpage>
@@ -56,9 +53,18 @@ class HassioDashboard extends LitElement {
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
hassioStyle,
css` css`
.content { .content {
margin: 0 auto; display: grid;
max-width: 1400px;
justify-content: center;
grid-template-columns: repeat(2, auto);
gap: 16px;
}
.content > * {
display: block;
min-width: 400px;
} }
`, `,
]; ];

View File

@@ -1,14 +1,7 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { mdiHomeAssistant } from "@mdi/js"; import { mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -49,11 +42,15 @@ export class HassioUpdate extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean;
private _pendingUpdates = memoizeOne( private _pendingUpdates = memoizeOne(
(supervisor: Supervisor): number => (supervisor: Supervisor): number =>
Object.keys(supervisor).filter( Object.keys(supervisor).filter(
(value) => supervisor[value].update_available (value) => supervisor[value].update_available
).length ).length +
supervisor.supervisor.addons.filter((addon) => addon.update_available)
.length
); );
protected render(): TemplateResult { protected render(): TemplateResult {
@@ -67,15 +64,38 @@ export class HassioUpdate extends LitElement {
} }
return html` return html`
<div class="content"> <ha-card
<h1> .header="${this.supervisor.localize(
${this.supervisor.localize( "common.update_available",
"common.update_available", "count",
"count", updatesAvailable + 1
updatesAvailable )}
🎉"
>
${this._renderUpdateRow({
type: "os",
heading: "Home Assistant Operating system",
icon: mdiHomeAssistant,
version: "5",
version_latest: "6",
})}
${this.supervisor.addon.addons
.filter((addon) => addon.update_available)
.map((addon) =>
this._renderUpdateRow({
type: "addon",
heading: addon.name,
version: addon.version_latest,
version_latest: addon.version,
image: addon.icon
? `/api/hassio/addons/${addon.slug}/icon`
: undefined,
icon: mdiPuzzle,
})
)} )}
🎉 </ha-card>
</h1> <div class="content">
<h1></h1>
<div class="card-group"> <div class="card-group">
${this._renderUpdateCard( ${this._renderUpdateCard(
"Home Assistant Core", "Home Assistant Core",
@@ -107,6 +127,37 @@ export class HassioUpdate extends LitElement {
`; `;
} }
private _renderUpdateRow(options: {
type: "supervisor" | "os" | "core" | "addon";
heading: string;
version: string;
version_latest: string;
icon?: string;
image?: string;
release_notes?: string;
slug?: string;
}): TemplateResult {
return html`<div class="update-row">
<paper-icon-item>
<div class="icon" slot="item-icon">
${options.image && atLeastVersion(this.hass.config.version, 0, 104)
? html`<img src="${options.image}" />`
: options.icon
? html`<ha-svg-icon .path=${options.icon}></ha-svg-icon>`
: ""}
</div>
<paper-item-body two-line>
${options.heading}
<div secondary>Version ${options.version_latest} is available</div>
</paper-item-body>
</paper-icon-item>
<div class="update-row-actions" ?narrow=${false}>
<mwc-button>Releaese notes</mwc-button>
<mwc-button>Update</mwc-button>
</div>
</div>`;
}
private _renderUpdateCard( private _renderUpdateCard(
name: string, name: string,
key: string, key: string,
@@ -238,31 +289,21 @@ export class HassioUpdate extends LitElement {
haStyle, haStyle,
hassioStyle, hassioStyle,
css` css`
.icon { .update-row,
--mdc-icon-size: 48px; paper-icon-item {
float: right; display: flex;
margin: 0 0 2px 10px; align-items: center;
color: var(--primary-text-color);
} }
.update-heading {
font-size: var(--paper-font-subhead_-_font-size); .update-row {
font-weight: 500; padding: 8px;
margin-bottom: 0.5em; justify-content: space-between;
color: var(--primary-text-color);
} }
.card-content { .icon > * {
height: calc(100% - 47px); max-height: 32px;
box-sizing: border-box; max-width: 32px;
} margin-right: 16px;
.card-actions { --mdc-icon-size: 32px;
text-align: right;
}
a {
text-decoration: none;
}
ha-settings-row {
padding: 0;
--paper-item-body-two-line-min-height: 32px;
} }
`, `,
]; ];

View File

@@ -1,13 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { createCloseHeading } from "../../../../src/components/ha-dialog"; import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-markdown"; import "../../../../src/components/ha-markdown";
import { haStyleDialog } from "../../../../src/resources/styles"; import { haStyleDialog } from "../../../../src/resources/styles";

View File

@@ -6,17 +6,9 @@ import "@material/mwc-tab";
import "@material/mwc-tab-bar"; import "@material/mwc-tab-bar";
import { mdiClose } from "@mdi/js"; import { mdiClose } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup, import { cache } from "lit/directives/cache";
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { cache } from "lit-html/directives/cache";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-dialog";

View File

@@ -3,16 +3,8 @@ import "@material/mwc-icon-button/mwc-icon-button";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDelete } from "@mdi/js"; import { mdiDelete } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import { createCloseHeading } from "../../../../src/components/ha-dialog"; import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-svg-icon"; import "../../../../src/components/ha-svg-icon";
@@ -53,7 +45,7 @@ class HassioRegistriesDialog extends LitElement {
return html` return html`
<ha-dialog <ha-dialog
.open=${this._opened} .open=${this._opened}
@closing=${this.closeDialog} @closed=${this.closeDialog}
scrimClickAction scrimClickAction
escapeKeyAction escapeKeyAction
.heading=${createCloseHeading( .heading=${createCloseHeading(
@@ -252,9 +244,6 @@ class HassioRegistriesDialog extends LitElement {
mwc-list-item span[slot="secondary"] { mwc-list-item span[slot="secondary"] {
color: var(--secondary-text-color); 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 type { PaperInputElement } from "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, query, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
query,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
@@ -76,7 +67,7 @@ class HassioRepositoriesDialog extends LitElement {
return html` return html`
<ha-dialog <ha-dialog
.open=${this._opened} .open=${this._opened}
@closing=${this.closeDialog} @closed=${this.closeDialog}
scrimClickAction scrimClickAction
escapeKeyAction escapeKeyAction
.heading=${createCloseHeading( .heading=${createCloseHeading(
@@ -159,9 +150,6 @@ class HassioRepositoriesDialog extends LitElement {
mwc-button { mwc-button {
margin-left: 8px; margin-left: 8px;
} }
ha-paper-dropdown-menu {
display: block;
}
ha-circular-progress { ha-circular-progress {
display: block; display: block;
margin: 32px; margin: 32px;

View File

@@ -1,99 +1,43 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import { customElement, property, query, state } from "lit/decorators";
import {
css,
CSSResult,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { formatDate } from "../../../../src/common/datetime/format_date";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import { compare } from "../../../../src/common/string/compare";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-checkbox";
import type { HaCheckbox } from "../../../../src/components/ha-checkbox";
import { createCloseHeading } from "../../../../src/components/ha-dialog"; import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-radio";
import type { HaRadio } from "../../../../src/components/ha-radio";
import "../../../../src/components/ha-settings-row";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { import {
createHassioFullSnapshot, createHassioFullSnapshot,
createHassioPartialSnapshot, createHassioPartialSnapshot,
HassioFullSnapshotCreateParams,
HassioPartialSnapshotCreateParams,
HassioSnapshot,
} from "../../../../src/data/hassio/snapshot"; } from "../../../../src/data/hassio/snapshot";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { PolymerChangedEvent } from "../../../../src/polymer-types";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; 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"; import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot";
interface CheckboxItem {
slug: string;
checked: boolean;
name?: string;
version?: string;
}
const folderList = () => [
{
slug: "homeassistant",
checked: true,
},
{ slug: "ssl", checked: true },
{ slug: "share", checked: true },
{ slug: "media", checked: true },
{ slug: "addons/local", checked: true },
];
@customElement("dialog-hassio-create-snapshot") @customElement("dialog-hassio-create-snapshot")
class HassioCreateSnapshotDialog extends LitElement { class HassioCreateSnapshotDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@state() private _snapshotName = "";
@state() private _snapshotPassword = "";
@state() private _snapshotHasPassword = false;
@state() private _snapshotType: HassioSnapshot["type"] = "full";
@state() private _dialogParams?: HassioCreateSnapshotDialogParams; @state() private _dialogParams?: HassioCreateSnapshotDialogParams;
@state() private _addonList: CheckboxItem[] = []; @state() private _error?: string;
@state() private _folderList: CheckboxItem[] = folderList(); @state() private _creatingSnapshot = false;
@state() private _error = ""; @query("supervisor-snapshot-content")
private _snapshotContent!: SupervisorSnapshotContent;
public showDialog(params: HassioCreateSnapshotDialogParams) { public showDialog(params: HassioCreateSnapshotDialogParams) {
this._dialogParams = params; this._dialogParams = params;
this._addonList = this._dialogParams.supervisor.supervisor.addons this._creatingSnapshot = false;
.map((addon) => ({
slug: addon.slug,
name: addon.name,
version: addon.version,
checked: true,
}))
.sort((a, b) => compare(a.name, b.name));
this._snapshotType = "full";
this._error = "";
this._folderList = folderList();
this._snapshotHasPassword = false;
this._snapshotPassword = "";
this._snapshotName = "";
} }
public closeDialog() { public closeDialog() {
this._dialogParams = undefined; this._dialogParams = undefined;
this._creatingSnapshot = false;
this._error = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName }); fireEvent(this, "dialog-closed", { dialog: this.localName });
} }
@@ -104,179 +48,36 @@ class HassioCreateSnapshotDialog extends LitElement {
return html` return html`
<ha-dialog <ha-dialog
open open
@closing=${this.closeDialog} scrimClickAction
@closed=${this.closeDialog}
.heading=${createCloseHeading( .heading=${createCloseHeading(
this.hass, this.hass,
this._dialogParams.supervisor.localize("snapshot.create_snapshot") this._dialogParams.supervisor.localize("snapshot.create_snapshot")
)} )}
> >
<paper-input ${this._creatingSnapshot
name="snapshotName" ? html` <ha-circular-progress active></ha-circular-progress>`
.label=${this._dialogParams.supervisor.localize("snapshot.name")} : html`<supervisor-snapshot-content
.value=${this._snapshotName} .hass=${this.hass}
@value-changed=${this._handleTextValueChanged} .supervisor=${this._dialogParams.supervisor}
>
</paper-input>
<div class="snapshot-types">
<div>
${this._dialogParams.supervisor.localize("snapshot.type")}:
</div>
<ha-formfield
.label=${this._dialogParams.supervisor.localize(
"snapshot.full_snapshot"
)}
> >
<ha-radio </supervisor-snapshot-content>`}
@change=${this._handleRadioValueChanged} ${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
value="full"
name="snapshotType"
.checked=${this._snapshotType === "full"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this._dialogParams.supervisor.localize(
"snapshot.partial_snapshot"
)}
>
<ha-radio
@change=${this._handleRadioValueChanged}
value="partial"
name="snapshotType"
.checked=${this._snapshotType === "partial"}
>
</ha-radio>
</ha-formfield>
</div>
${
this._snapshotType === "full"
? undefined
: html`
${this._dialogParams.supervisor.localize("snapshot.folders")}:
<div class="checkbox-section">
${this._folderList.map(
(folder, idx) => html`
<div class="checkbox-line">
<ha-checkbox
.idx=${idx}
.checked=${folder.checked}
@change=${this._folderChecked}
slot="prefix"
>
</ha-checkbox>
<span>
${this._dialogParams!.supervisor.localize(
`snapshot.folder.${folder.slug}`
)}
</span>
</div>
`
)}
</div>
${this._dialogParams.supervisor.localize("snapshot.addons")}:
<div class="checkbox-section">
${this._addonList.map(
(addon, idx) => html`
<div class="checkbox-line">
<ha-checkbox
.idx=${idx}
.checked=${addon.checked}
@change=${this._addonChecked}
slot="prefix"
>
</ha-checkbox>
<span>
${addon.name}<span class="version">
(${addon.version})
</span>
</span>
</div>
`
)}
</div>
`
}
${this._dialogParams.supervisor.localize("snapshot.security")}:
<div class="checkbox-section">
<div class="checkbox-line">
<ha-checkbox
.checked=${this._snapshotHasPassword}
@change=${this._handleCheckboxValueChanged}
slot="prefix"
>
</ha-checkbox>
<span>
${this._dialogParams.supervisor.localize(
"snapshot.password_protection"
)}
</span>
</span>
</div>
</div>
${
this._snapshotHasPassword
? html`
<paper-input
.label=${this._dialogParams.supervisor.localize(
"snapshot.password"
)}
type="password"
name="snapshotPassword"
.value=${this._snapshotPassword}
@value-changed=${this._handleTextValueChanged}
>
</paper-input>
`
: undefined
}
${
this._error !== ""
? html` <p class="error">${this._error}</p> `
: undefined
}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}> <mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this._dialogParams.supervisor.localize("common.close")} ${this._dialogParams.supervisor.localize("common.close")}
</mwc-button> </mwc-button>
<ha-progress-button slot="primaryAction" @click=${this._createSnapshot}> <mwc-button
.disabled=${this._creatingSnapshot}
slot="primaryAction"
@click=${this._createSnapshot}
>
${this._dialogParams.supervisor.localize("snapshot.create")} ${this._dialogParams.supervisor.localize("snapshot.create")}
</ha-progress-button> </mwc-button>
</ha-dialog> </ha-dialog>
`; `;
} }
private _handleTextValueChanged(ev: PolymerChangedEvent<string>) { private async _createSnapshot(): Promise<void> {
const input = ev.currentTarget as PaperInputElement;
this[`_${input.name}`] = ev.detail.value;
}
private _handleCheckboxValueChanged(ev: CustomEvent) {
const input = ev.currentTarget as HaCheckbox;
this._snapshotHasPassword = input.checked;
}
private _handleRadioValueChanged(ev: CustomEvent) {
const input = ev.currentTarget as HaRadio;
this[`_${input.name}`] = input.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 _createSnapshot(ev: CustomEvent): Promise<void> {
if (this._dialogParams!.supervisor.info.state !== "running") { if (this._dialogParams!.supervisor.info.state !== "running") {
showAlertDialog(this, { showAlertDialog(this, {
title: this._dialogParams!.supervisor.localize( title: this._dialogParams!.supervisor.localize(
@@ -290,40 +91,26 @@ class HassioCreateSnapshotDialog extends LitElement {
}); });
return; return;
} }
const button = ev.currentTarget as any; const snapshotDetails = this._snapshotContent.snapshotDetails();
button.progress = true; this._creatingSnapshot = true;
this._error = ""; this._error = "";
if (this._snapshotHasPassword && !this._snapshotPassword.length) { if (
this._snapshotContent.snapshotHasPassword &&
!this._snapshotContent.snapshotPassword.length
) {
this._error = this._dialogParams!.supervisor.localize( this._error = this._dialogParams!.supervisor.localize(
"snapshot.enter_password" "snapshot.enter_password"
); );
button.progress = false; this._creatingSnapshot = false;
return; return;
} }
const name = this._snapshotName || formatDate(new Date(), this.hass.locale);
try { try {
if (this._snapshotType === "full") { if (this._snapshotContent.snapshotType === "full") {
const data: HassioFullSnapshotCreateParams = { name }; await createHassioFullSnapshot(this.hass, snapshotDetails);
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioFullSnapshot(this.hass, data);
} else { } else {
const data: HassioPartialSnapshotCreateParams = { await createHassioPartialSnapshot(this.hass, snapshotDetails);
name,
folders: this._folderList
.filter((folder) => folder.checked)
.map((folder) => folder.slug),
addons: this._addonList
.filter((addon) => addon.checked)
.map((addon) => addon.slug),
};
if (this._snapshotHasPassword) {
data.password = this._snapshotPassword;
}
await createHassioPartialSnapshot(this.hass, data);
} }
this._dialogParams!.onCreate(); this._dialogParams!.onCreate();
@@ -331,30 +118,17 @@ class HassioCreateSnapshotDialog extends LitElement {
} catch (err) { } catch (err) {
this._error = extractApiErrorMessage(err); this._error = extractApiErrorMessage(err);
} }
button.progress = false; this._creatingSnapshot = false;
} }
static get styles(): CSSResult[] { static get styles(): CSSResultGroup {
return [ return [
haStyle, haStyle,
haStyleDialog, haStyleDialog,
css` css`
.error { ha-circular-progress {
color: var(--error-color);
}
paper-input[type="password"] {
display: block; display: block;
margin: 4px 0 4px 16px; text-align: center;
}
span.version {
color: var(--secondary-text-color);
}
.checkbox-section {
display: grid;
}
.checkbox-line {
display: inline-flex;
align-items: center;
} }
`, `,
]; ];

View File

@@ -1,14 +1,6 @@
import { mdiClose } from "@mdi/js"; import { mdiClose } from "@mdi/js";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-header-bar"; import "../../../../src/components/ha-header-bar";
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager"; import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";

View File

@@ -1,21 +1,13 @@
import "@material/mwc-button"; import { ActionDetail } from "@material/mwc-list";
import { mdiClose, mdiDelete, mdiDownload, mdiHistory } from "@mdi/js"; import "@material/mwc-list/mwc-list-item";
import "@polymer/paper-checkbox/paper-checkbox"; import { mdiDotsVertical } from "@mdi/js";
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import "@polymer/paper-input/paper-input"; import { customElement, property, query, state } from "lit/decorators";
import {
css,
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
import { fireEvent } from "../../../../src/common/dom/fire_event"; 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 "../../../../src/components/ha-svg-icon";
import { getSignedPath } from "../../../../src/data/auth"; import { getSignedPath } from "../../../../src/data/auth";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@@ -23,95 +15,46 @@ import {
fetchHassioSnapshotInfo, fetchHassioSnapshotInfo,
HassioSnapshotDetail, HassioSnapshotDetail,
} from "../../../../src/data/hassio/snapshot"; } from "../../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
} from "../../../../src/dialogs/generic/show-dialog-box"; } 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 { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; 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"; 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) =>
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") @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 hass!: HomeAssistant;
@property({ attribute: false }) public supervisor?: Supervisor;
@state() private _error?: string; @state() private _error?: string;
@state() private _onboarding = false;
@state() private _snapshot?: HassioSnapshotDetail; @state() private _snapshot?: HassioSnapshotDetail;
@state() private _folders!: FolderItem[];
@state() private _addons!: AddonItem[];
@state() private _dialogParams?: HassioSnapshotDialogParams; @state() private _dialogParams?: HassioSnapshotDialogParams;
@state() private _snapshotPassword!: string; @state() private _restoringSnapshot = false;
@state() private _restoreHass = true; @query("supervisor-snapshot-content")
private _snapshotContent!: SupervisorSnapshotContent;
public async showDialog(params: HassioSnapshotDialogParams) { public async showDialog(params: HassioSnapshotDialogParams) {
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug); 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._dialogParams = params;
this._onboarding = params.onboarding ?? false; this._restoringSnapshot = false;
this.supervisor = params.supervisor; }
if (!this._snapshot.homeassistant) {
this._restoreHass = 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 { protected render(): TemplateResult {
@@ -119,121 +62,42 @@ class HassioSnapshotDialog extends LitElement {
return html``; return html``;
} }
return html` return html`
<ha-dialog open @closing=${this._closeDialog} .heading=${true}> <ha-dialog
<div slot="heading"> open
<ha-header-bar> scrimClickAction
<span slot="title"> ${this._computeName} </span> @closed=${this.closeDialog}
<mwc-icon-button slot="actionItems" dialogAction="cancel"> .heading=${createCloseHeading(this.hass, this._computeName)}
<ha-svg-icon .path=${mdiClose}></ha-svg-icon> >
</mwc-icon-button> ${this._restoringSnapshot
</ha-header-bar> ? html` <ha-circular-progress active></ha-circular-progress>`
</div> : html`<supervisor-snapshot-content
<div class="details"> .hass=${this.hass}
${this._snapshot.type === "full" .supervisor=${this._dialogParams.supervisor}
? "Full snapshot" .snapshot=${this._snapshot}
: "Partial snapshot"} >
(${this._computeSize})<br /> </supervisor-snapshot-content>`}
${formatDateTime(new Date(this._snapshot.date), this.hass.locale)} ${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
</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
<span class="version">(${this._snapshot.homeassistant})</span>
</paper-checkbox>`
: ""}
${this._folders.length
? html`
<div>Folders:</div>
<paper-dialog-scrollable class="no-margin-top">
${this._folders.map(
(item) => 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) => html`
<paper-checkbox
.checked=${item.checked}
@change="${(ev: Event) =>
this._updateAddons(
item,
(ev.target as PaperCheckboxElement).checked
)}"
>
${item.name}
<span class="version">(${item.version})</span>
</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> ` : ""}
<div class="button-row" slot="primaryAction"> <mwc-button
<mwc-button @click=${this._partialRestoreClicked}> .disabled=${this._restoringSnapshot}
<ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon> slot="secondaryAction"
Restore Selected @click=${this._restoreClicked}
</mwc-button> >
${!this._onboarding Restore
? html` </mwc-button>
<mwc-button @click=${this._deleteClicked}>
<ha-svg-icon .path=${mdiDelete} class="icon warning"> <ha-button-menu
</ha-svg-icon> fixed
<span class="warning">Delete Snapshot</span> slot="primaryAction"
</mwc-button> @action=${this._handleMenuAction}
` @closed=${(ev: Event) => ev.stopPropagation()}
: ""} >
</div> <mwc-icon-button slot="trigger" alt="menu">
<div class="button-row" slot="secondaryAction"> <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
${this._snapshot.type === "full" </mwc-icon-button>
? html` <mwc-list-item>Download Snapshot</mwc-list-item>
<mwc-button @click=${this._fullRestoreClicked}> <mwc-list-item class="error">Delete Snapshot</mwc-list-item>
<ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon> </ha-button-menu>
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>
</ha-dialog> </ha-dialog>
`; `;
} }
@@ -243,83 +107,47 @@ class HassioSnapshotDialog extends LitElement {
haStyle, haStyle,
haStyleDialog, haStyleDialog,
css` css`
paper-checkbox { ha-svg-icon {
color: var(--primary-text-color);
}
ha-circular-progress {
display: block; display: block;
margin: 4px; text-align: center;
}
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;
}
span.version {
color: var(--secondary-text-color);
}
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);
}
} }
`, `,
]; ];
} }
private _updateFolders(item: FolderItem, value: boolean | null | undefined) { private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
this._folders = this._folders.map((folder) => { switch (ev.detail.index) {
if (folder.slug === item.slug) { case 0:
folder.checked = value; this._downloadClicked();
} break;
return folder; case 1:
}); this._deleteClicked();
break;
}
} }
private _updateAddons(item: AddonItem, value: boolean | null | undefined) { private async _restoreClicked() {
this._addons = this._addons.map((addon) => { const snapshotDetails = this._snapshotContent.snapshotDetails();
if (addon.slug === item.slug) { this._restoringSnapshot = true;
addon.checked = value; if (this._snapshotContent.snapshotType === "full") {
} await this._fullRestoreClicked(snapshotDetails);
return addon; } else {
}); await this._partialRestoreClicked(snapshotDetails);
}
this._restoringSnapshot = false;
} }
private _passwordInput(ev: PolymerChangedEvent<string>) { private async _partialRestoreClicked(snapshotDetails) {
this._snapshotPassword = ev.detail.value;
}
private async _partialRestoreClicked() {
if ( if (
this.supervisor !== undefined && this._dialogParams?.supervisor !== undefined &&
this.supervisor.info.state !== "running" this._dialogParams?.supervisor.info.state !== "running"
) { ) {
await showAlertDialog(this, { await showAlertDialog(this, {
title: "Could not restore snapshot", 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; return;
} }
@@ -333,41 +161,17 @@ class HassioSnapshotDialog extends LitElement {
return; return;
} }
const addons = this._addons if (!this._dialogParams?.onboarding) {
.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) {
this.hass this.hass
.callApi( .callApi(
"POST", "POST",
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`, `hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
data snapshotDetails
) )
.then( .then(
() => { () => {
alert("Snapshot restored!"); this.closeDialog();
this._closeDialog();
}, },
(error) => { (error) => {
this._error = error.body.message; this._error = error.body.message;
@@ -377,20 +181,20 @@ class HassioSnapshotDialog extends LitElement {
fireEvent(this, "restoring"); fireEvent(this, "restoring");
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, { fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(snapshotDetails),
}); });
this._closeDialog(); this.closeDialog();
} }
} }
private async _fullRestoreClicked() { private async _fullRestoreClicked(snapshotDetails) {
if ( if (
this.supervisor !== undefined && this._dialogParams?.supervisor !== undefined &&
this.supervisor.info.state !== "running" this._dialogParams?.supervisor.info.state !== "running"
) { ) {
await showAlertDialog(this, { await showAlertDialog(this, {
title: "Could not restore snapshot", 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; return;
} }
@@ -405,20 +209,16 @@ class HassioSnapshotDialog extends LitElement {
return; return;
} }
const data = this._snapshot!.protected if (!this._dialogParams?.onboarding) {
? { password: this._snapshotPassword }
: undefined;
if (!this._onboarding) {
this.hass this.hass
.callApi( .callApi(
"POST", "POST",
`hassio/snapshots/${this._snapshot!.slug}/restore/full`, `hassio/snapshots/${this._snapshot!.slug}/restore/full`,
data snapshotDetails
) )
.then( .then(
() => { () => {
alert("Snapshot restored!"); this.closeDialog();
this._closeDialog();
}, },
(error) => { (error) => {
this._error = error.body.message; this._error = error.body.message;
@@ -428,9 +228,9 @@ class HassioSnapshotDialog extends LitElement {
fireEvent(this, "restoring"); fireEvent(this, "restoring");
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, { fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(snapshotDetails),
}); });
this._closeDialog(); this.closeDialog();
} }
} }
@@ -453,7 +253,7 @@ class HassioSnapshotDialog extends LitElement {
if (this._dialogParams!.onDelete) { if (this._dialogParams!.onDelete) {
this._dialogParams!.onDelete(); this._dialogParams!.onDelete();
} }
this._closeDialog(); this.closeDialog();
}, },
(error) => { (error) => {
this._error = error.body.message; this._error = error.body.message;
@@ -469,7 +269,9 @@ class HassioSnapshotDialog extends LitElement {
`/api/hassio/snapshots/${this._snapshot!.slug}/download` `/api/hassio/snapshots/${this._snapshot!.slug}/download`
); );
} catch (err) { } catch (err) {
alert(`Error: ${extractApiErrorMessage(err)}`); await showAlertDialog(this, {
text: extractApiErrorMessage(err),
});
return; return;
} }
@@ -486,10 +288,9 @@ class HassioSnapshotDialog extends LitElement {
} }
} }
const name = this._computeName.replace(/[^a-z0-9]+/gi, "_");
const a = document.createElement("a"); const a = document.createElement("a");
a.href = signedPath.path; a.href = signedPath.path;
a.download = `Hass_io_${name}.tar`; a.download = `home_assistant_snapshot_${slugify(this._computeName)}.tar`;
this.shadowRoot!.appendChild(a); this.shadowRoot!.appendChild(a);
a.click(); a.click();
this.shadowRoot!.removeChild(a); this.shadowRoot!.removeChild(a);
@@ -500,18 +301,6 @@ class HassioSnapshotDialog extends LitElement {
? this._snapshot.name || this._snapshot.slug ? this._snapshot.name || this._snapshot.slug
: "Unnamed snapshot"; : "Unnamed snapshot";
} }
private get _computeSize() {
return Math.ceil(this._snapshot!.size * 10) / 10 + " MB";
}
private _closeDialog() {
this._dialogParams = undefined;
this._snapshot = undefined;
this._snapshotPassword = "";
this._folders = [];
this._addons = [];
}
} }
declare global { declare global {

View File

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

View File

@@ -1,13 +1,6 @@
import "@material/mwc-button/mwc-button"; import "@material/mwc-button/mwc-button";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-dialog";

View File

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

View File

@@ -1,8 +1,10 @@
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 { atLeastVersion } from "../../src/common/config/version";
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../src/common/dom/fire_event"; import { fireEvent } from "../../src/common/dom/fire_event";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; 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 { navigate } from "../../src/common/navigate";
import { HassioPanelInfo } from "../../src/data/hassio/supervisor"; import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { Supervisor } from "../../src/data/supervisor/supervisor"; import { Supervisor } from "../../src/data/supervisor/supervisor";
@@ -49,7 +51,7 @@ export class HassioMain extends SupervisorBaseElement {
// Joakim - April 26, 2021 // Joakim - April 26, 2021
// Due to changes in behavior in Google Chrome, we changed navigate to listen on the top element // Due to changes in behavior in Google Chrome, we changed navigate to listen on the top element
top.addEventListener("location-changed", (ev) => mainWindow.addEventListener("location-changed", (ev) =>
// @ts-ignore // @ts-ignore
fireEvent(this, ev.type, ev.detail, { fireEvent(this, ev.type, ev.detail, {
bubbles: false, bubbles: false,
@@ -61,7 +63,7 @@ export class HassioMain extends SupervisorBaseElement {
document.body.addEventListener("click", (ev) => { document.body.addEventListener("click", (ev) => {
const href = isNavigationClick(ev); const href = isNavigationClick(ev);
if (href) { if (href) {
navigate(document.body, href); navigate(href);
} }
}); });
@@ -101,7 +103,7 @@ export class HassioMain extends SupervisorBaseElement {
private _applyTheme() { private _applyTheme() {
let themeName: string; let themeName: string;
let options: Partial<HomeAssistant["selectedTheme"]> | undefined; let themeSettings: Partial<HomeAssistant["selectedTheme"]> | undefined;
if (atLeastVersion(this.hass.config.version, 0, 114)) { if (atLeastVersion(this.hass.config.version, 0, 114)) {
themeName = themeName =
@@ -110,9 +112,9 @@ export class HassioMain extends SupervisorBaseElement {
? this.hass.themes.default_dark_theme! ? this.hass.themes.default_dark_theme!
: this.hass.themes.default_theme); : this.hass.themes.default_theme);
options = this.hass.selectedTheme; themeSettings = this.hass.selectedTheme;
if (themeName === "default" && options?.dark === undefined) { if (themeSettings?.dark === undefined) {
options = { themeSettings = {
...this.hass.selectedTheme, ...this.hass.selectedTheme,
dark: this.hass.themes.darkMode, dark: this.hass.themes.darkMode,
}; };
@@ -127,7 +129,7 @@ export class HassioMain extends SupervisorBaseElement {
this.parentElement, this.parentElement,
this.hass.themes, this.hass.themes,
themeName, themeName,
options themeSettings
); );
} }
} }

View File

@@ -1,11 +1,4 @@
import { import { html, LitElement, TemplateResult } from "lit";
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { sanitizeUrl } from "@braintree/sanitize-url"; import { sanitizeUrl } from "@braintree/sanitize-url";
import { import {
createSearchParam, createSearchParam,
@@ -20,6 +13,7 @@ import {
import { navigate } from "../../src/common/navigate"; import { navigate } from "../../src/common/navigate";
import { HomeAssistant, Route } from "../../src/types"; import { HomeAssistant, Route } from "../../src/types";
import { Supervisor } from "../../src/data/supervisor/supervisor"; import { Supervisor } from "../../src/data/supervisor/supervisor";
import { customElement, property, state } from "lit/decorators";
const REDIRECTS: Redirects = { const REDIRECTS: Redirects = {
supervisor: { supervisor: {
@@ -95,7 +89,7 @@ class HassioMyRedirect extends LitElement {
return; return;
} }
navigate(this, url, true); navigate(url, { replace: true });
} }
protected render(): TemplateResult { 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 { Supervisor } from "../../src/data/supervisor/supervisor";
import { import {
HassRouterPage, HassRouterPage,

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { import {
Supervisor, Supervisor,
supervisorCollection, supervisorCollection,

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 { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { Supervisor } from "../../src/data/supervisor/supervisor"; import { Supervisor } from "../../src/data/supervisor/supervisor";
import { import {

View File

@@ -2,17 +2,16 @@ import { mdiMenu } from "@mdi/js";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
customElement,
html, html,
state,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate"; import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params"; import { extractSearchParam } from "../../../src/common/url/search-params";
import { nextRender } from "../../../src/common/util/render-status";
import { import {
fetchHassioAddonInfo, fetchHassioAddonInfo,
HassioAddonDetails, HassioAddonDetails,
@@ -97,6 +96,7 @@ class HassioIngressView extends LitElement {
text: extractApiErrorMessage(err), text: extractApiErrorMessage(err),
title: requestedAddon, title: requestedAddon,
}); });
await nextRender();
history.back(); history.back();
return; return;
} }
@@ -105,9 +105,10 @@ class HassioIngressView extends LitElement {
text: this.supervisor.localize("my.error_addon_no_ingress"), text: this.supervisor.localize("my.error_addon_no_ingress"),
title: addonInfo.name, title: addonInfo.name,
}); });
await nextRender();
history.back(); history.back();
} else { } else {
navigate(this, `/hassio/ingress/${addonInfo.slug}`, true); navigate(`/hassio/ingress/${addonInfo.slug}`, { replace: true });
} }
} }
} }
@@ -142,6 +143,7 @@ class HassioIngressView extends LitElement {
text: "Unable to fetch add-on info to start Ingress", text: "Unable to fetch add-on info to start Ingress",
title: "Supervisor", title: "Supervisor",
}); });
await nextRender();
history.back(); history.back();
return; return;
} }
@@ -151,6 +153,7 @@ class HassioIngressView extends LitElement {
text: "Add-on does not support Ingress", text: "Add-on does not support Ingress",
title: addon.name, title: addon.name,
}); });
await nextRender();
history.back(); history.back();
return; return;
} }
@@ -160,7 +163,8 @@ class HassioIngressView extends LitElement {
text: "Add-on is not running. Please start it first", text: "Add-on is not running. Please start it first",
title: addon.name, title: addon.name,
}); });
navigate(this, `/hassio/addon/${addon.slug}/info`, true); await nextRender();
navigate(`/hassio/addon/${addon.slug}/info`, { replace: true });
return; return;
} }
@@ -173,6 +177,7 @@ class HassioIngressView extends LitElement {
text: "Unable to create an Ingress session", text: "Unable to create an Ingress session",
title: addon.name, title: addon.name,
}); });
await nextRender();
history.back(); history.back();
return; return;
} }

View File

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

View File

@@ -1,17 +1,17 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list"; import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical, mdiPlus } from "@mdi/js"; import { mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import { import {
css,
CSSResultGroup, CSSResultGroup,
customElement,
html, html,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
state,
TemplateResult, 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 memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import relativeTime from "../../../src/common/datetime/relative_time"; import relativeTime from "../../../src/common/datetime/relative_time";
@@ -19,18 +19,25 @@ import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import { import {
DataTableColumnContainer, DataTableColumnContainer,
RowClickedEvent, RowClickedEvent,
SelectionChangedEvent,
} from "../../../src/components/data-table/ha-data-table"; } from "../../../src/components/data-table/ha-data-table";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-fab"; import "../../../src/components/ha-fab";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { import {
fetchHassioSnapshots, fetchHassioSnapshots,
friendlyFolderName, friendlyFolderName,
HassioSnapshot, HassioSnapshot,
reloadHassioSnapshots, reloadHassioSnapshots,
removeSnapshot,
} from "../../../src/data/hassio/snapshot"; } from "../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box"; import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-tabs-subpage-data-table"; 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 { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot"; import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
@@ -51,10 +58,15 @@ export class HassioSnapshots extends LitElement {
@property({ type: Boolean }) public isWide!: boolean; @property({ type: Boolean }) public isWide!: boolean;
private _firstUpdatedCalled = false; @state() private _selectedSnapshots: string[] = [];
@state() private _snapshots?: HassioSnapshot[] = []; @state() private _snapshots?: HassioSnapshot[] = [];
@query("hass-tabs-subpage-data-table", true)
private _dataTable!: HaTabsSubpageDataTable;
private _firstUpdatedCalled = false;
public connectedCallback(): void { public connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) { if (this.hass && this._firstUpdatedCalled) {
@@ -155,7 +167,9 @@ export class HassioSnapshots extends LitElement {
.data=${this._snapshotData(this._snapshots || [])} .data=${this._snapshotData(this._snapshots || [])}
id="slug" id="slug"
@row-click=${this._handleRowClicked} @row-click=${this._handleRowClicked}
@selection-changed=${this._handleSelectionChanged}
clickable clickable
selectable
hasFab hasFab
main-page main-page
supervisor supervisor
@@ -178,6 +192,45 @@ export class HassioSnapshots extends LitElement {
: ""} : ""}
</ha-button-menu> </ha-button-menu>
${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`
<mwc-button
@click=${this._deleteSelected}
class="warning"
>
${this.supervisor.localize("snapshot.delete_selected")}
</mwc-button>
`
: 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> `
: ""}
<ha-fab <ha-fab
slot="fab" slot="fab"
@click=${this._createSnapshot} @click=${this._createSnapshot}
@@ -201,6 +254,12 @@ export class HassioSnapshots extends LitElement {
} }
} }
private _handleSelectionChanged(
ev: HASSDomEvent<SelectionChangedEvent>
): void {
this._selectedSnapshots = ev.detail.value;
}
private _showUploadSnapshotDialog() { private _showUploadSnapshotDialog() {
showSnapshotUploadDialog(this, { showSnapshotUploadDialog(this, {
showSnapshot: (slug: string) => showSnapshot: (slug: string) =>
@@ -218,6 +277,35 @@ export class HassioSnapshots extends LitElement {
this._snapshots = await fetchHassioSnapshots(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>) { private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const slug = ev.detail.id; const slug = ev.detail.id;
showHassioSnapshotDialog(this, { showHassioSnapshotDialog(this, {
@@ -246,7 +334,45 @@ export class HassioSnapshots extends LitElement {
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [haStyle, hassioStyle]; return [
haStyle,
hassioStyle,
css`
.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);
}
.header-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--secondary-text-color);
position: relative;
top: -4px;
}
.selected-txt {
font-weight: bold;
padding-left: 16px;
color: var(--primary-text-color);
}
.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,5 +1,6 @@
import { Collection, UnsubscribeFunc } from "home-assistant-js-websocket"; import { Collection, UnsubscribeFunc } from "home-assistant-js-websocket";
import { state, 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 { atLeastVersion } from "../../src/common/config/version";
import { computeLocalize } from "../../src/common/translations/localize"; import { computeLocalize } from "../../src/common/translations/localize";
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon"; import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";

View File

@@ -1,15 +1,7 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";

View File

@@ -2,16 +2,9 @@ import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiDotsVertical } from "@mdi/js";
import { safeDump } from "js-yaml"; import { dump } from "js-yaml";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -240,7 +233,7 @@ class HassioHostInfo extends LitElement {
const content = await fetchHassioHardwareInfo(this.hass); const content = await fetchHassioHardwareInfo(this.hass);
showHassioMarkdownDialog(this, { showHassioMarkdownDialog(this, {
title: this.supervisor.localize("system.host.hardware"), title: this.supervisor.localize("system.host.hardware"),
content: `<pre>${safeDump(content, { indent: 2 })}</pre>`, content: `<pre>${dump(content, { indent: 2 })}</pre>`,
}); });
} catch (err) { } catch (err) {
showAlertDialog(this, { showAlertDialog(this, {

View File

@@ -1,13 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";

View File

@@ -2,16 +2,8 @@ import "@material/mwc-button";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property, state } from "lit/decorators";
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../src/data/hassio/common";

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
css, import { customElement, property } from "lit/decorators";
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";

View File

@@ -16,13 +16,13 @@
"lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md", "lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md",
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types", "lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types",
"format": "yarn run format:eslint && yarn run format:prettier", "format": "yarn run format:eslint && yarn run format:prettier",
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts", "mocha": "ts-mocha -p test-mocha/tsconfig.test.json \"test-mocha/**/*.ts\"",
"test": "yarn run lint && yarn run mocha" "test": "yarn run lint && yarn run mocha"
}, },
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)", "author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^5.0.0", "@braintree/sanitize-url": "^5.0.1",
"@codemirror/commands": "^0.18.0", "@codemirror/commands": "^0.18.0",
"@codemirror/gutter": "^0.18.0", "@codemirror/gutter": "^0.18.0",
"@codemirror/highlight": "^0.18.0", "@codemirror/highlight": "^0.18.0",
@@ -35,29 +35,30 @@
"@codemirror/text": "^0.18.0", "@codemirror/text": "^0.18.0",
"@codemirror/view": "^0.18.0", "@codemirror/view": "^0.18.0",
"@formatjs/intl-getcanonicallocales": "^1.5.10", "@formatjs/intl-getcanonicallocales": "^1.5.10",
"@formatjs/intl-locale": "^2.4.24", "@formatjs/intl-locale": "^2.4.28",
"@formatjs/intl-pluralrules": "^4.0.18", "@formatjs/intl-pluralrules": "^4.0.22",
"@fullcalendar/common": "5.1.0", "@fullcalendar/common": "5.1.0",
"@fullcalendar/core": "5.1.0", "@fullcalendar/core": "5.1.0",
"@fullcalendar/daygrid": "5.1.0", "@fullcalendar/daygrid": "5.1.0",
"@fullcalendar/interaction": "5.1.0", "@fullcalendar/interaction": "5.1.0",
"@fullcalendar/list": "5.1.0", "@fullcalendar/list": "5.1.0",
"@material/chips": "=9.0.0-canary.1c156d69d.0", "@lit-labs/virtualizer": "^0.6.0",
"@material/mwc-button": "^0.20.0", "@material/chips": "=12.0.0-canary.1a8d06483.0",
"@material/mwc-checkbox": "^0.20.0", "@material/mwc-button": "canary",
"@material/mwc-circular-progress": "^0.20.0", "@material/mwc-checkbox": "canary",
"@material/mwc-dialog": "^0.20.0", "@material/mwc-circular-progress": "canary",
"@material/mwc-fab": "^0.20.0", "@material/mwc-dialog": "canary",
"@material/mwc-formfield": "^0.20.0", "@material/mwc-fab": "canary",
"@material/mwc-icon-button": "^0.20.0", "@material/mwc-formfield": "canary",
"@material/mwc-list": "^0.20.0", "@material/mwc-icon-button": "canary",
"@material/mwc-menu": "^0.20.0", "@material/mwc-list": "canary",
"@material/mwc-radio": "^0.20.0", "@material/mwc-menu": "canary",
"@material/mwc-ripple": "^0.20.0", "@material/mwc-radio": "canary",
"@material/mwc-switch": "^0.20.0", "@material/mwc-ripple": "canary",
"@material/mwc-tab": "^0.20.0", "@material/mwc-switch": "canary",
"@material/mwc-tab-bar": "^0.20.0", "@material/mwc-tab": "canary",
"@material/top-app-bar": "=9.0.0-canary.1c156d69d.0", "@material/mwc-tab-bar": "canary",
"@material/top-app-bar": "=12.0.0-canary.1a8d06483.0",
"@mdi/js": "5.9.55", "@mdi/js": "5.9.55",
"@mdi/svg": "5.9.55", "@mdi/svg": "5.9.55",
"@polymer/app-layout": "^3.0.2", "@polymer/app-layout": "^3.0.2",
@@ -70,7 +71,6 @@
"@polymer/iron-label": "^3.0.1", "@polymer/iron-label": "^3.0.1",
"@polymer/iron-overlay-behavior": "^3.0.2", "@polymer/iron-overlay-behavior": "^3.0.2",
"@polymer/iron-resizable-behavior": "^3.0.1", "@polymer/iron-resizable-behavior": "^3.0.1",
"@polymer/paper-card": "^3.0.1",
"@polymer/paper-checkbox": "^3.1.0", "@polymer/paper-checkbox": "^3.1.0",
"@polymer/paper-dialog": "^3.0.1", "@polymer/paper-dialog": "^3.0.1",
"@polymer/paper-dialog-behavior": "^3.0.1", "@polymer/paper-dialog-behavior": "^3.0.1",
@@ -100,27 +100,26 @@
"@webcomponents/webcomponentsjs": "^2.2.7", "@webcomponents/webcomponentsjs": "^2.2.7",
"chart.js": "^2.9.4", "chart.js": "^2.9.4",
"chartjs-chart-timeline": "^0.4.0", "chartjs-chart-timeline": "^0.4.0",
"comlink": "^4.3.0", "comlink": "^4.3.1",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"cropperjs": "^1.5.7", "cropperjs": "^1.5.11",
"deep-clone-simple": "^1.1.1", "deep-clone-simple": "^1.1.1",
"deep-freeze": "^0.0.1", "deep-freeze": "^0.0.1",
"fecha": "^4.2.0", "fecha": "^4.2.0",
"fuse.js": "^6.0.0", "fuse.js": "^6.0.0",
"google-timezones-json": "^1.0.2", "google-timezones-json": "^1.0.2",
"hls.js": "^1.0.3", "hls.js": "^1.0.4",
"home-assistant-js-websocket": "^5.10.0", "home-assistant-js-websocket": "^5.10.0",
"idb-keyval": "^3.2.0", "idb-keyval": "^5.0.5",
"intl-messageformat": "^9.6.13", "intl-messageformat": "^9.6.16",
"js-yaml": "^3.13.1", "js-yaml": "^4.1.0",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"leaflet-draw": "^1.0.4", "leaflet-draw": "^1.0.4",
"lit-element": "2.5.1", "lit": "^2.0.0-rc.2",
"lit-html": "1.4.1", "lit-vaadin-helpers": "^0.1.3",
"lit-virtualizer": "^0.4.2", "marked": "^2.0.5",
"marked": "2.0.0",
"mdn-polyfills": "^5.16.0", "mdn-polyfills": "^5.16.0",
"memoize-one": "^5.0.2", "memoize-one": "^5.2.1",
"node-vibrant": "3.2.1-alpha.1", "node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "^0.3.1", "proxy-polyfill": "^0.3.1",
"punycode": "^2.1.1", "punycode": "^2.1.1",
@@ -130,12 +129,12 @@
"roboto-fontface": "^0.10.0", "roboto-fontface": "^0.10.0",
"sortablejs": "^1.10.2", "sortablejs": "^1.10.2",
"superstruct": "^0.15.2", "superstruct": "^0.15.2",
"tinykeys": "^1.1.1", "tinykeys": "^1.1.3",
"tsparticles": "^1.19.2", "tsparticles": "^1.19.2",
"unfetch": "^4.1.0", "unfetch": "^4.1.0",
"vis-data": "^7.1.1", "vis-data": "^7.1.2",
"vis-network": "^8.5.4", "vis-network": "^8.5.4",
"vue": "^2.6.11", "vue": "^2.6.12",
"vue2-daterange-picker": "^0.5.1", "vue2-daterange-picker": "^0.5.1",
"web-animations-js": "^2.3.2", "web-animations-js": "^2.3.2",
"workbox-cacheable-response": "^6.1.5", "workbox-cacheable-response": "^6.1.5",
@@ -147,7 +146,7 @@
"xss": "^1.0.9" "xss": "^1.0.9"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.0", "@babel/core": "^7.14.3",
"@babel/plugin-external-helpers": "^7.12.13", "@babel/plugin-external-helpers": "^7.12.13",
"@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-decorators": "^7.13.15", "@babel/plugin-proposal-decorators": "^7.13.15",
@@ -156,7 +155,7 @@
"@babel/plugin-proposal-optional-chaining": "^7.13.12", "@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/preset-env": "^7.14.0", "@babel/preset-env": "^7.14.2",
"@babel/preset-typescript": "^7.13.0", "@babel/preset-typescript": "^7.13.0",
"@koa/cors": "^3.1.0", "@koa/cors": "^3.1.0",
"@open-wc/dev-server-hmr": "^0.0.2", "@open-wc/dev-server-hmr": "^0.0.2",
@@ -165,16 +164,13 @@
"@rollup/plugin-json": "^4.0.3", "@rollup/plugin-json": "^4.0.3",
"@rollup/plugin-node-resolve": "^7.1.3", "@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-replace": "^2.3.2", "@rollup/plugin-replace": "^2.3.2",
"@types/chai": "^4.1.7", "@types/chromecast-caf-receiver": "5.0.12",
"@types/chromecast-caf-receiver": "^5.0.11",
"@types/chromecast-caf-sender": "^1.0.3", "@types/chromecast-caf-sender": "^1.0.3",
"@types/js-yaml": "^3.12.1", "@types/js-yaml": "^4.0.1",
"@types/leaflet": "^1.7.0", "@types/leaflet": "^1.7.0",
"@types/leaflet-draw": "^1.0.3", "@types/leaflet-draw": "^1.0.3",
"@types/marked": "^1.2.2", "@types/marked": "^2.0.3",
"@types/memoize-one": "4.1.0", "@types/mocha": "^8.2.2",
"@types/mocha": "^7.0.2",
"@types/resize-observer-browser": "^0.1.3",
"@types/sortablejs": "^1.10.6", "@types/sortablejs": "^1.10.6",
"@types/webspeechapi": "^0.0.29", "@types/webspeechapi": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.22.0",
@@ -182,7 +178,7 @@
"@web/dev-server": "^0.0.24", "@web/dev-server": "^0.0.24",
"@web/dev-server-rollup": "^0.2.11", "@web/dev-server-rollup": "^0.2.11",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"chai": "^4.2.0", "chai": "^4.3.4",
"cpx": "^1.5.0", "cpx": "^1.5.0",
"del": "^4.0.0", "del": "^4.0.0",
"eslint": "^7.25.0", "eslint": "^7.25.0",
@@ -196,7 +192,7 @@
"eslint-plugin-wc": "^1.3.0", "eslint-plugin-wc": "^1.3.0",
"fancy-log": "^1.3.3", "fancy-log": "^1.3.3",
"fs-extra": "^7.0.1", "fs-extra": "^7.0.1",
"gulp": "^4.0.0", "gulp": "^4.0.2",
"gulp-foreach": "^0.1.0", "gulp-foreach": "^0.1.0",
"gulp-json-transform": "^0.4.6", "gulp-json-transform": "^0.4.6",
"gulp-merge-json": "^1.3.1", "gulp-merge-json": "^1.3.1",
@@ -210,7 +206,7 @@
"magic-string": "^0.25.7", "magic-string": "^0.25.7",
"map-stream": "^0.0.7", "map-stream": "^0.0.7",
"merge-stream": "^1.0.1", "merge-stream": "^1.0.1",
"mocha": "^7.2.0", "mocha": "^8.4.0",
"object-hash": "^2.0.3", "object-hash": "^2.0.3",
"open": "^7.0.4", "open": "^7.0.4",
"prettier": "^2.0.4", "prettier": "^2.0.4",
@@ -220,13 +216,13 @@
"rollup-plugin-string": "^3.0.0", "rollup-plugin-string": "^3.0.0",
"rollup-plugin-terser": "^5.3.0", "rollup-plugin-terser": "^5.3.0",
"rollup-plugin-visualizer": "^4.0.4", "rollup-plugin-visualizer": "^4.0.4",
"serve": "^11.3.0", "serve": "^11.3.2",
"sinon": "^7.3.1", "sinon": "^11.0.0",
"source-map-url": "^0.4.0", "source-map-url": "^0.4.0",
"systemjs": "^6.3.2", "systemjs": "^6.3.2",
"terser-webpack-plugin": "^5.1.1", "terser-webpack-plugin": "^5.1.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"ts-mocha": "^7.0.0", "ts-mocha": "^8.0.0",
"typescript": "^4.2.4", "typescript": "^4.2.4",
"vinyl-buffer": "^1.0.1", "vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0", "vinyl-source-stream": "^2.0.0",
@@ -241,8 +237,8 @@
"resolutions": { "resolutions": {
"@webcomponents/webcomponentsjs": "^2.2.10", "@webcomponents/webcomponentsjs": "^2.2.10",
"@polymer/polymer": "3.1.0", "@polymer/polymer": "3.1.0",
"lit-html": "1.4.1", "lit-html": "2.0.0-rc.3",
"lit-element": "2.5.1" "lit-element": "3.0.0-rc.2"
}, },
"main": "src/home-assistant.js", "main": "src/home-assistant.js",
"husky": { "husky": {

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name="home-assistant-frontend", name="home-assistant-frontend",
version="20210517.0", version="20210601.1",
description="The Home Assistant frontend", description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer", url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors", author="The Home Assistant Authors",

View File

@@ -3,12 +3,11 @@ import {
css, css,
CSSResultGroup, CSSResultGroup,
html, html,
state,
LitElement, LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit";
import { property, state } from "lit/decorators";
import "../components/ha-form/ha-form"; import "../components/ha-form/ha-form";
import "../components/ha-markdown"; import "../components/ha-markdown";
import { AuthProvider } from "../data/auth"; import { AuthProvider } from "../data/auth";

View File

@@ -1,12 +1,5 @@
import { import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
css, import { property, state } from "lit/decorators";
CSSResultGroup,
html,
state,
LitElement,
property,
PropertyValues,
} from "lit-element";
import punycode from "punycode"; import punycode from "punycode";
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
import { extractSearchParamsObject } from "../common/url/search-params"; import { extractSearchParamsObject } from "../common/url/search-params";

View File

@@ -1,6 +1,7 @@
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { html, LitElement, property } from "lit-element"; import { html, LitElement } from "lit";
import { property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import "../components/ha-icon-next"; import "../components/ha-icon-next";
import { AuthProvider } from "../data/auth"; import { AuthProvider } from "../data/auth";

View File

@@ -1,21 +1,32 @@
import { format } from "fecha"; import { format } from "fecha";
import { FrontendTranslationData } from "../../data/translation"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleDateStringSupportsOptions } from "./check_options_support"; import { toLocaleDateStringSupportsOptions } from "./check_options_support";
const formatDateMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
year: "numeric",
month: "long",
day: "numeric",
})
);
export const formatDate = toLocaleDateStringSupportsOptions export const formatDate = toLocaleDateStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleDateString(locales.language, { formatDateMem(locale).format(dateObj)
year: "numeric",
month: "long",
day: "numeric",
})
: (dateObj: Date) => format(dateObj, "longDate"); : (dateObj: Date) => format(dateObj, "longDate");
const formatDateWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
month: "long",
day: "numeric",
})
);
export const formatDateWeekday = toLocaleDateStringSupportsOptions export const formatDateWeekday = toLocaleDateStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleDateString(locales.language, { formatDateWeekdayMem(locale).format(dateObj)
weekday: "long",
month: "short",
day: "numeric",
})
: (dateObj: Date) => format(dateObj, "dddd, MMM D"); : (dateObj: Date) => format(dateObj, "dddd, MMM D");

View File

@@ -1,26 +1,42 @@
import { format } from "fecha"; import { format } from "fecha";
import { FrontendTranslationData } from "../../data/translation"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleStringSupportsOptions } from "./check_options_support"; import { toLocaleStringSupportsOptions } from "./check_options_support";
import { useAmPm } from "./use_am_pm";
const formatDateTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatDateTime = toLocaleStringSupportsOptions export const formatDateTime = toLocaleStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleString(locales.language, { formatDateTimeMem(locale).format(dateObj)
year: "numeric", : (dateObj: Date, locale: FrontendLocaleData) =>
month: "long", format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
day: "numeric",
hour: "numeric", const formatDateTimeWithSecondsMem = memoizeOne(
minute: "2-digit", (locale: FrontendLocaleData) =>
}) new Intl.DateTimeFormat(locale.language, {
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm"); year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleString(locales.language, { formatDateTimeWithSecondsMem(locale).format(dateObj)
year: "numeric", : (dateObj: Date, locale: FrontendLocaleData) =>
month: "long", format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
})
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm:ss");

View File

@@ -1,29 +1,52 @@
import { format } from "fecha"; import { format } from "fecha";
import { FrontendTranslationData } from "../../data/translation"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleTimeStringSupportsOptions } from "./check_options_support"; import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
import { useAmPm } from "./use_am_pm";
const formatTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
hour: "numeric",
minute: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatTime = toLocaleTimeStringSupportsOptions export const formatTime = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleTimeString(locales.language, { formatTimeMem(locale).format(dateObj)
hour: "numeric", : (dateObj: Date, locale: FrontendLocaleData) =>
minute: "2-digit", format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
})
: (dateObj: Date) => format(dateObj, "shortTime"); const formatTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleTimeString(locales.language, { formatTimeWithSecondsMem(locale).format(dateObj)
hour: "numeric", : (dateObj: Date, locale: FrontendLocaleData) =>
minute: "2-digit", format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
second: "2-digit",
}) const formatTimeWeekdayMem = memoizeOne(
: (dateObj: Date) => format(dateObj, "mediumTime"); (locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
hour: "numeric",
minute: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) => ? (dateObj: Date, locale: FrontendLocaleData) =>
dateObj.toLocaleTimeString(locales.language, { formatTimeWeekdayMem(locale).format(dateObj)
weekday: "long", : (dateObj: Date, locale: FrontendLocaleData) =>
hour: "numeric", format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");
minute: "2-digit",
})
: (dateObj: Date) => format(dateObj, "dddd, HH:mm");

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