Merge pull request #10109 from home-assistant/dev

20210930.0
This commit is contained in:
Bram Kragten 2021-09-30 12:57:53 +02:00 committed by GitHub
commit bc5010a953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
453 changed files with 8795 additions and 6377 deletions

View File

@ -1,9 +1,10 @@
{
"extends": [
"airbnb-base",
"airbnb-typescript/base",
"plugin:@typescript-eslint/recommended",
"plugin:wc/recommended",
"plugin:lit/recommended",
"plugin:lit/all",
"prettier"
],
"parser": "@typescript-eslint/parser",
@ -109,7 +110,9 @@
}
],
"unused-imports/no-unused-imports": "error",
"lit/attribute-value-entities": "off"
"lit/attribute-value-entities": "off",
"lit/no-template-map": "off",
"lit/no-template-arrow": "warn"
},
"plugins": ["disable", "unused-imports"],
"processor": "disable/disable"

View File

@ -12,7 +12,7 @@ on:
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
NODE_OPTIONS: --max_old_space_size=6144
jobs:
lint:
@ -53,8 +53,8 @@ jobs:
run: yarn install
env:
CI: true
- name: Run Mocha
run: yarn run mocha
- name: Run Tests
run: yarn run test
build:
runs-on: ubuntu-latest
needs: [lint, test]

View File

@ -7,7 +7,7 @@ on:
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
NODE_OPTIONS: --max_old_space_size=6144
jobs:
deploy:

View File

@ -8,7 +8,7 @@ on:
env:
PYTHON_VERSION: 3.8
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
NODE_OPTIONS: --max_old_space_size=6144
jobs:
release:
@ -73,7 +73,6 @@ jobs:
matrix:
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
tag:
- "3.9-alpine3.13"
- "3.9-alpine3.14"
steps:
- name: Download requirements.txt

View File

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

File diff suppressed because one or more lines are too long

631
.yarn/releases/yarn-3.0.2.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -6,4 +6,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-2.4.2.cjs
yarnPath: .yarn/releases/yarn-3.0.2.cjs

View File

