mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 13:07:49 +00:00
Merge pull request #10578 from home-assistant/dev
This commit is contained in:
commit
72b9f8636d
@ -165,6 +165,7 @@ module.exports.config = {
|
|||||||
cast({ isProdBuild, latestBuild }) {
|
cast({ isProdBuild, latestBuild }) {
|
||||||
const entry = {
|
const entry = {
|
||||||
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
|
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
|
||||||
|
media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (latestBuild) {
|
if (latestBuild) {
|
||||||
|
@ -154,6 +154,15 @@ gulp.task("gen-index-cast-dev", (done) => {
|
|||||||
contentReceiver
|
contentReceiver
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const contentMedia = renderCastTemplate("media", {
|
||||||
|
latestMediaJS: "/frontend_latest/media.js",
|
||||||
|
es5MediaJS: "/frontend_es5/media.js",
|
||||||
|
});
|
||||||
|
fs.outputFileSync(
|
||||||
|
path.resolve(paths.cast_output_root, "media.html"),
|
||||||
|
contentMedia
|
||||||
|
);
|
||||||
|
|
||||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||||
latestLauncherJS: "/frontend_latest/launcher.js",
|
latestLauncherJS: "/frontend_latest/launcher.js",
|
||||||
es5LauncherJS: "/frontend_es5/launcher.js",
|
es5LauncherJS: "/frontend_es5/launcher.js",
|
||||||
@ -192,6 +201,15 @@ gulp.task("gen-index-cast-prod", (done) => {
|
|||||||
contentReceiver
|
contentReceiver
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const contentMedia = renderCastTemplate("media", {
|
||||||
|
latestMediaJS: latestManifest["media.js"],
|
||||||
|
es5MediaJS: es5Manifest["media.js"],
|
||||||
|
});
|
||||||
|
fs.outputFileSync(
|
||||||
|
path.resolve(paths.cast_output_root, "media.html"),
|
||||||
|
contentMedia
|
||||||
|
);
|
||||||
|
|
||||||
const contentFAQ = renderCastTemplate("launcher-faq", {
|
const contentFAQ = renderCastTemplate("launcher-faq", {
|
||||||
latestLauncherJS: latestManifest["launcher.js"],
|
latestLauncherJS: latestManifest["launcher.js"],
|
||||||
es5LauncherJS: es5Manifest["launcher.js"],
|
es5LauncherJS: es5Manifest["launcher.js"],
|
||||||
|
45
cast/src/html/media.html.template
Normal file
45
cast/src/html/media.html.template
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
--logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
||||||
|
--logo-repeat: no-repeat;
|
||||||
|
--playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg');
|
||||||
|
--theme-hue: 200;
|
||||||
|
--progress-color: #03a9f4;
|
||||||
|
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
|
||||||
|
--splash-size: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
|
||||||
|
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||||
|
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
|
||||||
|
s.parentNode.insertBefore(g,s)}(document,'script'));
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<%= renderTemplate('_js_base') %>
|
||||||
|
|
||||||
|
<cast-media-player></cast-media-player>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import("<%= latestMediaJS %>");
|
||||||
|
window.latestJS = true;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if (!window.latestJS) {
|
||||||
|
<% if (useRollup) { %>
|
||||||
|
_ls("/static/js/s.min.js").onload = function() {
|
||||||
|
System.import("<%= es5MediaJS %>");
|
||||||
|
};
|
||||||
|
<% } else { %>
|
||||||
|
_ls("<%= es5MediaJS %>");
|
||||||
|
<% } %>
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
cast/src/media/entrypoint.ts
Normal file
25
cast/src/media/entrypoint.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { CastReceiverContext } from "chromecast-caf-receiver/cast.framework";
|
||||||
|
|
||||||
|
const castContext =
|
||||||
|
cast.framework.CastContext.getInstance() as unknown as CastReceiverContext;
|
||||||
|
|
||||||
|
const playerManager = castContext.getPlayerManager();
|
||||||
|
|
||||||
|
playerManager.setMessageInterceptor(
|
||||||
|
cast.framework.messages.MessageType.LOAD,
|
||||||
|
(loadRequestData) => {
|
||||||
|
const media = loadRequestData.media;
|
||||||
|
// Special handling if it came from Google Assistant
|
||||||
|
if (media.entity) {
|
||||||
|
media.contentId = media.entity;
|
||||||
|
media.streamType = cast.framework.messages.StreamType.LIVE;
|
||||||
|
media.contentType = "application/vnd.apple.mpegurl";
|
||||||
|
// @ts-ignore
|
||||||
|
media.hlsVideoSegmentFormat =
|
||||||
|
cast.framework.messages.HlsVideoSegmentFormat.FMP4;
|
||||||
|
}
|
||||||
|
return loadRequestData;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
castContext.start();
|
@ -16,11 +16,9 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
|
||||||
|
|
||||||
@customElement("hassio-upload-backup")
|
@customElement("hassio-upload-backup")
|
||||||
export class HassioUploadBackup extends LitElement {
|
export class HassioUploadBackup extends LitElement {
|
||||||
public hass!: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() public value: string | null = null;
|
@state() public value: string | null = null;
|
||||||
|
|
||||||
@ -43,20 +41,6 @@ export class HassioUploadBackup extends LitElement {
|
|||||||
private async _uploadFile(ev) {
|
private async _uploadFile(ev) {
|
||||||
const file = ev.detail.files[0];
|
const file = ev.detail.files[0];
|
||||||
|
|
||||||
if (file.size > MAX_FILE_SIZE) {
|
|
||||||
showAlertDialog(this, {
|
|
||||||
title: "Backup file is too big",
|
|
||||||
text: html`The maximum allowed filesize is 1GB.<br />
|
|
||||||
<a
|
|
||||||
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-backup-on-a-new-install"
|
|
||||||
target="_blank"
|
|
||||||
>Have a look here on how to restore it.</a
|
|
||||||
>`,
|
|
||||||
confirmText: "ok",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!["application/x-tar"].includes(file.type)) {
|
if (!["application/x-tar"].includes(file.type)) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Unsupported file format",
|
title: "Unsupported file format",
|
||||||
|
@ -15,7 +15,7 @@ export class DialogHassioBackupUpload
|
|||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioBackupUploadDialogParams>
|
implements HassDialog<HassioBackupUploadDialogParams>
|
||||||
{
|
{
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _params?: HassioBackupUploadDialogParams;
|
@state() private _params?: HassioBackupUploadDialogParams;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export class DialogHassioBackupUpload
|
|||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title"> Upload backup </span>
|
<span slot="title"> Upload backup </span>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass.localize("common.close")}
|
.label=${this.hass?.localize("common.close") || "close"}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
slot="actionItems"
|
slot="actionItems"
|
||||||
dialogAction="cancel"
|
dialogAction="cancel"
|
||||||
|
@ -35,7 +35,7 @@ class HassioBackupDialog
|
|||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioBackupDialogParams>
|
implements HassDialog<HassioBackupDialogParams>
|
||||||
{
|
{
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class HassioBackupDialog
|
|||||||
<ha-header-bar>
|
<ha-header-bar>
|
||||||
<span slot="title">${this._backup.name}</span>
|
<span slot="title">${this._backup.name}</span>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass.localize("common.close")}
|
.label=${this.hass?.localize("common.close") || "close"}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
slot="actionItems"
|
slot="actionItems"
|
||||||
dialogAction="cancel"
|
dialogAction="cancel"
|
||||||
@ -114,7 +114,7 @@ class HassioBackupDialog
|
|||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass.localize("common.menu")}
|
.label=${this.hass!.localize("common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
@ -192,25 +192,23 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._dialogParams?.onboarding) {
|
if (!this._dialogParams?.onboarding) {
|
||||||
this.hass
|
this.hass!.callApi(
|
||||||
.callApi(
|
"POST",
|
||||||
"POST",
|
|
||||||
|
|
||||||
`hassio/${
|
`hassio/${
|
||||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
atLeastVersion(this.hass!.config.version, 2021, 9)
|
||||||
? "backups"
|
? "backups"
|
||||||
: "snapshots"
|
: "snapshots"
|
||||||
}/${this._backup!.slug}/restore/partial`,
|
}/${this._backup!.slug}/restore/partial`,
|
||||||
backupDetails
|
backupDetails
|
||||||
)
|
).then(
|
||||||
.then(
|
() => {
|
||||||
() => {
|
this.closeDialog();
|
||||||
this.closeDialog();
|
},
|
||||||
},
|
(error) => {
|
||||||
(error) => {
|
this._error = error.body.message;
|
||||||
this._error = error.body.message;
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||||
@ -244,24 +242,22 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._dialogParams?.onboarding) {
|
if (!this._dialogParams?.onboarding) {
|
||||||
this.hass
|
this.hass!.callApi(
|
||||||
.callApi(
|
"POST",
|
||||||
"POST",
|
`hassio/${
|
||||||
`hassio/${
|
atLeastVersion(this.hass!.config.version, 2021, 9)
|
||||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
? "backups"
|
||||||
? "backups"
|
: "snapshots"
|
||||||
: "snapshots"
|
}/${this._backup!.slug}/restore/full`,
|
||||||
}/${this._backup!.slug}/restore/full`,
|
backupDetails
|
||||||
backupDetails
|
).then(
|
||||||
)
|
() => {
|
||||||
.then(
|
this.closeDialog();
|
||||||
() => {
|
},
|
||||||
this.closeDialog();
|
(error) => {
|
||||||
},
|
this._error = error.body.message;
|
||||||
(error) => {
|
}
|
||||||
this._error = error.body.message;
|
);
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||||
@ -283,36 +279,33 @@ class HassioBackupDialog
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass
|
this.hass!.callApi(
|
||||||
|
atLeastVersion(this.hass!.config.version, 2021, 9) ? "DELETE" : "POST",
|
||||||
.callApi(
|
`hassio/${
|
||||||
atLeastVersion(this.hass.config.version, 2021, 9) ? "DELETE" : "POST",
|
atLeastVersion(this.hass!.config.version, 2021, 9)
|
||||||
`hassio/${
|
? `backups/${this._backup!.slug}`
|
||||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
: `snapshots/${this._backup!.slug}/remove`
|
||||||
? `backups/${this._backup!.slug}`
|
}`
|
||||||
: `snapshots/${this._backup!.slug}/remove`
|
).then(
|
||||||
}`
|
() => {
|
||||||
)
|
if (this._dialogParams!.onDelete) {
|
||||||
.then(
|
this._dialogParams!.onDelete();
|
||||||
() => {
|
|
||||||
if (this._dialogParams!.onDelete) {
|
|
||||||
this._dialogParams!.onDelete();
|
|
||||||
}
|
|
||||||
this.closeDialog();
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
this._error = error.body.message;
|
|
||||||
}
|
}
|
||||||
);
|
this.closeDialog();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this._error = error.body.message;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _downloadClicked() {
|
private async _downloadClicked() {
|
||||||
let signedPath: { path: string };
|
let signedPath: { path: string };
|
||||||
try {
|
try {
|
||||||
signedPath = await getSignedPath(
|
signedPath = await getSignedPath(
|
||||||
this.hass,
|
this.hass!,
|
||||||
`/api/hassio/${
|
`/api/hassio/${
|
||||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
atLeastVersion(this.hass!.config.version, 2021, 9)
|
||||||
? "backups"
|
? "backups"
|
||||||
: "snapshots"
|
: "snapshots"
|
||||||
}/${this._backup!.slug}/download`
|
}/${this._backup!.slug}/download`
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
|
||||||
import { mdiDelete } from "@mdi/js";
|
import { mdiDelete } from "@mdi/js";
|
||||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-circular-progress";
|
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
|
import { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
|
import "../../../../src/components/ha-settings-row";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
addHassioDockerRegistry,
|
addHassioDockerRegistry,
|
||||||
@ -19,22 +19,41 @@ import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { RegistriesDialogParams } from "./show-dialog-registries";
|
import { RegistriesDialogParams } from "./show-dialog-registries";
|
||||||
|
|
||||||
|
const SCHEMA = [
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "registry",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "username",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "string",
|
||||||
|
name: "password",
|
||||||
|
required: true,
|
||||||
|
format: "password",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
@customElement("dialog-hassio-registries")
|
@customElement("dialog-hassio-registries")
|
||||||
class HassioRegistriesDialog extends LitElement {
|
class HassioRegistriesDialog extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) private _registries?: {
|
@state() private _registries?: {
|
||||||
registry: string;
|
registry: string;
|
||||||
username: string;
|
username: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@state() private _registry?: string;
|
@state() private _input: {
|
||||||
|
registry?: string;
|
||||||
@state() private _username?: string;
|
username?: string;
|
||||||
|
password?: string;
|
||||||
@state() private _password?: string;
|
} = {};
|
||||||
|
|
||||||
@state() private _opened = false;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@ -47,6 +66,7 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
scrimClickAction
|
scrimClickAction
|
||||||
escapeKeyAction
|
escapeKeyAction
|
||||||
|
hideActions
|
||||||
.heading=${createCloseHeading(
|
.heading=${createCloseHeading(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._addingRegistry
|
this._addingRegistry
|
||||||
@ -54,99 +74,77 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
: this.supervisor.localize("dialog.registries.title_manage")
|
: this.supervisor.localize("dialog.registries.title_manage")
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="form">
|
${this._addingRegistry
|
||||||
${this._addingRegistry
|
? html`
|
||||||
? html`
|
<ha-form
|
||||||
<paper-input
|
.data=${this._input}
|
||||||
@value-changed=${this._inputChanged}
|
.schema=${SCHEMA}
|
||||||
class="flex-auto"
|
@value-changed=${this._valueChanged}
|
||||||
name="registry"
|
.computeLabel=${this._computeLabel}
|
||||||
.label=${this.supervisor.localize(
|
></ha-form>
|
||||||
"dialog.registries.registry"
|
<div class="action">
|
||||||
)}
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
@value-changed=${this._inputChanged}
|
|
||||||
class="flex-auto"
|
|
||||||
name="username"
|
|
||||||
.label=${this.supervisor.localize(
|
|
||||||
"dialog.registries.username"
|
|
||||||
)}
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
|
||||||
@value-changed=${this._inputChanged}
|
|
||||||
class="flex-auto"
|
|
||||||
name="password"
|
|
||||||
.label=${this.supervisor.localize(
|
|
||||||
"dialog.registries.password"
|
|
||||||
)}
|
|
||||||
type="password"
|
|
||||||
required
|
|
||||||
auto-validate
|
|
||||||
></paper-input>
|
|
||||||
|
|
||||||
<mwc-button
|
<mwc-button
|
||||||
?disabled=${Boolean(
|
?disabled=${Boolean(
|
||||||
!this._registry || !this._username || !this._password
|
!this._input.registry ||
|
||||||
|
!this._input.username ||
|
||||||
|
!this._input.password
|
||||||
)}
|
)}
|
||||||
@click=${this._addNewRegistry}
|
@click=${this._addNewRegistry}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("dialog.registries.add_registry")}
|
${this.supervisor.localize("dialog.registries.add_registry")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
</div>
|
||||||
: html`${this._registries?.length
|
`
|
||||||
? this._registries.map(
|
: html`${this._registries?.length
|
||||||
(entry) => html`
|
? this._registries.map(
|
||||||
<mwc-list-item class="option" hasMeta twoline>
|
(entry) => html`
|
||||||
<span>${entry.registry}</span>
|
<ha-settings-row class="registry">
|
||||||
<span slot="secondary"
|
<span slot="heading"> ${entry.registry} </span>
|
||||||
>${this.supervisor.localize(
|
<span slot="description">
|
||||||
"dialog.registries.username"
|
${this.supervisor.localize(
|
||||||
)}:
|
"dialog.registries.username"
|
||||||
${entry.username}</span
|
)}:
|
||||||
>
|
${entry.username}
|
||||||
<ha-icon-button
|
</span>
|
||||||
.entry=${entry}
|
<ha-icon-button
|
||||||
.label=${this.supervisor.localize(
|
.entry=${entry}
|
||||||
"dialog.registries.remove"
|
.label=${this.supervisor.localize(
|
||||||
)}
|
"dialog.registries.remove"
|
||||||
.path=${mdiDelete}
|
)}
|
||||||
slot="meta"
|
.path=${mdiDelete}
|
||||||
@click=${this._removeRegistry}
|
@click=${this._removeRegistry}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</mwc-list-item>
|
</ha-settings-row>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
: html`
|
: html`
|
||||||
<mwc-list-item>
|
<ha-alert>
|
||||||
<span
|
${this.supervisor.localize(
|
||||||
>${this.supervisor.localize(
|
"dialog.registries.no_registries"
|
||||||
"dialog.registries.no_registries"
|
)}
|
||||||
)}</span
|
</ha-alert>
|
||||||
>
|
`}
|
||||||
</mwc-list-item>
|
<div class="action">
|
||||||
`}
|
|
||||||
<mwc-button @click=${this._addRegistry}>
|
<mwc-button @click=${this._addRegistry}>
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"dialog.registries.add_new_registry"
|
"dialog.registries.add_new_registry"
|
||||||
)}
|
)}
|
||||||
</mwc-button> `}
|
</mwc-button>
|
||||||
</div>
|
</div> `}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _inputChanged(ev: Event) {
|
private _computeLabel = (schema: HaFormSchema) =>
|
||||||
const target = ev.currentTarget as PaperInputElement;
|
this.supervisor.localize(`dialog.registries.${schema.name}`) || schema.name;
|
||||||
this[`_${target.name}`] = target.value;
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
this._input = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog(dialogParams: RegistriesDialogParams): Promise<void> {
|
public async showDialog(dialogParams: RegistriesDialogParams): Promise<void> {
|
||||||
this._opened = true;
|
this._opened = true;
|
||||||
|
this._input = {};
|
||||||
this.supervisor = dialogParams.supervisor;
|
this.supervisor = dialogParams.supervisor;
|
||||||
await this._loadRegistries();
|
await this._loadRegistries();
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
@ -155,6 +153,7 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._addingRegistry = false;
|
this._addingRegistry = false;
|
||||||
this._opened = false;
|
this._opened = false;
|
||||||
|
this._input = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
@ -179,15 +178,16 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
|
|
||||||
private async _addNewRegistry(): Promise<void> {
|
private async _addNewRegistry(): Promise<void> {
|
||||||
const data = {};
|
const data = {};
|
||||||
data[this._registry!] = {
|
data[this._input.registry!] = {
|
||||||
username: this._username,
|
username: this._input.username,
|
||||||
password: this._password,
|
password: this._input.password,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await addHassioDockerRegistry(this.hass, data);
|
await addHassioDockerRegistry(this.hass, data);
|
||||||
await this._loadRegistries();
|
await this._loadRegistries();
|
||||||
this._addingRegistry = false;
|
this._addingRegistry = false;
|
||||||
|
this._input = {};
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
||||||
@ -215,32 +215,20 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-dialog.button-left {
|
.registry {
|
||||||
--justify-action-buttons: flex-start;
|
|
||||||
}
|
|
||||||
paper-icon-item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.form {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
.option {
|
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
mwc-button {
|
.action {
|
||||||
margin-left: 8px;
|
margin-top: 24px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
margin: -10px;
|
margin-right: -10px;
|
||||||
}
|
|
||||||
mwc-list-item {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
mwc-list-item span[slot="secondary"] {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20211103.0",
|
version="20211108.0",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/frontend",
|
url="https://github.com/home-assistant/frontend",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
@ -21,7 +21,11 @@ class HaPickAuthProvider extends litLocalizeLiteMixin(LitElement) {
|
|||||||
<p>${this.localize("ui.panel.page-authorize.pick_auth_provider")}:</p>
|
<p>${this.localize("ui.panel.page-authorize.pick_auth_provider")}:</p>
|
||||||
${this.authProviders.map(
|
${this.authProviders.map(
|
||||||
(provider) => html`
|
(provider) => html`
|
||||||
<paper-item .auth_provider=${provider} @click=${this._handlePick}>
|
<paper-item
|
||||||
|
role="button"
|
||||||
|
.auth_provider=${provider}
|
||||||
|
@click=${this._handlePick}
|
||||||
|
>
|
||||||
<paper-item-body>${provider.name}</paper-item-body>
|
<paper-item-body>${provider.name}</paper-item-body>
|
||||||
<ha-icon-next></ha-icon-next>
|
<ha-icon-next></ha-icon-next>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
|
@ -76,7 +76,6 @@ export const FIXED_DOMAIN_ICONS = {
|
|||||||
configurator: mdiCog,
|
configurator: mdiCog,
|
||||||
conversation: mdiTextToSpeech,
|
conversation: mdiTextToSpeech,
|
||||||
counter: mdiCounter,
|
counter: mdiCounter,
|
||||||
device_tracker: mdiAccount,
|
|
||||||
fan: mdiFan,
|
fan: mdiFan,
|
||||||
google_assistant: mdiGoogleAssistant,
|
google_assistant: mdiGoogleAssistant,
|
||||||
group: mdiGoogleCirclesCommunities,
|
group: mdiGoogleCirclesCommunities,
|
||||||
@ -104,7 +103,6 @@ export const FIXED_DOMAIN_ICONS = {
|
|||||||
siren: mdiBullhorn,
|
siren: mdiBullhorn,
|
||||||
simple_alarm: mdiBell,
|
simple_alarm: mdiBell,
|
||||||
sun: mdiWhiteBalanceSunny,
|
sun: mdiWhiteBalanceSunny,
|
||||||
switch: mdiFlash,
|
|
||||||
timer: mdiTimerOutline,
|
timer: mdiTimerOutline,
|
||||||
updater: mdiCloudUpload,
|
updater: mdiCloudUpload,
|
||||||
vacuum: mdiRobotVacuum,
|
vacuum: mdiRobotVacuum,
|
||||||
@ -145,6 +143,7 @@ export const FIXED_DEVICE_CLASS_ICONS = {
|
|||||||
|
|
||||||
/** Domains that have a state card. */
|
/** Domains that have a state card. */
|
||||||
export const DOMAINS_WITH_CARD = [
|
export const DOMAINS_WITH_CARD = [
|
||||||
|
"button",
|
||||||
"climate",
|
"climate",
|
||||||
"cover",
|
"cover",
|
||||||
"configurator",
|
"configurator",
|
||||||
|
@ -6,6 +6,8 @@ import {
|
|||||||
mdiBrightness5,
|
mdiBrightness5,
|
||||||
mdiBrightness7,
|
mdiBrightness7,
|
||||||
mdiCheckboxMarkedCircle,
|
mdiCheckboxMarkedCircle,
|
||||||
|
mdiCheckNetworkOutline,
|
||||||
|
mdiCloseNetworkOutline,
|
||||||
mdiCheckCircle,
|
mdiCheckCircle,
|
||||||
mdiCropPortrait,
|
mdiCropPortrait,
|
||||||
mdiDoorClosed,
|
mdiDoorClosed,
|
||||||
@ -26,8 +28,6 @@ import {
|
|||||||
mdiPowerPlugOff,
|
mdiPowerPlugOff,
|
||||||
mdiRadioboxBlank,
|
mdiRadioboxBlank,
|
||||||
mdiRun,
|
mdiRun,
|
||||||
mdiServerNetwork,
|
|
||||||
mdiServerNetworkOff,
|
|
||||||
mdiSmoke,
|
mdiSmoke,
|
||||||
mdiSnowflake,
|
mdiSnowflake,
|
||||||
mdiSquare,
|
mdiSquare,
|
||||||
@ -55,7 +55,7 @@ export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
|||||||
case "cold":
|
case "cold":
|
||||||
return is_off ? mdiThermometer : mdiSnowflake;
|
return is_off ? mdiThermometer : mdiSnowflake;
|
||||||
case "connectivity":
|
case "connectivity":
|
||||||
return is_off ? mdiServerNetworkOff : mdiServerNetwork;
|
return is_off ? mdiCloseNetworkOutline : mdiCheckNetworkOutline;
|
||||||
case "door":
|
case "door":
|
||||||
return is_off ? mdiDoorClosed : mdiDoorOpen;
|
return is_off ? mdiDoorClosed : mdiDoorOpen;
|
||||||
case "garage_door":
|
case "garage_door":
|
||||||
|
@ -116,6 +116,14 @@ export const computeStateDisplay = (
|
|||||||
return formatNumber(compareState, locale);
|
return formatNumber(compareState, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// state of button is a timestamp
|
||||||
|
if (
|
||||||
|
domain === "button" ||
|
||||||
|
(domain === "sensor" && stateObj.attributes.device_class === "timestamp")
|
||||||
|
) {
|
||||||
|
return formatDateTime(new Date(compareState), locale);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Return device class translation
|
// Return device class translation
|
||||||
(stateObj.attributes.device_class &&
|
(stateObj.attributes.device_class &&
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
|
mdiAccount,
|
||||||
|
mdiAccountArrowRight,
|
||||||
mdiAirHumidifierOff,
|
mdiAirHumidifierOff,
|
||||||
mdiAirHumidifier,
|
mdiAirHumidifier,
|
||||||
|
mdiFlash,
|
||||||
|
mdiBluetooth,
|
||||||
|
mdiBluetoothConnect,
|
||||||
|
mdiLanConnect,
|
||||||
|
mdiLanDisconnect,
|
||||||
mdiLockOpen,
|
mdiLockOpen,
|
||||||
mdiLockAlert,
|
mdiLockAlert,
|
||||||
mdiLockClock,
|
mdiLockClock,
|
||||||
@ -8,8 +15,12 @@ import {
|
|||||||
mdiCastConnected,
|
mdiCastConnected,
|
||||||
mdiCast,
|
mdiCast,
|
||||||
mdiEmoticonDead,
|
mdiEmoticonDead,
|
||||||
|
mdiPowerPlug,
|
||||||
|
mdiPowerPlugOff,
|
||||||
mdiSleep,
|
mdiSleep,
|
||||||
mdiTimerSand,
|
mdiTimerSand,
|
||||||
|
mdiToggleSwitch,
|
||||||
|
mdiToggleSwitchOff,
|
||||||
mdiZWave,
|
mdiZWave,
|
||||||
mdiClock,
|
mdiClock,
|
||||||
mdiCalendar,
|
mdiCalendar,
|
||||||
@ -44,6 +55,17 @@ export const domainIcon = (
|
|||||||
case "cover":
|
case "cover":
|
||||||
return coverIcon(compareState, stateObj);
|
return coverIcon(compareState, stateObj);
|
||||||
|
|
||||||
|
case "device_tracker":
|
||||||
|
if (stateObj?.attributes.source_type === "router") {
|
||||||
|
return compareState === "home" ? mdiLanConnect : mdiLanDisconnect;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
["bluetooth", "bluetooth_le"].includes(stateObj?.attributes.source_type)
|
||||||
|
) {
|
||||||
|
return compareState === "home" ? mdiBluetoothConnect : mdiBluetooth;
|
||||||
|
}
|
||||||
|
return compareState === "not_home" ? mdiAccountArrowRight : mdiAccount;
|
||||||
|
|
||||||
case "humidifier":
|
case "humidifier":
|
||||||
return state && state === "off" ? mdiAirHumidifierOff : mdiAirHumidifier;
|
return state && state === "off" ? mdiAirHumidifierOff : mdiAirHumidifier;
|
||||||
|
|
||||||
@ -63,6 +85,16 @@ export const domainIcon = (
|
|||||||
case "media_player":
|
case "media_player":
|
||||||
return compareState === "playing" ? mdiCastConnected : mdiCast;
|
return compareState === "playing" ? mdiCastConnected : mdiCast;
|
||||||
|
|
||||||
|
case "switch":
|
||||||
|
switch (stateObj?.attributes.device_class) {
|
||||||
|
case "outlet":
|
||||||
|
return state === "on" ? mdiPowerPlug : mdiPowerPlugOff;
|
||||||
|
case "switch":
|
||||||
|
return state === "on" ? mdiToggleSwitch : mdiToggleSwitchOff;
|
||||||
|
default:
|
||||||
|
return mdiFlash;
|
||||||
|
}
|
||||||
|
|
||||||
case "zwave":
|
case "zwave":
|
||||||
switch (compareState) {
|
switch (compareState) {
|
||||||
case "dead":
|
case "dead":
|
||||||
|
@ -32,6 +32,7 @@ if (__BUILD__ === "latest") {
|
|||||||
}
|
}
|
||||||
if (shouldPolyfillDateTime()) {
|
if (shouldPolyfillDateTime()) {
|
||||||
polyfills.push(import("@formatjs/intl-datetimeformat/polyfill"));
|
polyfills.push(import("@formatjs/intl-datetimeformat/polyfill"));
|
||||||
|
polyfills.push(import("@formatjs/intl-datetimeformat/add-all-tz"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ declare global {
|
|||||||
|
|
||||||
@customElement("ha-file-upload")
|
@customElement("ha-file-upload")
|
||||||
export class HaFileUpload extends LitElement {
|
export class HaFileUpload extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() public accept!: string;
|
@property() public accept!: string;
|
||||||
|
|
||||||
@ -88,7 +88,8 @@ export class HaFileUpload extends LitElement {
|
|||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="suffix"
|
slot="suffix"
|
||||||
@click=${this._clearValue}
|
@click=${this._clearValue}
|
||||||
.label=${this.hass.localize("ui.common.close")}
|
.label=${this.hass?.localize("ui.common.close") ||
|
||||||
|
"close"}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
|
@ -47,12 +47,19 @@ export class HaFormFloat extends LitElement implements HaFormElement {
|
|||||||
|
|
||||||
private _valueChanged(ev: Event) {
|
private _valueChanged(ev: Event) {
|
||||||
const source = ev.target as TextField;
|
const source = ev.target as TextField;
|
||||||
const rawValue = source.value;
|
const rawValue = source.value.replace(",", ".");
|
||||||
|
|
||||||
let value: number | undefined;
|
let value: number | undefined;
|
||||||
|
|
||||||
|
if (rawValue.endsWith(".")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (rawValue !== "") {
|
if (rawValue !== "") {
|
||||||
value = parseFloat(rawValue);
|
value = parseFloat(rawValue);
|
||||||
|
if (isNaN(value)) {
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect anything changed
|
// Detect anything changed
|
||||||
@ -61,7 +68,6 @@ export class HaFormFloat extends LitElement implements HaFormElement {
|
|||||||
const newRawValue = value === undefined ? "" : String(value);
|
const newRawValue = value === undefined ? "" : String(value);
|
||||||
if (source.value !== newRawValue) {
|
if (source.value !== newRawValue) {
|
||||||
source.value = newRawValue;
|
source.value = newRawValue;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,8 @@ export class HaIconPicker extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public invalid = false;
|
||||||
|
|
||||||
@state() private _opened = false;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@query("vaadin-combo-box-light", true) private comboBox!: HTMLElement;
|
@query("vaadin-combo-box-light", true) private comboBox!: HTMLElement;
|
||||||
@ -86,6 +88,8 @@ export class HaIconPicker extends LitElement {
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
|
.errorMessage=${this.errorMessage}
|
||||||
|
.invalid=${this.invalid}
|
||||||
>
|
>
|
||||||
${this._value || this.placeholder
|
${this._value || this.placeholder
|
||||||
? html`
|
? html`
|
||||||
|
@ -502,6 +502,9 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _entityRegMeetsFilter(entity: EntityRegistryEntry): boolean {
|
private _entityRegMeetsFilter(entity: EntityRegistryEntry): boolean {
|
||||||
|
if (entity.entity_category) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.includeDomains &&
|
this.includeDomains &&
|
||||||
!this.includeDomains.includes(computeDomain(entity.entity_id))
|
!this.includeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
@ -79,7 +79,7 @@ export const fetchHassioBackups = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const fetchHassioBackupInfo = async (
|
export const fetchHassioBackupInfo = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant | undefined,
|
||||||
backup: string
|
backup: string
|
||||||
): Promise<HassioBackupDetail> => {
|
): Promise<HassioBackupDetail> => {
|
||||||
if (hass) {
|
if (hass) {
|
||||||
@ -202,7 +202,7 @@ export const createHassioPartialBackup = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const uploadBackup = async (
|
export const uploadBackup = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant | undefined,
|
||||||
file: File
|
file: File
|
||||||
): Promise<HassioResponse<HassioBackup>> => {
|
): Promise<HassioResponse<HassioBackup>> => {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
|
@ -12,7 +12,7 @@ export interface Zone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ZoneMutableParams {
|
export interface ZoneMutableParams {
|
||||||
icon: string;
|
icon?: string;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -45,7 +45,7 @@ class StepFlowPickFlow extends LitElement {
|
|||||||
domain: flow.handler,
|
domain: flow.handler,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
useFallback: true,
|
useFallback: true,
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
/>
|
/>
|
||||||
|
@ -102,7 +102,7 @@ class StepFlowPickHandler extends LitElement {
|
|||||||
domain: handler.slug,
|
domain: handler.slug,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
useFallback: true,
|
useFallback: true,
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
/>
|
/>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<img src="/static/icons/favicon-192x192.png" height="52" />
|
<img src="/static/icons/favicon-192x192.png" height="52" alt="" />
|
||||||
Home Assistant
|
Home Assistant
|
||||||
</div>
|
</div>
|
||||||
<ha-authorize><p>Initializing</p></ha-authorize>
|
<ha-authorize><p>Initializing</p></ha-authorize>
|
||||||
@ -70,4 +70,4 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
<body id="particles">
|
<body id="particles">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<img src="/static/icons/favicon-192x192.png" height="52" width="52" />
|
<img src="/static/icons/favicon-192x192.png" height="52" width="52" alt="" />
|
||||||
Home Assistant
|
Home Assistant
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ class OnboardingIntegrations extends LitElement {
|
|||||||
.domain=${entry.domain}
|
.domain=${entry.domain}
|
||||||
.title=${title}
|
.title=${title}
|
||||||
.badgeIcon=${mdiCheck}
|
.badgeIcon=${mdiCheck}
|
||||||
.darkOptimizedIcon=${this.hass.selectedTheme?.dark}
|
.darkOptimizedIcon=${this.hass.themes?.darkMode}
|
||||||
></integration-badge>
|
></integration-badge>
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
@ -98,7 +98,7 @@ class OnboardingIntegrations extends LitElement {
|
|||||||
clickable
|
clickable
|
||||||
.domain=${flow.handler}
|
.domain=${flow.handler}
|
||||||
.title=${title}
|
.title=${title}
|
||||||
.darkOptimizedIcon=${this.hass.selectedTheme?.dark}
|
.darkOptimizedIcon=${this.hass.themes?.darkMode}
|
||||||
></integration-badge>
|
></integration-badge>
|
||||||
</button>
|
</button>
|
||||||
`,
|
`,
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
import { mdiPencil, mdiPlusCircle, mdiOpenInNew } from "@mdi/js";
|
import { mdiOpenInNew, mdiPencil, mdiPlusCircle } from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { stringCompare } from "../../../common/string/compare";
|
import { stringCompare } from "../../../common/string/compare";
|
||||||
import { groupBy } from "../../../common/util/group-by";
|
|
||||||
import { slugify } from "../../../common/string/slugify";
|
import { slugify } from "../../../common/string/slugify";
|
||||||
|
import { groupBy } from "../../../common/util/group-by";
|
||||||
import "../../../components/entity/ha-battery-icon";
|
import "../../../components/entity/ha-battery-icon";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import "../../../components/ha-alert";
|
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { AreaRegistryEntry } from "../../../data/area_registry";
|
import { AreaRegistryEntry } from "../../../data/area_registry";
|
||||||
import {
|
import {
|
||||||
@ -52,7 +53,6 @@ import {
|
|||||||
loadDeviceRegistryDetailDialog,
|
loadDeviceRegistryDetailDialog,
|
||||||
showDeviceRegistryDetailDialog,
|
showDeviceRegistryDetailDialog,
|
||||||
} from "./device-registry-detail/show-dialog-device-registry-detail";
|
} from "./device-registry-detail/show-dialog-device-registry-detail";
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
|
||||||
|
|
||||||
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
||||||
stateName?: string | null;
|
stateName?: string | null;
|
||||||
@ -293,7 +293,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: integrations[0],
|
domain: integrations[0],
|
||||||
type: "logo",
|
type: "logo",
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
@load=${this._onImageLoad}
|
@load=${this._onImageLoad}
|
||||||
@ -407,41 +407,45 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</h1>
|
</h1>
|
||||||
${this._related?.automation?.length
|
${this._related?.automation?.length
|
||||||
? this._related.automation.map((automation) => {
|
? html`
|
||||||
const entityState = this.hass.states[automation];
|
<div class="items">
|
||||||
return entityState
|
${this._related.automation.map((automation) => {
|
||||||
? html`
|
const entityState =
|
||||||
<div>
|
this.hass.states[automation];
|
||||||
<a
|
return entityState
|
||||||
href=${ifDefined(
|
? html`<div>
|
||||||
entityState.attributes.id
|
<a
|
||||||
? `/config/automation/edit/${entityState.attributes.id}`
|
href=${ifDefined(
|
||||||
: undefined
|
entityState.attributes.id
|
||||||
)}
|
? `/config/automation/edit/${entityState.attributes.id}`
|
||||||
>
|
: undefined
|
||||||
<paper-item
|
)}
|
||||||
.automation=${entityState}
|
|
||||||
.disabled=${!entityState.attributes.id}
|
|
||||||
>
|
>
|
||||||
<paper-item-body>
|
<paper-item
|
||||||
${computeStateName(entityState)}
|
.automation=${entityState}
|
||||||
</paper-item-body>
|
.disabled=${!entityState.attributes
|
||||||
<ha-icon-next></ha-icon-next>
|
.id}
|
||||||
</paper-item>
|
>
|
||||||
</a>
|
<paper-item-body>
|
||||||
${!entityState.attributes.id
|
${computeStateName(entityState)}
|
||||||
? html`
|
</paper-item-body>
|
||||||
<paper-tooltip animation-delay="0">
|
<ha-icon-next></ha-icon-next>
|
||||||
${this.hass.localize(
|
</paper-item>
|
||||||
"ui.panel.config.devices.cant_edit"
|
</a>
|
||||||
)}
|
${!entityState.attributes.id
|
||||||
</paper-tooltip>
|
? html`
|
||||||
`
|
<paper-tooltip animation-delay="0">
|
||||||
: ""}
|
${this.hass.localize(
|
||||||
</div>
|
"ui.panel.config.devices.cant_edit"
|
||||||
`
|
)}
|
||||||
: "";
|
</paper-tooltip>
|
||||||
})
|
`
|
||||||
|
: ""}
|
||||||
|
</div> `
|
||||||
|
: "";
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -479,43 +483,49 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.path=${mdiPlusCircle}
|
.path=${mdiPlusCircle}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
${this._related?.scene?.length
|
${this._related?.scene?.length
|
||||||
? this._related.scene.map((scene) => {
|
? html`
|
||||||
const entityState = this.hass.states[scene];
|
<div class="items">
|
||||||
return entityState
|
${this._related.scene.map((scene) => {
|
||||||
? html`
|
const entityState = this.hass.states[scene];
|
||||||
<div>
|
return entityState
|
||||||
<a
|
? html`
|
||||||
href=${ifDefined(
|
<div>
|
||||||
entityState.attributes.id
|
<a
|
||||||
? `/config/scene/edit/${entityState.attributes.id}`
|
href=${ifDefined(
|
||||||
: undefined
|
entityState.attributes.id
|
||||||
)}
|
? `/config/scene/edit/${entityState.attributes.id}`
|
||||||
>
|
: undefined
|
||||||
<paper-item
|
)}
|
||||||
.scene=${entityState}
|
>
|
||||||
.disabled=${!entityState.attributes.id}
|
<paper-item
|
||||||
>
|
.scene=${entityState}
|
||||||
<paper-item-body>
|
.disabled=${!entityState.attributes
|
||||||
${computeStateName(entityState)}
|
.id}
|
||||||
</paper-item-body>
|
>
|
||||||
<ha-icon-next></ha-icon-next>
|
<paper-item-body>
|
||||||
</paper-item>
|
${computeStateName(entityState)}
|
||||||
</a>
|
</paper-item-body>
|
||||||
${!entityState.attributes.id
|
<ha-icon-next></ha-icon-next>
|
||||||
? html`
|
</paper-item>
|
||||||
<paper-tooltip animation-delay="0">
|
</a>
|
||||||
${this.hass.localize(
|
${!entityState.attributes.id
|
||||||
"ui.panel.config.devices.cant_edit"
|
? html`
|
||||||
)}
|
<paper-tooltip
|
||||||
</paper-tooltip>
|
animation-delay="0"
|
||||||
`
|
>
|
||||||
: ""}
|
${this.hass.localize(
|
||||||
</div>
|
"ui.panel.config.devices.cant_edit"
|
||||||
`
|
)}
|
||||||
: "";
|
</paper-tooltip>
|
||||||
})
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: "";
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -553,23 +563,27 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</h1>
|
</h1>
|
||||||
${this._related?.script?.length
|
${this._related?.script?.length
|
||||||
? this._related.script.map((script) => {
|
? html`
|
||||||
const entityState = this.hass.states[script];
|
<div class="items">
|
||||||
return entityState
|
${this._related.script.map((script) => {
|
||||||
? html`
|
const entityState = this.hass.states[script];
|
||||||
<a
|
return entityState
|
||||||
href=${`/config/script/edit/${entityState.entity_id}`}
|
? html`
|
||||||
>
|
<a
|
||||||
<paper-item .script=${script}>
|
href=${`/config/script/edit/${entityState.entity_id}`}
|
||||||
<paper-item-body>
|
>
|
||||||
${computeStateName(entityState)}
|
<paper-item .script=${script}>
|
||||||
</paper-item-body>
|
<paper-item-body>
|
||||||
<ha-icon-next></ha-icon-next>
|
${computeStateName(entityState)}
|
||||||
</paper-item>
|
</paper-item-body>
|
||||||
</a>
|
<ha-icon-next></ha-icon-next>
|
||||||
`
|
</paper-item>
|
||||||
: "";
|
</a>
|
||||||
})
|
`
|
||||||
|
: "";
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -869,6 +883,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header ha-icon-button {
|
.card-header ha-icon-button {
|
||||||
@ -978,6 +993,10 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
ha-svg-icon[slot="trailingIcon"] {
|
ha-svg-icon[slot="trailingIcon"] {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ export class EnergyGridSettings extends LitElement {
|
|||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: "co2signal",
|
domain: "co2signal",
|
||||||
type: "icon",
|
type: "icon",
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<span class="content">${entry.title}</span>
|
<span class="content">${entry.title}</span>
|
||||||
@ -223,7 +223,7 @@ export class EnergyGridSettings extends LitElement {
|
|||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: "co2signal",
|
domain: "co2signal",
|
||||||
type: "icon",
|
type: "icon",
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<mwc-button @click=${this._addCO2Sensor}>
|
<mwc-button @click=${this._addCO2Sensor}>
|
||||||
|
@ -136,7 +136,7 @@ export class DialogEnergySolarSettings
|
|||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: entry.domain,
|
domain: entry.domain,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
/>${entry.title}
|
/>${entry.title}
|
||||||
</div>`}
|
</div>`}
|
||||||
|
@ -98,7 +98,7 @@ class IntegrationsCard extends LitElement {
|
|||||||
domain: domain,
|
domain: domain,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
useFallback: true,
|
useFallback: true,
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
/>
|
/>
|
||||||
|
@ -84,7 +84,7 @@ export class HaIntegrationHeader extends LitElement {
|
|||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: this.domain,
|
domain: this.domain,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
darkOptimized: this.hass.selectedTheme?.dark,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
@error=${this._onImageError}
|
@error=${this._onImageError}
|
||||||
|
@ -8,6 +8,7 @@ import { addDistanceToCoord } from "../../../common/location/add_distance_to_coo
|
|||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||||
import "../../../components/ha-formfield";
|
import "../../../components/ha-formfield";
|
||||||
|
import "../../../components/ha-icon-picker";
|
||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
import "../../../components/map/ha-locations-editor";
|
import "../../../components/map/ha-locations-editor";
|
||||||
import type { MarkerLocation } from "../../../components/map/ha-locations-editor";
|
import type { MarkerLocation } from "../../../components/map/ha-locations-editor";
|
||||||
@ -77,14 +78,18 @@ class DialogZoneDetail extends LitElement {
|
|||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const nameValid = this._name.trim() === "";
|
const nameInvalid = this._name.trim() === "";
|
||||||
const iconValid = !this._icon.trim().includes(":");
|
const iconInvalid = Boolean(this._icon && !this._icon.trim().includes(":"));
|
||||||
const latValid = String(this._latitude) === "";
|
const latInvalid = String(this._latitude) === "";
|
||||||
const lngValid = String(this._longitude) === "";
|
const lngInvalid = String(this._longitude) === "";
|
||||||
const radiusValid = String(this._radius) === "";
|
const radiusInvalid = String(this._radius) === "";
|
||||||
|
|
||||||
const valid =
|
const valid =
|
||||||
!nameValid && !iconValid && !latValid && !lngValid && !radiusValid;
|
!nameInvalid &&
|
||||||
|
!iconInvalid &&
|
||||||
|
!latInvalid &&
|
||||||
|
!lngInvalid &&
|
||||||
|
!radiusInvalid;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
@ -114,7 +119,7 @@ class DialogZoneDetail extends LitElement {
|
|||||||
required
|
required
|
||||||
auto-validate
|
auto-validate
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<ha-icon-picker
|
||||||
.value=${this._icon}
|
.value=${this._icon}
|
||||||
.configValue=${"icon"}
|
.configValue=${"icon"}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@ -122,8 +127,8 @@ class DialogZoneDetail extends LitElement {
|
|||||||
.errorMessage=${this.hass!.localize(
|
.errorMessage=${this.hass!.localize(
|
||||||
"ui.panel.config.zone.detail.icon_error_msg"
|
"ui.panel.config.zone.detail.icon_error_msg"
|
||||||
)}
|
)}
|
||||||
.invalid=${iconValid}
|
.invalid=${iconInvalid}
|
||||||
></paper-input>
|
></ha-icon-picker>
|
||||||
<ha-locations-editor
|
<ha-locations-editor
|
||||||
class="flex"
|
class="flex"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -148,7 +153,7 @@ class DialogZoneDetail extends LitElement {
|
|||||||
.errorMessage=${this.hass!.localize(
|
.errorMessage=${this.hass!.localize(
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
"ui.panel.config.zone.detail.required_error_msg"
|
||||||
)}
|
)}
|
||||||
.invalid=${latValid}
|
.invalid=${latInvalid}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
.value=${this._longitude}
|
.value=${this._longitude}
|
||||||
@ -160,7 +165,7 @@ class DialogZoneDetail extends LitElement {
|
|||||||
.errorMessage=${this.hass!.localize(
|
.errorMessage=${this.hass!.localize(
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
"ui.panel.config.zone.detail.required_error_msg"
|
||||||
)}
|
)}
|
||||||
.invalid=${lngValid}
|
.invalid=${lngInvalid}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>
|
</div>
|
||||||
<paper-input
|
<paper-input
|
||||||
@ -173,7 +178,7 @@ class DialogZoneDetail extends LitElement {
|
|||||||
.errorMessage=${this.hass!.localize(
|
.errorMessage=${this.hass!.localize(
|
||||||
"ui.panel.config.zone.detail.required_error_msg"
|
"ui.panel.config.zone.detail.required_error_msg"
|
||||||
)}
|
)}
|
||||||
.invalid=${radiusValid}
|
.invalid=${radiusInvalid}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<p>
|
<p>
|
||||||
${this.hass!.localize("ui.panel.config.zone.detail.passive_note")}
|
${this.hass!.localize("ui.panel.config.zone.detail.passive_note")}
|
||||||
@ -268,12 +273,14 @@ class DialogZoneDetail extends LitElement {
|
|||||||
try {
|
try {
|
||||||
const values: ZoneMutableParams = {
|
const values: ZoneMutableParams = {
|
||||||
name: this._name.trim(),
|
name: this._name.trim(),
|
||||||
icon: this._icon.trim(),
|
|
||||||
latitude: this._latitude,
|
latitude: this._latitude,
|
||||||
longitude: this._longitude,
|
longitude: this._longitude,
|
||||||
passive: this._passive,
|
passive: this._passive,
|
||||||
radius: this._radius,
|
radius: this._radius,
|
||||||
};
|
};
|
||||||
|
if (this._icon) {
|
||||||
|
values.icon = this._icon.trim();
|
||||||
|
}
|
||||||
if (this._params!.entry) {
|
if (this._params!.entry) {
|
||||||
await this._params!.updateEntry!(values);
|
await this._params!.updateEntry!(values);
|
||||||
} else {
|
} else {
|
||||||
|
@ -231,7 +231,6 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.card-header {
|
.card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -261,7 +260,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#states > div > * {
|
#states > div > * {
|
||||||
overflow: hidden;
|
overflow: clip visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
#states > div {
|
#states > div {
|
||||||
|
@ -241,7 +241,7 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
|
|||||||
);
|
);
|
||||||
const lastDate = this._lastLogbookDate || hoursToShowDate;
|
const lastDate = this._lastLogbookDate || hoursToShowDate;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let newEntries;
|
let newEntries: LogbookEntry[];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
[newEntries] = await Promise.all([
|
[newEntries] = await Promise.all([
|
||||||
@ -256,6 +256,7 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
|
|||||||
]);
|
]);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err.message;
|
this._error = err.message;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const logbookEntries = this._logbookEntries
|
const logbookEntries = this._logbookEntries
|
||||||
|
@ -24,6 +24,7 @@ const ALWAYS_LOADED_TYPES = new Set([
|
|||||||
"call-service",
|
"call-service",
|
||||||
]);
|
]);
|
||||||
const LAZY_LOAD_TYPES = {
|
const LAZY_LOAD_TYPES = {
|
||||||
|
"button-entity": () => import("../entity-rows/hui-button-entity-row"),
|
||||||
"climate-entity": () => import("../entity-rows/hui-climate-entity-row"),
|
"climate-entity": () => import("../entity-rows/hui-climate-entity-row"),
|
||||||
"cover-entity": () => import("../entity-rows/hui-cover-entity-row"),
|
"cover-entity": () => import("../entity-rows/hui-cover-entity-row"),
|
||||||
"group-entity": () => import("../entity-rows/hui-group-entity-row"),
|
"group-entity": () => import("../entity-rows/hui-group-entity-row"),
|
||||||
@ -53,6 +54,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
|
|||||||
_domain_not_found: "text",
|
_domain_not_found: "text",
|
||||||
alert: "toggle",
|
alert: "toggle",
|
||||||
automation: "toggle",
|
automation: "toggle",
|
||||||
|
button: "button",
|
||||||
climate: "climate",
|
climate: "climate",
|
||||||
cover: "cover",
|
cover: "cover",
|
||||||
fan: "toggle",
|
fan: "toggle",
|
||||||
|
82
src/panels/lovelace/entity-rows/hui-button-entity-row.ts
Normal file
82
src/panels/lovelace/entity-rows/hui-button-entity-row.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
|
import "../components/hui-generic-entity-row";
|
||||||
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
|
import { ActionRowConfig, LovelaceRow } from "./types";
|
||||||
|
|
||||||
|
@customElement("hui-button-entity-row")
|
||||||
|
class HuiButtonEntityRow extends LitElement implements LovelaceRow {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: ActionRowConfig;
|
||||||
|
|
||||||
|
public setConfig(config: ActionRowConfig): void {
|
||||||
|
if (!config) {
|
||||||
|
throw new Error("Invalid configuration");
|
||||||
|
}
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
return hasConfigOrEntityChanged(this, changedProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateObj = this.hass.states[this._config.entity];
|
||||||
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return html`
|
||||||
|
<hui-warning>
|
||||||
|
${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||||
|
</hui-warning>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||||
|
<mwc-button
|
||||||
|
@click=${this._pressButton}
|
||||||
|
.disabled=${stateObj.state === UNAVAILABLE}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.card.button.press")}
|
||||||
|
</mwc-button>
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
mwc-button:last-child {
|
||||||
|
margin-right: -0.57em;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _pressButton(ev): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.hass.callService("button", "press", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-button-entity-row": HuiButtonEntityRow;
|
||||||
|
}
|
||||||
|
}
|
54
src/state-summary/state-card-button.ts
Normal file
54
src/state-summary/state-card-button.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { CSSResultGroup, html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/ha-entity-toggle";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { UNAVAILABLE } from "../data/entity";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
@customElement("state-card-button")
|
||||||
|
export class StateCardButton extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const stateObj = this.stateObj;
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
<mwc-button
|
||||||
|
@click=${this._pressButton}
|
||||||
|
.disabled=${stateObj.state === UNAVAILABLE}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.card.button.press")}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _pressButton(ev: Event) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.hass.callService("button", "press", {
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return haStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-button": StateCardButton;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import dynamicContentUpdater from "../common/dom/dynamic_content_updater";
|
import dynamicContentUpdater from "../common/dom/dynamic_content_updater";
|
||||||
import { stateCardType } from "../common/entity/state_card_type";
|
import { stateCardType } from "../common/entity/state_card_type";
|
||||||
|
import "./state-card-button";
|
||||||
import "./state-card-climate";
|
import "./state-card-climate";
|
||||||
import "./state-card-configurator";
|
import "./state-card-configurator";
|
||||||
import "./state-card-cover";
|
import "./state-card-cover";
|
||||||
|
@ -24,6 +24,7 @@ import { getState } from "../util/ha-pref-storage";
|
|||||||
import hassCallApi from "../util/hass-call-api";
|
import hassCallApi from "../util/hass-call-api";
|
||||||
import { getLocalLanguage } from "../util/common-translation";
|
import { getLocalLanguage } from "../util/common-translation";
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
import { HassBaseEl } from "./hass-base-mixin";
|
||||||
|
import { polyfillsLoaded } from "../common/translations/localize";
|
||||||
|
|
||||||
export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
||||||
superClass: T
|
superClass: T
|
||||||
@ -180,12 +181,18 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
|
|
||||||
subscribeEntities(conn, (states) => this._updateHass({ states }));
|
subscribeEntities(conn, (states) => this._updateHass({ states }));
|
||||||
subscribeConfig(conn, (config) => {
|
subscribeConfig(conn, (config) => {
|
||||||
if (
|
if (this.hass?.config?.time_zone !== config.time_zone) {
|
||||||
this.hass?.config?.time_zone !== config.time_zone &&
|
if (__BUILD__ === "latest" && polyfillsLoaded) {
|
||||||
"__setDefaultTimeZone" in Intl.DateTimeFormat
|
polyfillsLoaded.then(() => {
|
||||||
) {
|
if ("__setDefaultTimeZone" in Intl.DateTimeFormat) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Intl.DateTimeFormat.__setDefaultTimeZone(config.time_zone);
|
Intl.DateTimeFormat.__setDefaultTimeZone(config.time_zone);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ("__setDefaultTimeZone" in Intl.DateTimeFormat) {
|
||||||
|
// @ts-ignore
|
||||||
|
Intl.DateTimeFormat.__setDefaultTimeZone(config.time_zone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._updateHass({ config });
|
this._updateHass({ config });
|
||||||
});
|
});
|
||||||
|
@ -120,6 +120,9 @@
|
|||||||
"last_triggered": "Last triggered",
|
"last_triggered": "Last triggered",
|
||||||
"trigger": "Run Actions"
|
"trigger": "Run Actions"
|
||||||
},
|
},
|
||||||
|
"button": {
|
||||||
|
"press": "Press"
|
||||||
|
},
|
||||||
"camera": {
|
"camera": {
|
||||||
"not_available": "Image not available"
|
"not_available": "Image not available"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user