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,
"__VERSION__": false,
"__STATIC_PATH__": false,
"Polymer": true,
"webkitSpeechRecognition": false,
"ResizeObserver": false
"Polymer": true
},
"env": {
"browser": true,
@@ -106,5 +104,6 @@
"lit/attribute-value-entities": 0
},
"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-translations/*
hass_frontend/*
dist
# yarn
.yarn
yarn-error.log
node_modules/*
npm-debug.log
.DS_Store
hass_frontend/*
.reify-cache
# Python stuff
*.py[cod]
@@ -14,11 +21,8 @@ hass_frontend/*
# venv stuff
pyvenv.cfg
pip-selfcheck.json
venv
venv/*
.venv
lib
bin
dist
# vscode
.vscode/*
@@ -31,9 +35,8 @@ src/cast/dev_const.ts
# Secrets
.lokalise_token
yarn-error.log
#asdf
# asdf
.tool-versions
# 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,
rootDir: paths.polymer_dir,
}),
commonjs({
namedExports: {
"js-yaml": ["safeDump", "safeLoad"],
},
}),
commonjs(),
json(),
babel({
...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
new webpack.NormalModuleReplacementPlugin(
new RegExp(
require.resolve(
"lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
path.resolve(
paths.polymer_dir,
"src/resources/lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
)
),
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
@@ -126,6 +127,11 @@ const createWebpackConfig = ({
].filter(Boolean),
resolve: {
extensions: [".ts", ".js", ".json"],
alias: {
"lit/decorators$": "lit/decorators.js",
"lit/directive$": "lit/directive.js",
"lit/polyfill-support$": "lit/polyfill-support.js",
},
},
output: {
filename: ({ chunk }) => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,8 @@ import {
getAuth,
UnsubscribeFunc,
} 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 {
ConnectMessage,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import { html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../components/demo-cards";
const CONFIGS = [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,18 +3,16 @@ import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import { DEFAULT_SCHEMA, Type } from "js-yaml";
import {
css,
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
@@ -30,6 +28,7 @@ import {
HassioAddonDetails,
HassioAddonSetOptionParams,
setHassioAddonOption,
validateHassioAddonOption,
} from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@@ -41,6 +40,13 @@ import { hassioStyle } from "../../resources/hassio-style";
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
new Type("!secret", {
kind: "scalar",
construct: (data) => `!secret ${data}`,
}),
]);
@customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@@ -128,6 +134,7 @@ class HassioAddonConfig extends LitElement {
></ha-form>`
: html` <ha-yaml-editor
@value-changed=${this._configChanged}
.schema=${ADDON_YAML_SCHEMA}
></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode ||
@@ -272,6 +279,14 @@ class HassioAddonConfig extends LitElement {
this._error = undefined;
try {
const validation = await validateHassioAddonOption(
this.hass,
this.addon.slug,
this._editor?.value
);
if (!validation.valid) {
throw Error(validation.message);
}
await setHassioAddonOption(this.hass, this.addon.slug, {
options: this._yamlMode ? this._editor?.value : this._options,
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,6 @@
import {
css,
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate";
@@ -71,6 +64,7 @@ class SupervisorMetric extends LitElement {
.value {
width: 48px;
padding-right: 4px;
flex-shrink: 0;
}
`;
}

View File

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

View File

@@ -1,13 +1,6 @@
import { mdiArrowUpBoldCircle, mdiPuzzle } from "@mdi/js";
import {
css,
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { mdiArrowUpBoldCircle, mdiPlay, mdiPuzzle, mdiStop } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate";
import { compare } from "../../../src/common/string/compare";
@@ -24,7 +17,36 @@ class HassioAddons extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean;
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`
<div class="content">
<h1>${this.supervisor.localize("dashboard.addons")}</h1>
@@ -95,19 +117,52 @@ class HassioAddons extends LitElement {
haStyle,
hassioStyle,
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;
}
.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 {
navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}/info`);
navigate(`/hassio/addon/${ev.currentTarget.addon.slug}/info`);
}
private _openStore(): void {
navigate(this, "/hassio/store");
navigate("/hassio/store");
}
}

View File

@@ -1,17 +1,11 @@
import {
css,
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style";
import "./hassio-addons";
import "./hassio-update";
@@ -39,14 +33,17 @@ class HassioDashboard extends LitElement {
<span slot="header">
${this.supervisor.localize("panel.dashboard")}
</span>
<div class="content">
<hassio-update
.hass=${this.hass}
.supervisor=${this.supervisor}
.narrow=${this.narrow}
></hassio-update>
<hassio-addons
.hass=${this.hass}
.supervisor=${this.supervisor}
.narrow=${this.narrow}
></hassio-addons>
</div>
</hass-tabs-subpage>
@@ -56,9 +53,18 @@ class HassioDashboard extends LitElement {
static get styles(): CSSResultGroup {
return [
haStyle,
hassioStyle,
css`
.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 { mdiHomeAssistant } from "@mdi/js";
import {
css,
CSSResultGroup,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { mdiHomeAssistant, mdiPuzzle } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
@@ -49,11 +42,15 @@ export class HassioUpdate extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean;
private _pendingUpdates = memoizeOne(
(supervisor: Supervisor): number =>
Object.keys(supervisor).filter(
(value) => supervisor[value].update_available
).length
).length +
supervisor.supervisor.addons.filter((addon) => addon.update_available)
.length
);
protected render(): TemplateResult {
@@ -67,15 +64,38 @@ export class HassioUpdate extends LitElement {
}
return html`
<div class="content">
<h1>
${this.supervisor.localize(
"common.update_available",
"count",
updatesAvailable
<ha-card
.header="${this.supervisor.localize(
"common.update_available",
"count",
updatesAvailable + 1
)}
🎉"
>
${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,
})
)}
🎉
</h1>
</ha-card>
<div class="content">
<h1></h1>
<div class="card-group">
${this._renderUpdateCard(
"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(
name: string,
key: string,
@@ -238,31 +289,21 @@ export class HassioUpdate extends LitElement {
haStyle,
hassioStyle,
css`
.icon {
--mdc-icon-size: 48px;
float: right;
margin: 0 0 2px 10px;
color: var(--primary-text-color);
.update-row,
paper-icon-item {
display: flex;
align-items: center;
}
.update-heading {
font-size: var(--paper-font-subhead_-_font-size);
font-weight: 500;
margin-bottom: 0.5em;
color: var(--primary-text-color);
.update-row {
padding: 8px;
justify-content: space-between;
}
.card-content {
height: calc(100% - 47px);
box-sizing: border-box;
}
.card-actions {
text-align: right;
}
a {
text-decoration: none;
}
ha-settings-row {
padding: 0;
--paper-item-body-two-line-min-height: 32px;
.icon > * {
max-height: 32px;
max-width: 32px;
margin-right: 16px;
--mdc-icon-size: 32px;
}
`,
];

View File

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

View File

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

View File

@@ -3,16 +3,8 @@ import "@material/mwc-icon-button/mwc-icon-button";
import "@material/mwc-list/mwc-list-item";
import { mdiDelete } from "@mdi/js";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import {
css,
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-circular-progress";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-svg-icon";
@@ -53,7 +45,7 @@ class HassioRegistriesDialog extends LitElement {
return html`
<ha-dialog
.open=${this._opened}
@closing=${this.closeDialog}
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
@@ -252,9 +244,6 @@ class HassioRegistriesDialog extends LitElement {
mwc-list-item span[slot="secondary"] {
color: var(--secondary-text-color);
}
ha-paper-dropdown-menu {
display: block;
}
`,
];
}

View File

@@ -5,17 +5,8 @@ import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import {
css,
CSSResultGroup,
customElement,
html,
state,
LitElement,
property,
query,
TemplateResult,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress";
@@ -76,7 +67,7 @@ class HassioRepositoriesDialog extends LitElement {
return html`
<ha-dialog
.open=${this._opened}
@closing=${this.closeDialog}
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
@@ -159,9 +150,6 @@ class HassioRepositoriesDialog extends LitElement {
mwc-button {
margin-left: 8px;
}
ha-paper-dropdown-menu {
display: block;
}
ha-circular-progress {
display: block;
margin: 32px;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,17 +1,17 @@
import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical, mdiPlus } from "@mdi/js";
import { mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
import {
css,
CSSResultGroup,
customElement,
html,
LitElement,
property,
PropertyValues,
state,
TemplateResult,
} from "lit-element";
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import relativeTime from "../../../src/common/datetime/relative_time";
@@ -19,18 +19,25 @@ import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
} from "../../../src/components/data-table/ha-data-table";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-fab";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
fetchHassioSnapshots,
friendlyFolderName,
HassioSnapshot,
reloadHassioSnapshots,
removeSnapshot,
} from "../../../src/data/hassio/snapshot";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-tabs-subpage-data-table";
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
@@ -51,10 +58,15 @@ export class HassioSnapshots extends LitElement {
@property({ type: Boolean }) public isWide!: boolean;
private _firstUpdatedCalled = false;
@state() private _selectedSnapshots: string[] = [];
@state() private _snapshots?: HassioSnapshot[] = [];
@query("hass-tabs-subpage-data-table", true)
private _dataTable!: HaTabsSubpageDataTable;
private _firstUpdatedCalled = false;
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
@@ -155,7 +167,9 @@ export class HassioSnapshots extends LitElement {
.data=${this._snapshotData(this._snapshots || [])}
id="slug"
@row-click=${this._handleRowClicked}
@selection-changed=${this._handleSelectionChanged}
clickable
selectable
hasFab
main-page
supervisor
@@ -178,6 +192,45 @@ export class HassioSnapshots extends LitElement {
: ""}
</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
slot="fab"
@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() {
showSnapshotUploadDialog(this, {
showSnapshot: (slug: string) =>
@@ -218,6 +277,35 @@ export class HassioSnapshots extends LitElement {
this._snapshots = await fetchHassioSnapshots(this.hass);
}
private async _deleteSelected() {
const confirm = await showConfirmationDialog(this, {
title: this.supervisor.localize("snapshot.delete_snapshot_title"),
text: this.supervisor.localize("snapshot.delete_snapshot_text", {
number: this._selectedSnapshots.length,
}),
confirmText: this.supervisor.localize("snapshot.delete_snapshot_confirm"),
});
if (!confirm) {
return;
}
try {
await Promise.all(
this._selectedSnapshots.map((slug) => removeSnapshot(this.hass, slug))
);
} catch (err) {
showAlertDialog(this, {
title: this.supervisor.localize("snapshot.failed_to_delete"),
text: extractApiErrorMessage(err),
});
return;
}
await reloadHassioSnapshots(this.hass);
this._snapshots = await fetchHassioSnapshots(this.hass);
this._dataTable.clearSelection();
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const slug = ev.detail.id;
showHassioSnapshotDialog(this, {
@@ -246,7 +334,45 @@ export class HassioSnapshots extends LitElement {
}
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 { 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 { computeLocalize } from "../../src/common/translations/localize";
import { fetchHassioAddonsInfo } from "../../src/data/hassio/addon";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,5 @@
import {
css,
CSSResultGroup,
html,
state,
LitElement,
property,
PropertyValues,
} from "lit-element";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators";
import punycode from "punycode";
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
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-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 "../components/ha-icon-next";
import { AuthProvider } from "../data/auth";

View File

@@ -1,21 +1,32 @@
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";
const formatDateMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
year: "numeric",
month: "long",
day: "numeric",
})
);
export const formatDate = toLocaleDateStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleDateString(locales.language, {
year: "numeric",
month: "long",
day: "numeric",
})
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMem(locale).format(dateObj)
: (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
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleDateString(locales.language, {
weekday: "long",
month: "short",
day: "numeric",
})
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "dddd, MMM D");

View File

@@ -1,26 +1,42 @@
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 { 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
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleString(locales.language, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "2-digit",
})
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm");
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
const formatDateTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleString(locales.language, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
})
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm:ss");
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeWithSecondsMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");

View File

@@ -1,29 +1,52 @@
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 { 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
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleTimeString(locales.language, {
hour: "numeric",
minute: "2-digit",
})
: (dateObj: Date) => format(dateObj, "shortTime");
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
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
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleTimeString(locales.language, {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
})
: (dateObj: Date) => format(dateObj, "mediumTime");
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWithSecondsMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
const formatTimeWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
hour: "numeric",
minute: "2-digit",
hour12: useAmPm(locale),
})
);
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locales: FrontendTranslationData) =>
dateObj.toLocaleTimeString(locales.language, {
weekday: "long",
hour: "numeric",
minute: "2-digit",
})
: (dateObj: Date) => format(dateObj, "dddd, HH:mm");
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWeekdayMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");

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