@ -82,6 +82,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
// Only support the syntax, Webpack will handle it.
"@babel/plugin-syntax-import-meta",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-top-level-await",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@ const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const paths = require("./paths.js");
const bundle = require("./bundle.js");
const log = require("fancy-log");
const WebpackBar = require("webpackbar");
class LogStartCompilePlugin {
ignoredFirst = false;
@ -74,6 +75,7 @@ const createWebpackConfig = ({
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
},
plugins: [
new WebpackBar({ fancy: !isProdBuild }),
new WebpackManifestPlugin({
// Only include the JS of entrypoints
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
@ -125,6 +127,13 @@ const createWebpackConfig = ({
alias: {
"lit/decorators$": "lit/decorators.js",
"lit/directive$": "lit/directive.js",
"lit/directives/until$": "lit/directives/until.js",
"lit/directives/class-map$": "lit/directives/class-map.js",
"lit/directives/style-map$": "lit/directives/style-map.js",
"lit/directives/if-defined$": "lit/directives/if-defined.js",
"lit/directives/guard$": "lit/directives/guard.js",
"lit/directives/cache$": "lit/directives/cache.js",
"lit/directives/repeat$": "lit/directives/repeat.js",
"lit/polyfill-support$": "lit/polyfill-support.js",
},
},
@ -142,6 +151,9 @@ const createWebpackConfig = ({
// To silence warning in worker plugin
globalObject: "self",
},
experiments: {
topLevelAwait: true,
},
};
};

View File

@ -191,7 +191,7 @@ class HcCast extends LitElement {
}
this.connection.close();
location.reload();
} catch (err) {
} catch (err: any) {
alert("Unable to log out!");
}
}

View File

@ -212,7 +212,7 @@ export class HcConnect extends LitElement {
let url: URL;
try {
url = new URL(value);
} catch (err) {
} catch (err: any) {
this.error = "Invalid URL";
return;
}
@ -240,7 +240,7 @@ export class HcConnect extends LitElement {
try {
this.loading = true;
auth = await getAuth(options);
} catch (err) {
} catch (err: any) {
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
this.cannotConnect = true;
return;
@ -259,7 +259,7 @@ export class HcConnect extends LitElement {
try {
conn = await createConnection({ auth });
} catch (err) {
} catch (err: any) {
// In case of saved tokens, silently solve problems.
if (init === "saved-tokens") {
if (err === ERR_CANNOT_CONNECT) {
@ -285,7 +285,7 @@ export class HcConnect extends LitElement {
try {
saveTokens(null);
location.reload();
} catch (err) {
} catch (err: any) {
alert("Unable to log out!");
}
}

View File

@ -148,14 +148,14 @@ export class HcMain extends HassElement {
expires_in: 0,
}),
});
} catch (err) {
} catch (err: any) {
this._error = this._getErrorMessage(err);
return;
}
let connection;
try {
connection = await createConnection({ auth });
} catch (err) {
} catch (err: any) {
this._error = this._getErrorMessage(err);
return;
}
@ -193,7 +193,7 @@ export class HcMain extends HassElement {
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
this._handleNewLovelaceConfig(lovelaceConfig)
);
} catch (err) {
} catch (err: any) {
// eslint-disable-next-line
console.log("Error fetching Lovelace configuration", err, msg);
// Generate a Lovelace config.

View File

@ -44,7 +44,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
(conf) => html`
${conf.name}
<small>
<a target="_blank" href="${conf.authorUrl}">
<a target="_blank" href=${conf.authorUrl}>
${this.hass.localize(
"ui.panel.page-demo.cards.demo.demo_by",
"name",
@ -94,7 +94,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
this._switching = true;
try {
await setDemoConfig(this.hass, this.lovelace!, index);
} catch (err) {
} catch (err: any) {
alert("Failed to switch config :-(");
} finally {
this._switching = false;

View File

@ -23,9 +23,9 @@ customElements.whenDefined("hui-view").then(() => {
// eslint-disable-next-line
const HUIView = customElements.get("hui-view");
// Patch HUI-VIEW to make the lovelace object available to the demo card
const oldCreateCard = HUIView.prototype.createCardElement;
const oldCreateCard = HUIView!.prototype.createCardElement;
HUIView.prototype.createCardElement = function (config) {
HUIView!.prototype.createCardElement = function (config) {
const el = oldCreateCard.call(this, config);
if (el.tagName === "HA-DEMO-CARD") {
(el as HADemoCard).lovelace = this.lovelace;

View File

@ -1,5 +1,5 @@
import "@material/mwc-button";
import { html, LitElement, TemplateResult } from "lit";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace";
@ -9,7 +9,6 @@ import { actionHandler } from "../../../src/panels/lovelace/common/directives/ac
export class DemoUtilLongPress extends LitElement {
protected render(): TemplateResult {
return html`
${this.renderStyle()}
${[1, 2, 3].map(
() => html`
<ha-card>
@ -41,26 +40,22 @@ export class DemoUtilLongPress extends LitElement {
area.scrollTop = area.scrollHeight;
}
private renderStyle() {
return html`
<style>
ha-card {
width: 200px;
margin: calc(42vh - 140px) auto;
padding: 8px;
text-align: center;
}
ha-card:first-of-type {
margin-top: 16px;
}
ha-card:last-of-type {
margin-bottom: 16px;
}
static styles = css`
ha-card {
width: 200px;
margin: calc(42vh - 140px) auto;
padding: 8px;
text-align: center;
}
ha-card:first-of-type {
margin-top: 16px;
}
ha-card:last-of-type {
margin-bottom: 16px;
}
textarea {
height: 50px;
}
</style>
`;
}
textarea {
height: 50px;
}
`;
}

View File

@ -259,7 +259,7 @@ class HassioAddonConfig extends LitElement {
path: "options",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.common.update_available",
"error",
@ -300,7 +300,7 @@ class HassioAddonConfig extends LitElement {
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",

View File

@ -89,9 +89,9 @@ class HassioAddonNetwork extends LitElement {
<td>
<paper-input
@value-changed=${this._configChanged}
placeholder="${this.supervisor.localize(
placeholder=${this.supervisor.localize(
"addon.configuration.network.disabled"
)}"
)}
.value=${item.host ? String(item.host) : ""}
.container=${item.container}
no-label-float
@ -171,7 +171,7 @@ class HassioAddonNetwork extends LitElement {
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_reset",
"error",
@ -207,7 +207,7 @@ class HassioAddonNetwork extends LitElement {
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",

View File

@ -79,7 +79,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
this.hass,
this.addon!.slug
);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.documentation.get_logs",
"error",

View File

@ -222,7 +222,7 @@ class HassioAddonDashboard extends LitElement {
try {
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
this.addon = addoninfo;
} catch (err) {
} catch (err: any) {
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
this.addon = undefined;
}

View File

@ -123,18 +123,18 @@ class HassioAddonInfo extends LitElement {
<div class="card-content">
<hassio-card-content
.hass=${this.hass}
.title="${this.supervisor.localize(
.title=${this.supervisor.localize(
"addon.dashboard.new_update_available",
"name",
this.addon.name,
"version",
this.addon.version_latest
)}"
.description="${this.supervisor.localize(
)}
.description=${this.supervisor.localize(
"common.running_version",
"version",
this.addon.version
)}"
)}
icon=${mdiArrowUpBoldCircle}
iconClass="update"
></hassio-card-content>
@ -254,7 +254,7 @@ class HassioAddonInfo extends LitElement {
${this.supervisor.localize(
"addon.dashboard.visit_addon_page",
"name",
html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer"
html`<a href=${this.addon.url!} target="_blank" rel="noreferrer"
>${this.addon.name}</a
>`
)}
@ -437,10 +437,10 @@ class HassioAddonInfo extends LitElement {
${this.addon.version
? html`
<div
class="${classMap({
class=${classMap({
"addon-options": true,
started: this.addon.state === "started",
})}"
})}
>
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
@ -796,7 +796,7 @@ class HassioAddonInfo extends LitElement {
path: "option",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
@ -818,7 +818,7 @@ class HassioAddonInfo extends LitElement {
path: "option",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
@ -840,7 +840,7 @@ class HassioAddonInfo extends LitElement {
path: "option",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
@ -862,7 +862,7 @@ class HassioAddonInfo extends LitElement {
path: "security",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
@ -884,7 +884,7 @@ class HassioAddonInfo extends LitElement {
path: "option",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
@ -912,7 +912,7 @@ class HassioAddonInfo extends LitElement {
title: this.supervisor.localize("addon.dashboard.changelog"),
content,
});
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"addon.dashboard.action_error.get_changelog"
@ -934,7 +934,7 @@ class HassioAddonInfo extends LitElement {
path: "install",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("addon.dashboard.action_error.install"),
text: extractApiErrorMessage(err),
@ -955,7 +955,7 @@ class HassioAddonInfo extends LitElement {
path: "stop",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("addon.dashboard.action_error.stop"),
text: extractApiErrorMessage(err),
@ -976,7 +976,7 @@ class HassioAddonInfo extends LitElement {
path: "stop",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("addon.dashboard.action_error.restart"),
text: extractApiErrorMessage(err),
@ -1035,7 +1035,7 @@ class HassioAddonInfo extends LitElement {
button.progress = false;
return;
}
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: "Failed to validate addon configuration",
text: extractApiErrorMessage(err),
@ -1053,7 +1053,7 @@ class HassioAddonInfo extends LitElement {
path: "start",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("addon.dashboard.action_error.start"),
text: extractApiErrorMessage(err),
@ -1091,7 +1091,7 @@ class HassioAddonInfo extends LitElement {
path: "uninstall",
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"addon.dashboard.action_error.uninstall"

View File

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

View File

@ -14,7 +14,7 @@ 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";
import { relativeTime } from "../../../src/common/datetime/relative_time";
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import {
DataTableColumnContainer,
@ -133,7 +133,7 @@ export class HassioBackups extends LitElement {
filterable: true,
sortable: true,
template: (entry: string) =>
relativeTime(new Date(entry), this.hass.localize),
relativeTime(new Date(entry), this.hass.locale),
},
secondary: {
title: "",
@ -294,7 +294,7 @@ export class HassioBackups extends LitElement {
await Promise.all(
this._selectedBackups.map((slug) => removeBackup(this.hass, slug))
);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("backup.failed_to_delete"),
text: extractApiErrorMessage(err),

View File

@ -37,7 +37,7 @@ class HassioCardContent extends LitElement {
${this.iconImage
? html`
<div class="icon_image ${this.iconClass}">
<img src="${this.iconImage}" .title=${this.iconTitle} />
<img src=${this.iconImage} .title=${this.iconTitle} />
<div></div>
</div>
`

View File

@ -70,7 +70,7 @@ export class HassioUploadBackup extends LitElement {
try {
const backup = await uploadBackup(this.hass, file);
fireEvent(this, "backup-uploaded", { backup: backup.data });
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: "Upload failed",
text: extractApiErrorMessage(err),

View File

@ -20,10 +20,10 @@ class SupervisorMetric extends LitElement {
<div slot="description" .title=${this.tooltip ?? ""}>
<span class="value"> ${roundedValue} % </span>
<ha-bar
class="${classMap({
class=${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
})}
.value=${this.value}
></ha-bar>
</div>

View File

@ -3,7 +3,7 @@ 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";
import { stringCompare } from "../../../src/common/string/compare";
import "../../../src/components/ha-card";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../src/resources/styles";
@ -33,7 +33,7 @@ class HassioAddons extends LitElement {
</ha-card>
`
: this.supervisor.supervisor.addons
.sort((a, b) => compare(a.name, b.name))
.sort((a, b) => stringCompare(a.name, b.name))
.map(
(addon) => html`
<ha-card .addon=${addon} @click=${this._addonTapped}>

View File

@ -136,7 +136,7 @@ export class HassioUpdate extends LitElement {
</ha-settings-row>
</div>
<div class="card-actions">
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
<a href=${releaseNotesUrl} target="_blank" rel="noreferrer">
<mwc-button>
${this.supervisor.localize("common.release_notes")}
</mwc-button>
@ -206,7 +206,7 @@ export class HassioUpdate extends LitElement {
fireEvent(this, "supervisor-collection-refresh", {
collection: item.key,
});
} catch (err) {
} catch (err: any) {
// Only show an error if the status code was not expected (user behind proxy)
// or no status at all(connection terminated)
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {

View File

@ -311,7 +311,7 @@ class HassioBackupDialog
: "snapshots"
}/${this._backup!.slug}/download`
);
} catch (err) {
} catch (err: any) {
await showAlertDialog(this, {
text: extractApiErrorMessage(err),
});

View File

@ -127,7 +127,7 @@ class HassioCreateBackupDialog extends LitElement {
this._dialogParams!.onCreate();
this.closeDialog();
} catch (err) {
} catch (err: any) {
this._error = extractApiErrorMessage(err);
}
this._creatingBackup = false;

View File

@ -0,0 +1,191 @@
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
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 "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-markdown";
import {
extractApiErrorMessage,
ignoreSupervisorError,
} from "../../../../src/data/hassio/common";
import {
DatadiskList,
listDatadisks,
moveDatadisk,
} from "../../../../src/data/hassio/host";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
const rebootTime = (supervisor.host.startup_time * 4) / 60;
return Math.ceil((moveTime + rebootTime) / 10) * 10;
});
@customElement("dialog-hassio-datadisk")
class HassioDatadiskDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private dialogParams?: HassioDatatiskDialogParams;
@state() private selectedDevice?: string;
@state() private devices?: DatadiskList["devices"];
@state() private moving = false;
public showDialog(params: HassioDatatiskDialogParams) {
this.dialogParams = params;
listDatadisks(this.hass).then((data) => {
this.devices = data.devices;
});
}
public closeDialog(): void {
this.dialogParams = undefined;
this.selectedDevice = undefined;
this.devices = undefined;
this.moving = false;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this.dialogParams) {
return html``;
}
return html`
<ha-dialog
open
scrimClickAction
escapeKeyAction
@closed=${this.closeDialog}
?hideActions=${this.moving}
>
${this.moving
? html`<slot name="heading">
<h2 id="title" class="header_title">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving"
)}
</h2>
</slot>
<ha-circular-progress alt="Moving" size="large" active>
</ha-circular-progress>
<p class="progress-text">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving_desc"
)}
</p>`
: html`<slot name="heading">
<h2 id="title" class="header_title">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.title"
)}
</h2>
</slot>
${this.devices?.length
? html`
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.description",
{
current_path: this.dialogParams.supervisor.os.data_disk,
time: calculateMoveTime(this.dialogParams.supervisor),
}
)}
<br /><br />
<paper-dropdown-menu
.label=${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.select_device"
)}
@value-changed=${this._select_device}
>
<paper-listbox slot="dropdown-content">
${this.devices.map(
(device) => html`<paper-item>${device}</paper-item>`
)}
</paper-listbox>
</paper-dropdown-menu>
`
: this.devices === undefined
? this.dialogParams.supervisor.localize(
"dialog.datadisk_move.loading_devices"
)
: this.dialogParams.supervisor.localize(
"dialog.datadisk_move.no_devices"
)}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.cancel"
)}
</mwc-button>
<mwc-button
.disabled=${!this.selectedDevice}
slot="primaryAction"
@click=${this._moveDatadisk}
>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.move"
)}
</mwc-button>`}
</ha-dialog>
`;
}
private _select_device(event) {
this.selectedDevice = event.detail.value;
}
private async _moveDatadisk() {
this.moving = true;
try {
await moveDatadisk(this.hass, this.selectedDevice!);
} catch (err: any) {
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
showAlertDialog(this, {
title: this.dialogParams!.supervisor.localize(
"system.host.failed_to_move"
),
text: extractApiErrorMessage(err),
});
this.closeDialog();
}
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
paper-dropdown-menu {
width: 100%;
}
ha-circular-progress {
display: block;
margin: 32px;
text-align: center;
}
.progress-text {
text-align: center;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-hassio-datadisk": HassioDatadiskDialog;
}
}

View File

@ -0,0 +1,17 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
export interface HassioDatatiskDialogParams {
supervisor: Supervisor;
}
export const showHassioDatadiskDialog = (
element: HTMLElement,
dialogParams: HassioDatatiskDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-datadisk",
dialogImport: () => import("./dialog-hassio-datadisk"),
dialogParams,
});
};

View File

@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/common/search/search-input";
import { compare } from "../../../../src/common/string/compare";
import { stringCompare } from "../../../../src/common/string/compare";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-expansion-panel";
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
@ -27,7 +27,7 @@ const _filterDevices = memoizeOne(
.toLocaleLowerCase()
.includes(filter))
)
.sort((a, b) => compare(a.name, b.name))
.sort((a, b) => stringCompare(a.name, b.name))
);
@customElement("dialog-hassio-hardware")

View File

@ -287,7 +287,7 @@ export class DialogHassioNetwork
this.hass,
this._interface.interface
);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: "Failed to scan for accesspoints",
text: extractApiErrorMessage(err),
@ -448,7 +448,7 @@ export class DialogHassioNetwork
this._interface!.interface,
interfaceOptions
);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("dialog.network.failed_to_change"),
text: extractApiErrorMessage(err),

View File

@ -190,7 +190,7 @@ class HassioRegistriesDialog extends LitElement {
await addHassioDockerRegistry(this.hass, data);
await this._loadRegistries();
this._addingRegistry = false;
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("dialog.registries.failed_to_add"),
text: extractApiErrorMessage(err),
@ -204,7 +204,7 @@ class HassioRegistriesDialog extends LitElement {
try {
await removeHassioDockerRegistry(this.hass, entry.registry);
await this._loadRegistries();
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("dialog.registries.failed_to_remove"),
text: extractApiErrorMessage(err),

View File

@ -185,7 +185,7 @@ class HassioRepositoriesDialog extends LitElement {
this._repositories = addonsinfo.repositories;
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
} catch (err) {
} catch (err: any) {
this._error = extractApiErrorMessage(err);
}
}
@ -207,7 +207,7 @@ class HassioRepositoriesDialog extends LitElement {
await this._loadData();
input.value = "";
} catch (err) {
} catch (err: any) {
this._error = extractApiErrorMessage(err);
}
this._processing = false;
@ -229,7 +229,7 @@ class HassioRepositoriesDialog extends LitElement {
addons_repositories: newRepositories,
});
await this._loadData();
} catch (err) {
} catch (err: any) {
this._error = extractApiErrorMessage(err);
}
}

View File

@ -26,7 +26,7 @@ export const suggestAddonRestart = async (
if (confirmed) {
try {
await restartHassioAddon(hass, addon.slug);
} catch (err) {
} catch (err: any) {
showAlertDialog(element, {
title: supervisor.localize(
"common.failed_to_restart_name",

View File

@ -148,7 +148,7 @@ class DialogSupervisorUpdate extends LitElement {
this.hass,
this._dialogParams!.backupParams
);
} catch (err) {
} catch (err: any) {
this._error = extractApiErrorMessage(err);
this._action = null;
return;
@ -158,7 +158,7 @@ class DialogSupervisorUpdate extends LitElement {
this._action = "update";
try {
await this._dialogParams!.updateHandler!();
} catch (err) {
} catch (err: any) {
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
this._error = extractApiErrorMessage(err);
this._action = null;

View File

@ -87,7 +87,7 @@ class HassioMyRedirect extends LitElement {
let url: string;
try {
url = this._createRedirectUrl(redirect);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize("my.error");
return;
}

View File

@ -91,7 +91,7 @@ class HassioIngressView extends LitElement {
if (requestedAddon) {
try {
addonInfo = await fetchHassioAddonInfo(this.hass, requestedAddon);
} catch (err) {
} catch (err: any) {
await showAlertDialog(this, {
text: extractApiErrorMessage(err),
title: requestedAddon,
@ -145,7 +145,7 @@ class HassioIngressView extends LitElement {
try {
addon = await fetchHassioAddonInfo(this.hass, addonSlug);
} catch (err) {
} catch (err: any) {
await showAlertDialog(this, {
text: "Unable to fetch add-on info to start Ingress",
title: "Supervisor",
@ -179,7 +179,7 @@ class HassioIngressView extends LitElement {
try {
session = await createSessionPromise;
} catch (err) {
} catch (err: any) {
await showAlertDialog(this, {
text: "Unable to create an Ingress session",
title: addon.name,
@ -195,7 +195,7 @@ class HassioIngressView extends LitElement {
this._sessionKeepAlive = window.setInterval(async () => {
try {
await validateHassioSession(this.hass, session);
} catch (err) {
} catch (err: any) {
session = await createHassioSession(this.hass);
}
}, 60000);

View File

@ -144,7 +144,7 @@ class HassioCoreInfo extends LitElement {
try {
await restartCore(this.hass);
} catch (err) {
} catch (err: any) {
if (this.hass.connection.connected) {
showAlertDialog(this, {
title: this.supervisor.localize(

View File

@ -1,5 +1,4 @@
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 { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
@ -40,8 +39,9 @@ import {
roundWithOneDecimal,
} from "../../../src/util/calculate";
import "../components/supervisor-metric";
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
import { showHassioDatadiskDialog } from "../dialogs/datadisk/show-dialog-hassio-datadisk";
import { showHassioHardwareDialog } from "../dialogs/hardware/show-dialog-hassio-hardware";
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
import { hassioStyle } from "../resources/hassio-style";
@customElement("hassio-host-info")
@ -180,20 +180,27 @@ class HassioHostInfo extends LitElement {
`
: ""}
<ha-button-menu
corner="BOTTOM_START"
@action=${this._handleMenuAction}
>
<ha-button-menu corner="BOTTOM_START">
<mwc-icon-button slot="trigger">
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item>
<mwc-list-item @click=${() => this._handleMenuAction("hardware")}>
${this.supervisor.localize("system.host.hardware")}
</mwc-list-item>
${this.supervisor.host.features.includes("haos")
? html`<mwc-list-item>
${this.supervisor.localize("system.host.import_from_usb")}
</mwc-list-item>`
? html`<mwc-list-item
@click=${() => this._handleMenuAction("import_from_usb")}
>
${this.supervisor.localize("system.host.import_from_usb")}
</mwc-list-item>
${this.supervisor.host.features.includes("os_agent") &&
atLeastVersion(this.supervisor.host.agent_version, 1, 2, 0)
? html`<mwc-list-item
@click=${() => this._handleMenuAction("move_datadisk")}
>
${this.supervisor.localize("system.host.move_datadisk")}
</mwc-list-item>`
: ""} `
: ""}
</ha-button-menu>
</div>
@ -216,22 +223,31 @@ class HassioHostInfo extends LitElement {
return network_info.interfaces.find((a) => a.primary)?.ipv4?.address![0];
});
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
private async _handleMenuAction(action: string) {
switch (action) {
case "hardware":
await this._showHardware();
break;
case 1:
case "import_from_usb":
await this._importFromUSB();
break;
case "move_datadisk":
await this._moveDatadisk();
break;
}
}
private _moveDatadisk(): void {
showHassioDatadiskDialog(this, {
supervisor: this.supervisor,
});
}
private async _showHardware(): Promise<void> {
let hardware;
try {
hardware = await fetchHassioHardwareInfo(this.hass);
} catch (err) {
} catch (err: any) {
await showAlertDialog(this, {
title: this.supervisor.localize(
"system.host.failed_to_get_hardware_list"
@ -261,7 +277,7 @@ class HassioHostInfo extends LitElement {
try {
await rebootHost(this.hass);
} catch (err) {
} catch (err: any) {
// Ignore connection errors, these are all expected
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
showAlertDialog(this, {
@ -291,7 +307,7 @@ class HassioHostInfo extends LitElement {
try {
await shutdownHost(this.hass);
} catch (err) {
} catch (err: any) {
// Ignore connection errors, these are all expected
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
showAlertDialog(this, {
@ -332,7 +348,7 @@ class HassioHostInfo extends LitElement {
try {
await updateOS(this.hass);
fireEvent(this, "supervisor-collection-refresh", { collection: "os" });
} catch (err) {
} catch (err: any) {
if (this.hass.connection.connected) {
showAlertDialog(this, {
title: this.supervisor.localize(
@ -370,7 +386,7 @@ class HassioHostInfo extends LitElement {
fireEvent(this, "supervisor-collection-refresh", {
collection: "host",
});
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("system.host.failed_to_set_hostname"),
text: extractApiErrorMessage(err),
@ -385,7 +401,7 @@ class HassioHostInfo extends LitElement {
fireEvent(this, "supervisor-collection-refresh", {
collection: "host",
});
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"system.host.failed_to_import_from_usb"

View File

@ -34,16 +34,18 @@ import { hassioStyle } from "../resources/hassio-style";
const UNSUPPORTED_REASON_URL = {
apparmor: "/more-info/unsupported/apparmor",
container: "/more-info/unsupported/container",
content_trust: "/more-info/unsupported/content_trust",
dbus: "/more-info/unsupported/dbus",
docker_configuration: "/more-info/unsupported/docker_configuration",
docker_version: "/more-info/unsupported/docker_version",
job_conditions: "/more-info/unsupported/job_conditions",
lxc: "/more-info/unsupported/lxc",
network_manager: "/more-info/unsupported/network_manager",
os_agent: "/more-info/unsupported/os_agent",
os: "/more-info/unsupported/os",
privileged: "/more-info/unsupported/privileged",
source_mods: "/more-info/unsupported/source_mods",
systemd: "/more-info/unsupported/systemd",
content_trust: "/more-info/unsupported/content_trust",
};
const UNHEALTHY_REASON_URL = {
@ -280,7 +282,7 @@ class HassioSupervisorInfo extends LitElement {
};
await setSupervisorOption(this.hass, data);
await this._reloadSupervisor();
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"system.supervisor.failed_to_set_option"
@ -298,7 +300,7 @@ class HassioSupervisorInfo extends LitElement {
try {
await this._reloadSupervisor();
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize("system.supervisor.failed_to_reload"),
text: extractApiErrorMessage(err),
@ -341,7 +343,7 @@ class HassioSupervisorInfo extends LitElement {
try {
await restartSupervisor(this.hass);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"common.failed_to_restart_name",
@ -386,7 +388,7 @@ class HassioSupervisorInfo extends LitElement {
fireEvent(this, "supervisor-collection-refresh", {
collection: "supervisor",
});
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"common.failed_to_update_name",
@ -425,10 +427,10 @@ class HassioSupervisorInfo extends LitElement {
<li>
${UNSUPPORTED_REASON_URL[reason]
? html`<a
href="${documentationUrl(
href=${documentationUrl(
this.hass,
UNSUPPORTED_REASON_URL[reason]
)}"
)}
target="_blank"
rel="noreferrer"
>
@ -456,10 +458,10 @@ class HassioSupervisorInfo extends LitElement {
<li>
${UNHEALTHY_REASON_URL[reason]
? html`<a
href="${documentationUrl(
href=${documentationUrl(
this.hass,
UNHEALTHY_REASON_URL[reason]
)}"
)}
target="_blank"
rel="noreferrer"
>
@ -481,7 +483,7 @@ class HassioSupervisorInfo extends LitElement {
diagnostics: !this.supervisor.supervisor?.diagnostics,
};
await setSupervisorOption(this.hass, data);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
"system.supervisor.failed_to_set_option"

View File

@ -130,7 +130,7 @@ class HassioSupervisorLog extends LitElement {
this.hass,
this._selectedLogProvider
);
} catch (err) {
} catch (err: any) {
this._error = this.supervisor.localize(
"system.log.get_logs",
"provider",

View File

@ -1,3 +1,3 @@
[build.environment]
YARN_VERSION = "1.22.11"
NODE_OPTIONS = "--max_old_space_size=4096"
NODE_OPTIONS = "--max_old_space_size=6144"

View File

@ -16,53 +16,56 @@
"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": "ts-mocha -p test-mocha/tsconfig.test.json \"test-mocha/**/*.ts\"",
"test": "yarn run mocha"
"test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.js \"test/**/*.ts\""
},
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
"license": "Apache-2.0",
"dependencies": {
"@braintree/sanitize-url": "^5.0.1",
"@codemirror/commands": "^0.18.0",
"@codemirror/gutter": "^0.18.0",
"@codemirror/highlight": "^0.18.0",
"@codemirror/history": "^0.18.0",
"@codemirror/legacy-modes": "^0.18.0",
"@codemirror/rectangular-selection": "^0.18.0",
"@codemirror/search": "^0.18.0",
"@codemirror/state": "^0.18.0",
"@codemirror/stream-parser": "^0.18.0",
"@codemirror/text": "^0.18.0",
"@codemirror/view": "^0.18.0",
"@formatjs/intl-getcanonicallocales": "^1.5.10",
"@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",
"@braintree/sanitize-url": "^5.0.2",
"@codemirror/commands": "^0.19.2",
"@codemirror/gutter": "^0.19.1",
"@codemirror/highlight": "^0.19.2",
"@codemirror/history": "^0.19.0",
"@codemirror/legacy-modes": "^0.19.0",
"@codemirror/rectangular-selection": "^0.19.0",
"@codemirror/search": "^0.19.0",
"@codemirror/state": "^0.19.1",
"@codemirror/stream-parser": "^0.19.1",
"@codemirror/text": "^0.19.2",
"@codemirror/view": "^0.19.4",
"@formatjs/intl-datetimeformat": "^4.2.4",
"@formatjs/intl-getcanonicallocales": "^1.7.3",
"@formatjs/intl-locale": "^2.4.38",
"@formatjs/intl-numberformat": "^7.2.4",
"@formatjs/intl-pluralrules": "^4.1.4",
"@formatjs/intl-relativetimeformat": "^9.3.1",
"@formatjs/intl-utils": "^3.8.4",
"@fullcalendar/common": "5.9.0",
"@fullcalendar/core": "5.9.0",
"@fullcalendar/daygrid": "5.9.0",
"@fullcalendar/interaction": "5.9.0",
"@fullcalendar/list": "5.9.0",
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.6.0#./.yarn/patches/@lit-labs/virtualizer/0.7.0.patch",
"@material/chips": "12.0.0-canary.22d29cbb4.0",
"@material/data-table": "12.0.0-canary.22d29cbb4.0",
"@material/mwc-button": "0.22.1",
"@material/mwc-checkbox": "0.22.1",
"@material/mwc-circular-progress": "0.22.1",
"@material/mwc-dialog": "0.22.1",
"@material/mwc-fab": "0.22.1",
"@material/mwc-formfield": "0.22.1",
"@material/mwc-icon-button": "0.22.1",
"@material/mwc-linear-progress": "0.22.1",
"@material/mwc-list": "0.22.1",
"@material/mwc-menu": "0.22.1",
"@material/mwc-radio": "0.22.1",
"@material/mwc-ripple": "0.22.1",
"@material/mwc-switch": "0.22.1",
"@material/mwc-tab": "0.22.1",
"@material/mwc-tab-bar": "0.22.1",
"@material/top-app-bar": "12.0.0-canary.22d29cbb4.0",
"@mdi/js": "5.9.55",
"@mdi/svg": "5.9.55",
"@material/chips": "13.0.0-canary.65125b3a6.0",
"@material/data-table": "13.0.0-canary.65125b3a6.0",
"@material/mwc-button": "0.25.1",
"@material/mwc-checkbox": "0.25.1",
"@material/mwc-circular-progress": "0.25.1",
"@material/mwc-dialog": "0.25.1",
"@material/mwc-fab": "0.25.1",
"@material/mwc-formfield": "0.25.1",
"@material/mwc-icon-button": "0.25.1",
"@material/mwc-linear-progress": "0.25.1",
"@material/mwc-list": "0.25.1",
"@material/mwc-menu": "0.25.1",
"@material/mwc-radio": "0.25.1",
"@material/mwc-ripple": "0.25.1",
"@material/mwc-switch": "0.25.1",
"@material/mwc-tab": "0.25.1",
"@material/mwc-tab-bar": "0.25.1",
"@material/top-app-bar": "13.0.0-canary.65125b3a6.0",
"@mdi/js": "6.1.95",
"@mdi/svg": "6.1.95",
"@polymer/app-layout": "^3.1.0",
"@polymer/iron-flex-layout": "^3.0.1",
"@polymer/iron-icon": "^3.0.1",
@ -88,9 +91,9 @@
"@polymer/paper-toast": "^3.0.1",
"@polymer/paper-tooltip": "^3.0.1",
"@polymer/polymer": "3.4.1",
"@thomasloven/round-slider": "0.5.2",
"@vaadin/vaadin-combo-box": "^20.0.1",
"@vaadin/vaadin-date-picker": "^20.0.1",
"@thomasloven/round-slider": "0.5.4",
"@vaadin/vaadin-combo-box": "^21.0.2",
"@vaadin/vaadin-date-picker": "^21.0.2",
"@vibrant/color": "^3.2.1-alpha.1",
"@vibrant/core": "^3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",
@ -99,36 +102,34 @@
"chart.js": "^3.3.2",
"comlink": "^4.3.1",
"core-js": "^3.15.2",
"cropperjs": "^1.5.11",
"date-fns": "^2.22.1",
"cropperjs": "^1.5.12",
"date-fns": "^2.23.0",
"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.7",
"hls.js": "^1.0.10",
"home-assistant-js-websocket": "^5.11.1",
"idb-keyval": "^5.0.5",
"intl-messageformat": "^9.6.16",
"idb-keyval": "^5.1.3",
"intl-messageformat": "^9.9.1",
"js-yaml": "^4.1.0",
"leaflet": "^1.7.1",
"leaflet-draw": "^1.0.4",
"lit": "^2.0.0-rc.3",
"lit-vaadin-helpers": "^0.1.3",
"marked": "^2.0.5",
"mdn-polyfills": "^5.16.0",
"lit": "^2.0.0",
"lit-vaadin-helpers": "^0.2.1",
"marked": "^3.0.2",
"memoize-one": "^5.2.1",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "^0.3.1",
"proxy-polyfill": "^0.3.2",
"punycode": "^2.1.1",
"qrcode": "^1.4.4",
"regenerator-runtime": "^0.13.8",
"resize-observer-polyfill": "^1.5.1",
"roboto-fontface": "^0.10.0",
"sortablejs": "^1.10.2",
"sortablejs": "^1.14.0",
"superstruct": "^0.15.2",
"tinykeys": "^1.1.3",
"tsparticles": "^1.19.2",
"tsparticles": "^1.34.0",
"unfetch": "^4.1.0",
"vis-data": "^7.1.2",
"vis-network": "^8.5.4",
@ -144,17 +145,18 @@
"xss": "^1.0.9"
},
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/core": "^7.15.5",
"@babel/plugin-external-helpers": "^7.14.5",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/plugin-proposal-decorators": "^7.14.5",
"@babel/plugin-proposal-decorators": "^7.15.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
"@babel/plugin-proposal-object-rest-spread": "^7.15.6",
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/preset-env": "^7.14.7",
"@babel/preset-typescript": "^7.14.5",
"@babel/plugin-syntax-top-level-await": "^7.14.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-typescript": "^7.15.0",
"@koa/cors": "^3.1.0",
"@open-wc/dev-server-hmr": "^0.0.2",
"@rollup/plugin-babel": "^5.2.1",
@ -164,30 +166,31 @@
"@rollup/plugin-replace": "^2.3.2",
"@types/chromecast-caf-receiver": "5.0.12",
"@types/chromecast-caf-sender": "^1.0.3",
"@types/js-yaml": "^4.0.1",
"@types/leaflet": "^1.7.0",
"@types/leaflet-draw": "^1.0.3",
"@types/marked": "^2.0.3",
"@types/mocha": "^8.2.2",
"@types/sortablejs": "^1.10.6",
"@types/js-yaml": "^4",
"@types/leaflet": "^1",
"@types/leaflet-draw": "^1",
"@types/marked": "^2",
"@types/mocha": "^8",
"@types/sortablejs": "^1",
"@types/webspeechapi": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
"@web/dev-server": "^0.0.24",
"@web/dev-server-rollup": "^0.2.11",
"babel-loader": "^8.2.2",
"chai": "^4.3.4",
"del": "^4.0.0",
"eslint": "^7.30.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-config-airbnb-typescript": "^14.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-webpack": "^0.13.1",
"eslint-plugin-disable": "^2.0.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-lit": "^1.5.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-unused-imports": "^1.1.2",
"eslint-plugin-wc": "^1.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-unused-imports": "^1.1.5",
"eslint-plugin-wc": "^1.3.2",
"fancy-log": "^1.3.3",
"fs-extra": "^7.0.1",
"gulp": "^4.0.2",
@ -198,7 +201,8 @@
"gulp-zopfli-green": "^3.0.1",
"html-minifier": "^4.0.0",
"husky": "^1.3.1",
"lint-staged": "^11.0.1",
"instant-mocha": "^1.3.1",
"lint-staged": "^11.1.2",
"lit-analyzer": "^1.2.1",
"lodash.template": "^4.5.0",
"magic-string": "^0.25.7",
@ -207,7 +211,7 @@
"mocha": "^8.4.0",
"object-hash": "^2.0.3",
"open": "^7.0.4",
"prettier": "^2.3.2",
"prettier": "^2.4.1",
"require-dir": "^1.2.0",
"rollup": "^2.8.2",
"rollup-plugin-string": "^3.0.0",
@ -217,26 +221,26 @@
"sinon": "^11.0.0",
"source-map-url": "^0.4.0",
"systemjs": "^6.3.2",
"terser-webpack-plugin": "^5.1.4",
"terser-webpack-plugin": "^5.2.4",
"ts-lit-plugin": "^1.2.1",
"ts-mocha": "^8.0.0",
"typescript": "^4.3.5",
"typescript": "^4.4.3",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"webpack": "^5.43.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2",
"webpack-manifest-plugin": "^3.1.1",
"webpack": "^5.55.1",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.3.0",
"webpack-manifest-plugin": "^4.0.2",
"webpackbar": "^5.0.0-3",
"workbox-build": "^6.1.5"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": {
"@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
"@webcomponents/webcomponentsjs": "^2.2.10",
"lit": "^2.0.0-rc.3",
"lit-html": "2.0.0-rc.4",
"lit-element": "3.0.0-rc.3",
"@lit/reactive-element": "1.0.0-rc.3"
"lit": "^2.0.0",
"lit-html": "2.0.0",
"lit-element": "3.0.0",
"@lit/reactive-element": "1.0.0"
},
"main": "src/home-assistant.js",
"husky": {

View File

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

View File

@ -194,7 +194,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
this._state = "error";
this._errorMessage = data.message;
}
} catch (err) {
} catch (err: any) {
// eslint-disable-next-line no-console
console.error("Error starting auth flow", err);
this._state = "error";
@ -317,7 +317,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
return;
}
await this._updateStep(newStep);
} catch (err) {
} catch (err: any) {
// eslint-disable-next-line no-console
console.error("Error submitting step", err);
this._state = "error";

View File

@ -8,10 +8,6 @@ import {
AuthUrlSearchParams,
fetchAuthProviders,
} from "../data/auth";
import {
DiscoveryInformation,
fetchDiscoveryInformation,
} from "../data/discovery";
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { registerServiceWorker } from "../util/register-service-worker";
import "./ha-auth-flow";
@ -29,8 +25,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
@state() private _authProviders?: AuthProvider[];
@state() private _discovery?: DiscoveryInformation;
constructor() {
super();
this.translationFragment = "page-authorize";
@ -58,17 +52,14 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
// the name with a bold tag.
const loggingInWith = document.createElement("div");
loggingInWith.innerText = this.localize(
this._discovery?.location_name
? "ui.panel.page-authorize.logging_in_to_with"
: "ui.panel.page-authorize.logging_in_with",
"locationName",
"LOCATION",
"ui.panel.page-authorize.logging_in_with",
"authProviderName",
"NAME"
);
loggingInWith.innerHTML = loggingInWith.innerHTML
.replace("**LOCATION**", `<b>${this._discovery?.location_name}</b>`)
.replace("**NAME**", `<b>${this._authProvider!.name}</b>`);
loggingInWith.innerHTML = loggingInWith.innerHTML.replace(
"**NAME**",
`<b>${this._authProvider!.name}</b>`
);
const inactiveProviders = this._authProviders.filter(
(prv) => prv !== this._authProvider
@ -85,20 +76,20 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
${loggingInWith}
<ha-auth-flow
.resources="${this.resources}"
.clientId="${this.clientId}"
.redirectUri="${this.redirectUri}"
.oauth2State="${this.oauth2State}"
.authProvider="${this._authProvider}"
.resources=${this.resources}
.clientId=${this.clientId}
.redirectUri=${this.redirectUri}
.oauth2State=${this.oauth2State}
.authProvider=${this._authProvider}
></ha-auth-flow>
${inactiveProviders.length > 0
? html`
<ha-pick-auth-provider
.resources="${this.resources}"
.clientId="${this.clientId}"
.authProviders="${inactiveProviders}"
@pick-auth-provider="${this._handleAuthProviderPick}"
.resources=${this.resources}
.clientId=${this.clientId}
.authProviders=${inactiveProviders}
@pick-auth-provider=${this._handleAuthProviderPick}
></ha-pick-auth-provider>
`
: ""}
@ -108,7 +99,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this._fetchAuthProviders();
this._fetchDiscoveryInfo();
if (matchMedia("(prefers-color-scheme: dark)").matches) {
applyThemesOnElement(
@ -144,10 +134,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
}
}
private async _fetchDiscoveryInfo() {
this._discovery = await fetchDiscoveryInformation();
}
private async _fetchAuthProviders() {
// Fetch auth providers
try {
@ -172,7 +158,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
this._authProviders = authProviders;
this._authProvider = authProviders[0];
} catch (err) {
} catch (err: any) {
// eslint-disable-next-line
console.error("Error loading auth providers", err);
}

View File

@ -1,3 +1,4 @@
/* eslint-disable lit/prefer-static-styles */
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";

View File

@ -1,6 +1,6 @@
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import { html, LitElement } from "lit";
import { css, html, LitElement } from "lit";
import { property } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import "../components/ha-icon-next";
@ -18,14 +18,6 @@ class HaPickAuthProvider extends litLocalizeLiteMixin(LitElement) {
protected render() {
return html`
<style>
paper-item {
cursor: pointer;
}
p {
margin-top: 0;
}
</style>
<p>${this.localize("ui.panel.page-authorize.pick_auth_provider")}:</p>
${this.authProviders.map(
(provider) => html`
@ -41,5 +33,14 @@ class HaPickAuthProvider extends litLocalizeLiteMixin(LitElement) {
private _handlePick(ev) {
fireEvent(this, "pick-auth-provider", ev.currentTarget.auth_provider);
}
static styles = css`
paper-item {
cursor: pointer;
}
p {
margin-top: 0;
}
`;
}
customElements.define("ha-pick-auth-provider", HaPickAuthProvider);

View File

@ -33,7 +33,7 @@ export function saveTokens(tokens: AuthData | null) {
if (tokenCache.writeEnabled) {
try {
storage.hassTokens = JSON.stringify(tokens);
} catch (err) {
} catch (err: any) {
// write failed, ignore it. Happens if storage is full or private mode.
}
}
@ -58,7 +58,7 @@ export function loadTokens() {
} else {
tokenCache.tokens = null;
}
} catch (err) {
} catch (err: any) {
tokenCache.tokens = null;
}
}

View File

@ -60,6 +60,7 @@ export const FIXED_DEVICE_CLASS_ICONS = {
current: "hass:current-ac",
carbon_dioxide: "mdi:molecule-co2",
carbon_monoxide: "mdi:molecule-co",
date: "hass:calendar",
energy: "hass:lightning-bolt",
gas: "hass:gas-cylinder",
humidity: "hass:water-percent",

View File

@ -1,34 +0,0 @@
// Check for support of native locale string options
function checkToLocaleDateStringSupportsOptions() {
try {
new Date().toLocaleDateString("i");
} catch (e) {
return e.name === "RangeError";
}
return false;
}
function checkToLocaleTimeStringSupportsOptions() {
try {
new Date().toLocaleTimeString("i");
} catch (e) {
return e.name === "RangeError";
}
return false;
}
function checkToLocaleStringSupportsOptions() {
try {
new Date().toLocaleString("i");
} catch (e) {
return e.name === "RangeError";
}
return false;
}
export const toLocaleDateStringSupportsOptions =
checkToLocaleDateStringSupportsOptions();
export const toLocaleTimeStringSupportsOptions =
checkToLocaleTimeStringSupportsOptions();
export const toLocaleStringSupportsOptions =
checkToLocaleStringSupportsOptions();

View File

@ -1,13 +1,15 @@
import { format } from "fecha";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
import { polyfillsLoaded } from "../translations/localize";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
// Tuesday, August 10
export const formatDateWeekday = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "dddd, MMMM D");
export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj);
const formatDateWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -18,10 +20,9 @@ const formatDateWeekdayMem = memoizeOne(
);
// August 10, 2021
export const formatDate = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY");
export const formatDate = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMem(locale).format(dateObj);
const formatDateMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -32,10 +33,9 @@ const formatDateMem = memoizeOne(
);
// 10/08/2021
export const formatDateNumeric = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateNumericMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "M/D/YYYY");
export const formatDateNumeric = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateNumericMem(locale).format(dateObj);
const formatDateNumericMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -46,10 +46,9 @@ const formatDateNumericMem = memoizeOne(
);
// Aug 10
export const formatDateShort = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateShortMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMM D");
export const formatDateShort = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateShortMem(locale).format(dateObj);
const formatDateShortMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -59,10 +58,11 @@ const formatDateShortMem = memoizeOne(
);
// August 2021
export const formatDateMonthYear = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMonthYearMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMMM YYYY");
export const formatDateMonthYear = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateMonthYearMem(locale).format(dateObj);
const formatDateMonthYearMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -72,10 +72,9 @@ const formatDateMonthYearMem = memoizeOne(
);
// August
export const formatDateMonth = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMonthMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMMM");
export const formatDateMonth = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMonthMem(locale).format(dateObj);
const formatDateMonthMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -84,10 +83,9 @@ const formatDateMonthMem = memoizeOne(
);
// 2021
export const formatDateYear = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateYearMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "YYYY");
export const formatDateYear = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateYearMem(locale).format(dateObj);
const formatDateYearMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {

View File

@ -1,40 +1,41 @@
import { format } from "fecha";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleStringSupportsOptions } from "./check_options_support";
import { useAmPm } from "./use_am_pm";
import { polyfillsLoaded } from "../translations/localize";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
// August 9, 2021, 8:23 AM
export const formatDateTime = toLocaleStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
export const formatDateTime = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeMem(locale).format(dateObj);
const formatDateTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit",
hour12: useAmPm(locale),
})
);
// August 9, 2021, 8:23:15 AM
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeWithSecondsMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");
export const formatDateTimeWithSeconds = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateTimeWithSecondsMem(locale).format(dateObj);
const formatDateTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
@ -42,11 +43,11 @@ const formatDateTimeWithSecondsMem = memoizeOne(
);
// 9/8/2021, 8:23 AM
export const formatDateTimeNumeric = toLocaleStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeNumericMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "M/D/YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
export const formatDateTimeNumeric = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateTimeNumericMem(locale).format(dateObj);
const formatDateTimeNumericMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {

View File

@ -1,15 +1,16 @@
import { format } from "fecha";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
import { useAmPm } from "./use_am_pm";
import { polyfillsLoaded } from "../translations/localize";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
// 9:15 PM || 21:15
export const formatTime = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
export const formatTime = (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeMem(locale).format(dateObj);
const formatTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -20,15 +21,15 @@ const formatTimeMem = memoizeOne(
);
// 9:15:24 PM || 21:15:24
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWithSecondsMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
export const formatTimeWithSeconds = (
dateObj: Date,
locale: FrontendLocaleData
) => formatTimeWithSecondsMem(locale).format(dateObj);
const formatTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
hour: "numeric",
hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
@ -36,17 +37,15 @@ const formatTimeWithSecondsMem = memoizeOne(
);
// Tuesday 7:00 PM || Tuesday 19:00
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWeekdayMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");
export const formatTimeWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWeekdayMem(locale).format(dateObj);
const formatTimeWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
hour: "numeric",
hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
})
);

View File

@ -1,48 +1,32 @@
import { LocalizeFunc } from "../translations/localize";
import { selectUnit } from "@formatjs/intl-utils";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { polyfillsLoaded } from "../translations/localize";
/**
* Calculate a string representing a date object as relative time from now.
*
* Example output: 5 minutes ago, in 3 days.
*/
const tests = [60, 60, 24, 7];
const langKey = ["second", "minute", "hour", "day"];
export default function relativeTime(
dateObj: Date,
localize: LocalizeFunc,
options: {
compareTime?: Date;
includeTense?: boolean;
} = {}
): string {
const compareTime = options.compareTime || new Date();
let delta = (compareTime.getTime() - dateObj.getTime()) / 1000;
const tense = delta >= 0 ? "past" : "future";
delta = Math.abs(delta);
let roundedDelta = Math.round(delta);
if (roundedDelta === 0) {
return localize("ui.components.relative_time.just_now");
}
let unit = "week";
for (let i = 0; i < tests.length; i++) {
if (roundedDelta < tests[i]) {
unit = langKey[i];
break;
}
delta /= tests[i];
roundedDelta = Math.round(delta);
}
return localize(
options.includeTense === false
? `ui.components.relative_time.duration.${unit}`
: `ui.components.relative_time.${tense}_duration.${unit}`,
"count",
roundedDelta
);
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
const formatRelTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
// @ts-expect-error
new Intl.RelativeTimeFormat(locale.language, { numeric: "auto" })
);
export const relativeTime = (
from: Date,
locale: FrontendLocaleData,
to?: Date,
includeTense = true
): string => {
const diff = selectUnit(from, to);
if (includeTense) {
return formatRelTimeMem(locale).format(diff.value, diff.unit);
}
return Intl.NumberFormat(locale.language, {
style: "unit",
// @ts-expect-error
unit: diff.unit,
unitDisplay: "long",
}).format(Math.abs(diff.value));
};

View File

@ -74,7 +74,7 @@ class Storage {
this._storage[storageKey] = value;
try {
window.localStorage.setItem(storageKey, JSON.stringify(value));
} catch (err) {
} catch (err: any) {
// Safari in private mode doesn't allow localstorage
}
}

View File

@ -167,7 +167,7 @@ const processTheme = (
const prefixedRgbKey = `--${rgbKey}`;
styles[prefixedRgbKey] = rgbValue;
keys[prefixedRgbKey] = "";
} catch (e) {
} catch (err: any) {
continue;
}
}

View File

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

View File

@ -0,0 +1,24 @@
/** Return an icon representing a alarm panel state. */
export const alarmPanelIcon = (state?: string) => {
switch (state) {
case "armed_away":
return "hass:shield-lock";
case "armed_vacation":
return "hass:shield-airplane";
case "armed_home":
return "hass:shield-home";
case "armed_night":
return "hass:shield-moon";
case "armed_custom_bypass":
return "hass:security";
case "pending":
return "hass:shield-outline";
case "triggered":
return "hass:bell-ring";
case "disarmed":
return "hass:shield-off";
default:
return "hass:shield";
}
};

View File

@ -22,8 +22,9 @@ export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
case "gas":
case "problem":
case "safety":
case "smoke":
return is_off ? "hass:check-circle" : "hass:alert-circle";
case "smoke":
return is_off ? "hass:check-circle" : "hass:smoke";
case "heat":
return is_off ? "hass:thermometer" : "hass:fire";
case "light":

View File

@ -4,7 +4,7 @@ import { FrontendLocaleData } from "../../data/translation";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
import { formatTime } from "../datetime/format_time";
import { formatNumber } from "../string/format_number";
import { formatNumber } from "../number/format_number";
import { LocalizeFunc } from "../translations/localize";
import { computeStateDomain } from "./compute_state_domain";

View File

@ -5,6 +5,7 @@ import { HassEntity } from "home-assistant-js-websocket";
* Optionally pass in a state to influence the domain icon.
*/
import { DEFAULT_DOMAIN_ICON, FIXED_DOMAIN_ICONS } from "../const";
import { alarmPanelIcon } from "./alarm_panel_icon";
import { binarySensorIcon } from "./binary_sensor_icon";
import { coverIcon } from "./cover_icon";
import { sensorIcon } from "./sensor_icon";
@ -18,18 +19,7 @@ export const domainIcon = (
switch (domain) {
case "alarm_control_panel":
switch (compareState) {
case "armed_home":
return "hass:bell-plus";
case "armed_night":
return "hass:bell-sleep";
case "disarmed":
return "hass:bell-outline";
case "triggered":
return "hass:bell-ring";
default:
return "hass:bell";
}
return alarmPanelIcon(compareState);
case "binary_sensor":
return binarySensorIcon(compareState, stateObj);

View File

@ -2,6 +2,7 @@
import { HassEntity } from "home-assistant-js-websocket";
import { FIXED_DEVICE_CLASS_ICONS, UNIT_C, UNIT_F } from "../const";
import { batteryIcon } from "./battery_icon";
import { SENSOR_DEVICE_CLASS_BATTERY } from "../../data/sensor";
export const sensorIcon = (stateObj?: HassEntity): string | undefined => {
const dclass = stateObj?.attributes.device_class;
@ -10,7 +11,7 @@ export const sensorIcon = (stateObj?: HassEntity): string | undefined => {
return FIXED_DEVICE_CLASS_ICONS[dclass];
}
if (dclass === "battery") {
if (dclass === SENSOR_DEVICE_CLASS_BATTERY) {
return stateObj ? batteryIcon(stateObj) : "hass:battery";
}

View File

@ -1,5 +1,5 @@
import { FrontendLocaleData, NumberFormat } from "../../data/translation";
import { round } from "../number/round";
import { round } from "./round";
export const numberFormatToLocale = (
localeOptions: FrontendLocaleData
@ -51,10 +51,10 @@ export const formatNumber = (
locale,
getDefaultFormatOptions(num, options)
).format(Number(num));
} catch (error) {
} catch (err: any) {
// Don't fail when using "TEST" language
// eslint-disable-next-line no-console
console.error(error);
console.error(err);
return new Intl.NumberFormat(
undefined,
getDefaultFormatOptions(num, options)

View File

@ -1,4 +1,4 @@
export const compare = (a: string, b: string) => {
export const stringCompare = (a: string, b: string) => {
if (a < b) {
return -1;
}
@ -9,5 +9,5 @@ export const compare = (a: string, b: string) => {
return 0;
};
export const caseInsensitiveCompare = (a: string, b: string) =>
compare(a.toLowerCase(), b.toLowerCase());
export const caseInsensitiveStringCompare = (a: string, b: string) =>
stringCompare(a.toLowerCase(), b.toLowerCase());

View File

@ -1,6 +1,6 @@
import { refine, string } from "superstruct";
const isCustomType = (value: string) => value.startsWith("custom:");
export const isCustomType = (value: string) => value.startsWith("custom:");
export const customType = () =>
refine(string(), "custom element type", isCustomType);

View File

@ -1,4 +1,7 @@
import { shouldPolyfill } from "@formatjs/intl-pluralrules/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillLocale } from "@formatjs/intl-locale/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillPluralRules } from "@formatjs/intl-pluralrules/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-relativetimeformat/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillDateTime } from "@formatjs/intl-datetimeformat/lib/should-polyfill";
import IntlMessageFormat from "intl-messageformat";
import { Resources } from "../../types";
@ -14,15 +17,35 @@ export interface FormatsType {
let loadedPolyfillLocale: Set<string> | undefined;
let polyfillLoaded = !shouldPolyfill();
const polyfillProm = polyfillLoaded
const polyfillPluralRules = shouldPolyfillPluralRules();
const polyfillRelativeTime = shouldPolyfillRelativeTime();
const polyfillDateTime = shouldPolyfillDateTime();
const polyfills: Promise<any>[] = [];
if (__BUILD__ === "latest") {
if (shouldPolyfillLocale()) {
polyfills.push(import("@formatjs/intl-locale/polyfill"));
}
if (polyfillPluralRules) {
polyfills.push(import("@formatjs/intl-pluralrules/polyfill"));
}
if (polyfillRelativeTime) {
polyfills.push(import("@formatjs/intl-relativetimeformat/polyfill"));
}
if (polyfillDateTime) {
polyfills.push(import("@formatjs/intl-datetimeformat/polyfill"));
}
}
let polyfillLoaded = polyfills.length === 0;
export const polyfillsLoaded = polyfillLoaded
? undefined
: import("@formatjs/intl-locale/polyfill")
.then(() => import("@formatjs/intl-pluralrules/polyfill"))
.then(() => {
loadedPolyfillLocale = new Set();
polyfillLoaded = true;
});
: Promise.all(polyfills).then(() => {
loadedPolyfillLocale = new Set();
polyfillLoaded = true;
// Load English so it becomes the default
return loadPolyfillLocales("en");
});
/**
* Adapted from Polymer app-localize-behavior.
@ -52,17 +75,10 @@ export const computeLocalize = async (
formats?: FormatsType
): Promise<LocalizeFunc> => {
if (!polyfillLoaded) {
await polyfillProm;
await polyfillsLoaded;
}
if (loadedPolyfillLocale && !loadedPolyfillLocale.has(language)) {
try {
loadedPolyfillLocale.add(language);
await import("@formatjs/intl-pluralrules/locale-data/en");
} catch (_e) {
// Ignore
}
}
loadPolyfillLocales(language);
// Everytime any of the parameters change, invalidate the strings cache.
cache._localizationCache = {};
@ -92,7 +108,7 @@ export const computeLocalize = async (
language,
formats
);
} catch (err) {
} catch (err: any) {
return "Translation error: " + err.message;
}
cache._localizationCache[messageKey] = translatedMessage;
@ -109,8 +125,28 @@ export const computeLocalize = async (
try {
return translatedMessage.format<string>(argObject) as string;
} catch (err) {
} catch (err: any) {
return "Translation " + err;
}
};
};
export const loadPolyfillLocales = async (language: string) => {
if (!loadedPolyfillLocale || loadedPolyfillLocale.has(language)) {
return;
}
loadedPolyfillLocale.add(language);
try {
if (polyfillPluralRules) {
await import(`@formatjs/intl-pluralrules/locale-data/${language}`);
}
if (polyfillRelativeTime) {
await import(`@formatjs/intl-relativetimeformat/locale-data/${language}`);
}
if (polyfillDateTime) {
await import(`@formatjs/intl-datetimeformat/locale-data/${language}`);
}
} catch (_e) {
// Ignore
}
};

View File

@ -25,7 +25,7 @@ export default function parseAspectRatio(input: string) {
return arr.length === 1
? { w: parseOrThrow(arr[0]), h: 1 }
: { w: parseOrThrow(arr[0]), h: parseOrThrow(arr[1]) };
} catch (err) {
} catch (err: any) {
// Ignore the error
}
return null;

View File

@ -50,7 +50,7 @@ class HaCallApiButton extends LitElement {
this._progressButton.actionSuccess();
eventData.success = true;
eventData.response = resp;
} catch (err) {
} catch (err: any) {
this.progress = false;
this._progressButton.actionError();
eventData.success = false;

View File

@ -5,7 +5,7 @@ import { getColorByIndex } from "../../common/color/colors";
import {
formatNumber,
numberFormatToLocale,
} from "../../common/string/format_number";
} from "../../common/number/format_number";
import { LineChartEntity, LineChartState } from "../../data/history";
import { HomeAssistant } from "../../types";
import "./ha-chart-base";

View File

@ -5,7 +5,7 @@ import { customElement, property, state } from "lit/decorators";
import { getColorByIndex } from "../../common/color/colors";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import { computeDomain } from "../../common/entity/compute_domain";
import { numberFormatToLocale } from "../../common/string/format_number";
import { numberFormatToLocale } from "../../common/number/format_number";
import { computeRTL } from "../../common/util/compute_rtl";
import { TimelineEntity } from "../../data/history";
import { HomeAssistant } from "../../types";

View File

@ -19,7 +19,7 @@ import { computeStateName } from "../../common/entity/compute_state_name";
import {
formatNumber,
numberFormatToLocale,
} from "../../common/string/format_number";
} from "../../common/number/format_number";
import {
getStatisticIds,
Statistics,

View File

@ -550,7 +550,7 @@ export class HaDataTable extends LitElement {
private _handleRowClick(ev: Event) {
const target = ev.target as HTMLElement;
if (target.tagName === "HA-CHECKBOX") {
if (["HA-CHECKBOX", "MWC-BUTTON"].includes(target.tagName)) {
return;
}
const rowId = (ev.currentTarget as any).rowId;

View File

@ -20,7 +20,7 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import { compare } from "../../common/string/compare";
import { stringCompare } from "../../common/string/compare";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
@ -50,6 +50,7 @@ interface AreaDevices {
devices: string[];
}
// eslint-disable-next-line lit/prefer-static-styles
const rowRenderer: ComboBoxLitRenderer<AreaDevices> = (item) => html`<style>
paper-item {
padding: 0;
@ -226,7 +227,10 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
const sorted = Object.keys(devicesByArea)
.sort((a, b) =>
compare(devicesByArea[a].name || "", devicesByArea[b].name || "")
stringCompare(
devicesByArea[a].name || "",
devicesByArea[b].name || ""
)
)
.map((key) => devicesByArea[key]);

View File

@ -15,7 +15,7 @@ import { ComboBoxLitRenderer } from "lit-vaadin-helpers";
import { mdiCheck } from "@mdi/js";
import { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import { compare } from "../../common/string/compare";
import { stringCompare } from "../../common/string/compare";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
@ -46,6 +46,7 @@ export type HaDevicePickerDeviceFilterFunc = (
device: DeviceRegistryEntry
) => boolean;
// eslint-disable-next-line lit/prefer-static-styles
const rowRenderer: ComboBoxLitRenderer<Device> = (item) => html`<style>
paper-item {
padding: 0;
@ -242,7 +243,9 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
if (outputDevices.length === 1) {
return outputDevices;
}
return outputDevices.sort((a, b) => compare(a.name || "", b.name || ""));
return outputDevices.sort((a, b) =>
stringCompare(a.name || "", b.name || "")
);
}
);

View File

@ -15,7 +15,7 @@ const haTabFixBehaviorImpl = {
},
};
// paper-dialog that uses the haTabFixBehaviorImpl behvaior
// paper-dialog that uses the haTabFixBehaviorImpl behavior
// export class HaPaperDialog extends paperDialogClass {}
// @ts-ignore
export class HaPaperDialog

View File

@ -23,6 +23,7 @@ import "./state-badge";
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
// eslint-disable-next-line lit/prefer-static-styles
const rowRenderer: ComboBoxLitRenderer<string> = (item) => html`<style>
paper-item {
padding: 0;

View File

@ -26,6 +26,7 @@ import "./state-badge";
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
// eslint-disable-next-line lit/prefer-static-styles
const rowRenderer: ComboBoxLitRenderer<HassEntity> = (item) => html`<style>
paper-icon-item {
padding: 0;

View File

@ -13,10 +13,9 @@ import secondsToDuration from "../../common/datetime/seconds_to_duration";
import { computeStateDisplay } from "../../common/entity/compute_state_display";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { domainIcon } from "../../common/entity/domain_icon";
import { stateIcon } from "../../common/entity/state_icon";
import { timerTimeRemaining } from "../../data/timer";
import { formatNumber } from "../../common/string/format_number";
import { formatNumber } from "../../common/number/format_number";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { HomeAssistant } from "../../types";
import "../ha-label-badge";
@ -58,11 +57,11 @@ export class HaStateLabelBadge extends LitElement {
return html`
<ha-label-badge
class="warning"
label="${this.hass!.localize("state_badge.default.error")}"
label=${this.hass!.localize("state_badge.default.error")}
icon="hass:alert"
description="${this.hass!.localize(
description=${this.hass!.localize(
"state_badge.default.entity_not_found"
)}"
)}
></ha-label-badge>
`;
}
@ -71,27 +70,25 @@ export class HaStateLabelBadge extends LitElement {
return html`
<ha-label-badge
class="${classMap({
class=${classMap({
[domain]: true,
"has-unit_of_measurement":
"unit_of_measurement" in entityState.attributes,
})}"
.value="${this._computeValue(domain, entityState)}"
.icon="${this.icon
? this.icon
: this._computeIcon(domain, entityState)}"
.image="${this.icon
})}
.value=${this._computeValue(domain, entityState)}
.icon=${this.icon ? this.icon : this._computeIcon(domain, entityState)}
.image=${this.icon
? ""
: this.image
? this.image
: entityState.attributes.entity_picture_local ||
entityState.attributes.entity_picture}"
.label="${this._computeLabel(
entityState.attributes.entity_picture}
.label=${this._computeLabel(
domain,
entityState,
this._timerTimeRemaining
)}"
.description="${this.name ? this.name : computeStateName(entityState)}"
)}
.description=${this.name ? this.name : computeStateName(entityState)}
></ha-label-badge>
`;
}
@ -106,19 +103,24 @@ export class HaStateLabelBadge extends LitElement {
private _computeValue(domain: string, entityState: HassEntity) {
switch (domain) {
case "alarm_control_panel":
case "binary_sensor":
case "device_tracker":
case "person":
case "updater":
case "scene":
case "sun":
case "alarm_control_panel":
case "timer":
case "updater":
return null;
// @ts-expect-error we don't break and go to default
case "sensor":
if (entityState.attributes.device_class === "moon__phase") {
return null;
}
// eslint-disable-next-line: disable=no-fallthrough
default:
return entityState.attributes.device_class === "moon__phase"
? null
: entityState.state === UNKNOWN
return entityState.state === UNKNOWN ||
entityState.state === UNAVAILABLE
? "-"
: entityState.attributes.unit_of_measurement
? formatNumber(entityState.state, this.hass!.locale)
@ -136,40 +138,23 @@ export class HaStateLabelBadge extends LitElement {
}
switch (domain) {
case "alarm_control_panel":
if (entityState.state === "pending") {
return "hass:clock-fast";
}
if (entityState.state === "armed_away") {
return "hass:nature";
}
if (entityState.state === "armed_home") {
return "hass:home-variant";
}
if (entityState.state === "armed_night") {
return "hass:weather-night";
}
if (entityState.state === "armed_custom_bypass") {
return "hass:shield-home";
}
if (entityState.state === "triggered") {
return "hass:alert-circle";
}
// state == 'disarmed'
return domainIcon(domain, entityState);
case "binary_sensor":
case "device_tracker":
case "updater":
case "person":
case "scene":
case "sun":
return stateIcon(entityState);
case "timer":
return entityState.state === "active"
? "hass:timer-outline"
: "hass:timer-off-outline";
default:
return entityState?.attributes.device_class === "moon__phase"
case "sensor":
return entityState.attributes.device_class === "moon__phase"
? stateIcon(entityState)
: null;
default:
return null;
}
}

View File

@ -18,7 +18,7 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import { computeStateName } from "../../common/entity/compute_state_name";
import { compare } from "../../common/string/compare";
import { stringCompare } from "../../common/string/compare";
import { getStatisticIds, StatisticsMetaData } from "../../data/history";
import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types";
@ -51,6 +51,14 @@ export class HaStatisticPicker extends LitElement {
@property({ type: Array, attribute: "include-unit-of-measurement" })
public includeUnitOfMeasurement?: string[];
/**
* Show only statistics with these device classes.
* @type {Array}
* @attr include-device-classes
*/
@property({ type: Array, attribute: "include-device-classes" })
public includeDeviceClasses?: string[];
/**
* Show only statistics on entities.
* @type {Boolean}
@ -69,6 +77,7 @@ export class HaStatisticPicker extends LitElement {
id: string;
name: string;
state?: HassEntity;
// eslint-disable-next-line lit/prefer-static-styles
}> = (item) => html`<style>
paper-icon-item {
padding: 0;
@ -102,7 +111,7 @@ export class HaStatisticPicker extends LitElement {
? html`<a
target="_blank"
rel="noopener noreferrer"
href="${documentationUrl(this.hass, "/more-info/statistics/")}"
href=${documentationUrl(this.hass, "/more-info/statistics/")}
>${this.hass.localize(
"ui.components.statistic-picker.learn_more"
)}</a
@ -116,6 +125,7 @@ export class HaStatisticPicker extends LitElement {
(
statisticIds: StatisticsMetaData[],
includeUnitOfMeasurement?: string[],
includeDeviceClasses?: string[],
entitiesOnly?: boolean
): Array<{ id: string; name: string; state?: HassEntity }> => {
if (!statisticIds.length) {
@ -148,11 +158,18 @@ export class HaStatisticPicker extends LitElement {
}
return;
}
output.push({
id: meta.statistic_id,
name: computeStateName(entityState),
state: entityState,
});
if (
!includeDeviceClasses ||
includeDeviceClasses.includes(
entityState!.attributes.device_class || ""
)
) {
output.push({
id: meta.statistic_id,
name: computeStateName(entityState),
state: entityState,
});
}
});
if (!output.length) {
@ -165,7 +182,7 @@ export class HaStatisticPicker extends LitElement {
}
if (output.length > 1) {
output.sort((a, b) => compare(a.name || "", b.name || ""));
output.sort((a, b) => stringCompare(a.name || "", b.name || ""));
}
output.push({
@ -203,6 +220,7 @@ export class HaStatisticPicker extends LitElement {
(this.comboBox as any).items = this._getStatistics(
this.statisticIds!,
this.includeUnitOfMeasurement,
this.includeDeviceClasses,
this.entitiesOnly
);
} else {
@ -210,6 +228,7 @@ export class HaStatisticPicker extends LitElement {
(this.comboBox as any).items = this._getStatistics(
this.statisticIds!,
this.includeUnitOfMeasurement,
this.includeDeviceClasses,
this.entitiesOnly
);
});

View File

@ -129,38 +129,39 @@ export class StateBadge extends LitElement {
}
static get styles(): CSSResultGroup {
return css`
:host {
position: relative;
display: inline-block;
width: 40px;
color: var(--paper-item-icon-color, #44739e);
border-radius: 50%;
height: 40px;
text-align: center;
background-size: cover;
line-height: 40px;
vertical-align: middle;
box-sizing: border-box;
}
:host(:focus) {
outline: none;
}
:host(:not([icon]):focus) {
border: 2px solid var(--divider-color);
}
:host([icon]:focus) {
background: var(--divider-color);
}
ha-icon {
transition: color 0.3s ease-in-out, filter 0.3s ease-in-out;
}
.missing {
color: #fce588;
}
${iconColorCSS}
`;
return [
iconColorCSS,
css`
:host {
position: relative;
display: inline-block;
width: 40px;
color: var(--paper-item-icon-color, #44739e);
border-radius: 50%;
height: 40px;
text-align: center;
background-size: cover;
line-height: 40px;
vertical-align: middle;
box-sizing: border-box;
}
:host(:focus) {
outline: none;
}
:host(:not([icon]):focus) {
border: 2px solid var(--divider-color);
}
:host([icon]:focus) {
background: var(--divider-color);
}
ha-icon {
transition: color 0.3s ease-in-out, filter 0.3s ease-in-out;
}
.missing {
color: #fce588;
}
`,
];
}
}

View File

@ -4,7 +4,7 @@ import { ComboBoxLitRenderer } from "lit-vaadin-helpers";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { fireEvent } from "../common/dom/fire_event";
import { compare } from "../common/string/compare";
import { stringCompare } from "../common/string/compare";
import { HassioAddonInfo } from "../data/hassio/addon";
import { fetchHassioSupervisorInfo } from "../data/hassio/supervisor";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
@ -12,6 +12,7 @@ import { PolymerChangedEvent } from "../polymer-types";
import { HomeAssistant } from "../types";
import { HaComboBox } from "./ha-combo-box";
// eslint-disable-next-line lit/prefer-static-styles
const rowRenderer: ComboBoxLitRenderer<HassioAddonInfo> = (item) => html`<style>
paper-item {
padding: 0;
@ -97,7 +98,7 @@ class HaAddonPicker extends LitElement {
if (isComponentLoaded(this.hass, "hassio")) {
const supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
this._addons = supervisorInfo.addons.sort((a, b) =>
compare(a.name, b.name)
stringCompare(a.name, b.name)
);
} else {
showAlertDialog(this, {
@ -109,7 +110,7 @@ class HaAddonPicker extends LitElement {
),
});
}
} catch (error) {
} catch (err: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.componencts.addon-picker.error.fetch_addons.title"

View File

@ -5,6 +5,7 @@ import {
mdiAlertOutline,
mdiCheckboxMarkedCircleOutline,
mdiClose,
mdiInformationOutline,
} from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
@ -13,7 +14,7 @@ import { fireEvent } from "../common/dom/fire_event";
import "./ha-svg-icon";
const ALERT_ICONS = {
info: mdiAlertCircleOutline,
info: mdiInformationOutline,
warning: mdiAlertOutline,
error: mdiAlertCircleOutline,
success: mdiCheckboxMarkedCircleOutline,

View File

@ -46,6 +46,7 @@ import "./ha-svg-icon";
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (
item
// eslint-disable-next-line lit/prefer-static-styles
) => html`<style>
paper-item {
padding: 0;
@ -435,7 +436,7 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
});
this._areas = [...this._areas!, area];
this._setValue(area.area_id);
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
text: this.hass.localize(
"ui.components.area-picker.add_dialog.failed_create_area"

View File

@ -5,7 +5,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { compare } from "../common/string/compare";
import { stringCompare } from "../common/string/compare";
import { Blueprint, Blueprints, fetchBlueprints } from "../data/blueprint";
import { HomeAssistant } from "../types";
@ -33,7 +33,7 @@ class HaBluePrintPicker extends LitElement {
...(blueprint as Blueprint).metadata,
path,
}));
return result.sort((a, b) => compare(a.name, b.name));
return result.sort((a, b) => stringCompare(a.name, b.name));
});
protected render(): TemplateResult {

View File

@ -117,7 +117,7 @@ class HaCameraStream extends LitElement {
);
this._url = url;
} catch (err) {
} catch (err: any) {
// Fails if we were unable to get a stream
// eslint-disable-next-line
console.error(err);

View File

@ -1,7 +1,7 @@
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { formatNumber } from "../common/string/format_number";
import { formatNumber } from "../common/number/format_number";
import { CLIMATE_PRESET_NONE } from "../data/climate";
import type { HomeAssistant } from "../types";

View File

@ -13,6 +13,7 @@ import { PolymerChangedEvent } from "../polymer-types";
import { HomeAssistant } from "../types";
import "./ha-svg-icon";
// eslint-disable-next-line lit/prefer-static-styles
const defaultRowRenderer: ComboBoxLitRenderer<string> = (item) => html`<style>
paper-item {
margin: -5px -10px;

View File

@ -32,17 +32,19 @@ export class HaFormFloat extends LitElement implements HaFormElement {
.autoValidate=${this.schema.required}
@value-changed=${this._valueChanged}
>
<span suffix="" slot="suffix">${this.suffix}</span>
<span suffix slot="suffix">${this.suffix}</span>
</paper-input>
`;
}
private get _value() {
return this.data || 0;
return this.data;
}
private _valueChanged(ev: Event) {
const value = Number((ev.target as PaperInputElement).value);
const value: number | undefined = (ev.target as PaperInputElement).value
? Number((ev.target as PaperInputElement).value)
: undefined;
if (this._value === value) {
return;
}

View File

@ -2,7 +2,7 @@ import { css, LitElement, PropertyValues, svg, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { formatNumber } from "../common/string/format_number";
import { formatNumber } from "../common/number/format_number";
import { afterNextRender } from "../common/util/render-status";
import { FrontendLocaleData } from "../data/translation";
import { getValueInPercentage, normalize } from "../util/calculate";

View File

@ -29,7 +29,335 @@ interface DeprecatedIcon {
};
}
const mdiDeprecatedIcons: DeprecatedIcon = {};
const mdiDeprecatedIcons: DeprecatedIcon = {
"adobe-acrobat": {
removeIn: "2021.12",
},
adobe: {
removeIn: "2021.12",
},
"amazon-alexa": {
removeIn: "2021.12",
},
amazon: {
removeIn: "2021.12",
},
"android-auto": {
removeIn: "2021.12",
},
"android-debug-bridge": {
removeIn: "2021.12",
},
"apple-airplay": {
newName: "cast-variant",
removeIn: "2021.12",
},
application: {
newName: "application-outline",
removeIn: "2021.12",
},
"application-cog": {
newName: "application-cog-outline",
removeIn: "2021.12",
},
"application-settings": {
newName: "application-settings-outline",
removeIn: "2021.12",
},
bandcamp: {
removeIn: "2021.12",
},
battlenet: {
removeIn: "2021.12",
},
blogger: {
removeIn: "2021.12",
},
"bolnisi-cross": {
newName: "cross-bolnisi",
removeIn: "2021.12",
},
"boom-gate-up": {
newName: "boom-gate-arrow-up",
removeIn: "2021.12",
},
"boom-gate-up-outline": {
newName: "boom-gate-arrow-up-outline",
removeIn: "2021.12",
},
"boom-gate-down": {
newName: "boom-gate-arrow-down",
removeIn: "2021.12",
},
"boom-gate-down-outline": {
newName: "boom-gate-arrow-down-outline",
removeIn: "2021.12",
},
buddhism: {
newName: "dharmachakra",
removeIn: "2021.12",
},
buffer: {
removeIn: "2021.12",
},
"cash-usd-outline": {
removeIn: "2021.12",
},
"cash-usd": {
removeIn: "2021.12",
},
"cellphone-android": {
newName: "cellphone",
removeIn: "2021.12",
},
"cellphone-erase": {
newName: "cellphone-remove",
removeIn: "2021.12",
},
"cellphone-iphone": {
newName: "cellphone",
removeIn: "2021.12",
},
"celtic-cross": {
newName: "cross-celtic",
removeIn: "2021.12",
},
christianity: {
newName: "cross",
removeIn: "2021.12",
},
"christianity-outline": {
newName: "cross-outline",
removeIn: "2021.12",
},
"concourse-ci": {
removeIn: "2021.12",
},
"currency-usd-circle": {
removeIn: "2021.12",
},
"currency-usd-circle-outline": {
removeIn: "2021.12",
},
"do-not-disturb-off": {
newName: "minus-circle-off",
removeIn: "2021.12",
},
"do-not-disturb": {
newName: "minus-circle",
removeIn: "2021.12",
},
douban: {
removeIn: "2021.12",
},
face: {
newName: "face-man",
removeIn: "2021.12",
},
"face-outline": {
newName: "face-man-outline",
removeIn: "2021.12",
},
"face-profile-woman": {
newName: "face-woman-profile",
removeIn: "2021.12",
},
"face-shimmer": {
newName: "face-man-shimmer",
removeIn: "2021.12",
},
"face-shimmer-outline": {
newName: "face-man-shimmer-outline",
removeIn: "2021.12",
},
"file-pdf": {
newName: "file-pdf-box",
removeIn: "2021.12",
},
"file-pdf-outline": {
newName: "file-pdf-box",
removeIn: "2021.12",
},
"file-pdf-box-outline": {
newName: "file-pdf-box",
removeIn: "2021.12",
},
"flash-circle": {
newName: "lightning-bolt-circle",
removeIn: "2021.12",
},
"floor-lamp-variant": {
newName: "floor-lamp-torchiere-variant",
removeIn: "2021.12",
},
gif: {
newName: "file-gif-box",
removeIn: "2021.12",
},
"google-photos": {
removeIn: "2021.12",
},
gradient: {
newName: "gradient-vertical",
removeIn: "2021.12",
},
hand: {
newName: "hand-front-right",
removeIn: "2021.12",
},
"hand-left": {
newName: "hand-back-left",
removeIn: "2021.12",
},
"hand-right": {
newName: "hand-back-right",
removeIn: "2021.12",
},
hinduism: {
newName: "om",
removeIn: "2021.12",
},
"home-currency-usd": {
removeIn: "2021.12",
},
iframe: {
newName: "application-brackets",
removeIn: "2021.12",
},
"iframe-outline": {
newName: "application-brackets-outline",
removeIn: "2021.12",
},
"iframe-array": {
newName: "application-array",
removeIn: "2021.12",
},
"iframe-array-outline": {
newName: "application-array-outline",
removeIn: "2021.12",
},
"iframe-braces": {
newName: "application-braces",
removeIn: "2021.12",
},
"iframe-braces-outline": {
newName: "application-braces-outline",
removeIn: "2021.12",
},
"iframe-parentheses": {
newName: "application-parentheses",
removeIn: "2021.12",
},
"iframe-parentheses-outline": {
newName: "application-parentheses-outline",
removeIn: "2021.12",
},
"iframe-variable": {
newName: "application-variable",
removeIn: "2021.12",
},
"iframe-variable-outline": {
newName: "application-variable-outline",
removeIn: "2021.12",
},
islam: {
newName: "star-crescent",
removeIn: "2021.12",
},
judaism: {
newName: "star-david",
removeIn: "2021.12",
},
"laptop-chromebook": {
newName: "laptop",
removeIn: "2021.12",
},
"laptop-mac": {
newName: "laptop",
removeIn: "2021.12",
},
"laptop-windows": {
newName: "laptop",
removeIn: "2021.12",
},
"microsoft-edge-legacy": {
removeIn: "2021.12",
},
"microsoft-yammer": {
removeIn: "2021.12",
},
"monitor-clean": {
newName: "monitor-shimmer",
removeIn: "2021.12",
},
"pdf-box": {
newName: "file-pdf-box",
removeIn: "2021.12",
},
pharmacy: {
newName: "mortar-pestle-plus",
removeIn: "2021.12",
},
"plus-one": {
newName: "numeric-positive-1",
removeIn: "2021.12",
},
"poll-box": {
newName: "chart-box",
removeIn: "2021.12",
},
"poll-box-outline": {
newName: "chart-box-outline",
removeIn: "2021.12",
},
sparkles: {
newName: "shimmer",
removeIn: "2021.12",
},
"tablet-ipad": {
newName: "tablet",
removeIn: "2021.12",
},
teach: {
newName: "human-male-board",
removeIn: "2021.12",
},
telegram: {
removeIn: "2021.12",
},
"television-clean": {
newName: "television-shimmer",
removeIn: "2021.12",
},
"text-subject": {
newName: "text-long",
removeIn: "2021.12",
},
"twitter-retweet": {
newName: "repeat-variant",
removeIn: "2021.12",
},
untappd: {
removeIn: "2021.12",
},
vk: {
removeIn: "2021.12",
},
"voice-off": {
newName: "account-voice-off",
removeIn: "2021.12",
},
"xamarian-outline": {
newName: "xamarian",
removeIn: "2021.12",
},
xing: {
removeIn: "2021.12",
},
"y-combinator": {
removeIn: "2021.12",
},
};
const chunks: Chunks = {};

View File

@ -26,10 +26,10 @@ class HaLabelBadge extends LitElement {
<div class="badge-container">
<div class="label-badge" id="badge">
<div
class="${classMap({
class=${classMap({
value: true,
big: Boolean(this.value && this.value.length > 4),
})}"
})}
>
<slot>
${this.icon && !this.value && !this.image
@ -43,10 +43,10 @@ class HaLabelBadge extends LitElement {
${this.label
? html`
<div
class="${classMap({
class=${classMap({
label: true,
big: this.label.length > 5,
})}"
})}
>
<span>${this.label}</span>
</div>

View File

@ -38,9 +38,8 @@ class HaMarkdownElement extends ReactiveElement {
const walker = document.createTreeWalker(
this,
1 /* SHOW_ELEMENT */,
null,
false
NodeFilter.SHOW_ELEMENT,
null
);
while (walker.nextNode()) {

View File

@ -89,7 +89,7 @@ export class HaPictureUpload extends LitElement {
const media = await createImage(this.hass, file);
this.value = generateImageThumbnailUrl(media.id, this.size);
fireEvent(this, "change");
} catch (err) {
} catch (err: any) {
showAlertDialog(this, {
text: err.toString(),
});

View File

@ -162,7 +162,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<li>
<button
@click=${this._openMoreInfo}
.entityId="${entityId}"
.entityId=${entityId}
class="link"
>
${entity.attributes.friendly_name || entityId}
@ -187,7 +187,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<button
class="link"
@click=${this._openMoreInfo}
.entityId="${groupId}"
.entityId=${groupId}
>
${group.attributes.friendly_name || group.entity_id}
</button>
@ -212,7 +212,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<button
class="link"
@click=${this._openMoreInfo}
.entityId="${sceneId}"
.entityId=${sceneId}
>
${scene.attributes.friendly_name || scene.entity_id}
</button>
@ -239,7 +239,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<button
class="link"
@click=${this._openMoreInfo}
.entityId="${automationId}"
.entityId=${automationId}
>
${automation.attributes.friendly_name ||
automation.entity_id}
@ -267,7 +267,7 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
<button
class="link"
@click=${this._openMoreInfo}
.entityId="${scriptId}"
.entityId=${scriptId}
>
${script.attributes.friendly_name || script.entity_id}
</button>

View File

@ -1,6 +1,6 @@
import { PropertyValues, ReactiveElement } from "lit";
import { customElement, property } from "lit/decorators";
import relativeTime from "../common/datetime/relative_time";
import { relativeTime } from "../common/datetime/relative_time";
import type { HomeAssistant } from "../types";
@customElement("ha-relative-time")
@ -55,10 +55,7 @@ class HaRelativeTime extends ReactiveElement {
if (!this.datetime) {
this.innerHTML = this.hass.localize("ui.components.relative_time.never");
} else {
this.innerHTML = relativeTime(
new Date(this.datetime),
this.hass.localize
);
this.innerHTML = relativeTime(new Date(this.datetime), this.hass.locale);
}
}
}

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