mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-18 13:49:26 +00:00
Compare commits
1 Commits
Move-parti
...
dev-tools-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
94215dc50b |
@@ -206,7 +206,6 @@ const createDeviceRegistryEntries = (
|
|||||||
model: "Mock Device",
|
model: "Mock Device",
|
||||||
name: "Tag Reader",
|
name: "Tag Reader",
|
||||||
sw_version: null,
|
sw_version: null,
|
||||||
hw_version: "1.0.0",
|
|
||||||
id: "mock-device-id",
|
id: "mock-device-id",
|
||||||
identifiers: [],
|
identifiers: [],
|
||||||
via_device_id: null,
|
via_device_id: null,
|
||||||
|
@@ -29,6 +29,10 @@ import {
|
|||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
updateHassioAddon,
|
updateHassioAddon,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
|
import {
|
||||||
|
createHassioPartialBackup,
|
||||||
|
HassioPartialBackupCreateParams,
|
||||||
|
} from "../../../src/data/hassio/backup";
|
||||||
import {
|
import {
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
ignoreSupervisorError,
|
ignoreSupervisorError,
|
||||||
@@ -44,6 +48,7 @@ import "../../../src/layouts/hass-subpage";
|
|||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
import { SUPERVISOR_UPDATE_NAMES } from "../../../src/panels/config/dashboard/ha-config-updates";
|
import { SUPERVISOR_UPDATE_NAMES } from "../../../src/panels/config/dashboard/ha-config-updates";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
|
import { documentationUrl } from "../../../src/util/documentation-url";
|
||||||
import { addonArchIsSupported, extractChangelog } from "../util/addon";
|
import { addonArchIsSupported, extractChangelog } from "../util/addon";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -55,6 +60,7 @@ declare global {
|
|||||||
type updateType = "os" | "supervisor" | "core" | "addon";
|
type updateType = "os" | "supervisor" | "core" | "addon";
|
||||||
|
|
||||||
const changelogUrl = (
|
const changelogUrl = (
|
||||||
|
hass: HomeAssistant,
|
||||||
entry: updateType,
|
entry: updateType,
|
||||||
version: string
|
version: string
|
||||||
): string | undefined => {
|
): string | undefined => {
|
||||||
@@ -62,19 +68,17 @@ const changelogUrl = (
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (entry === "core") {
|
if (entry === "core") {
|
||||||
return version.includes("dev")
|
return version?.includes("dev")
|
||||||
? "https://github.com/home-assistant/core/commits/dev"
|
? "https://github.com/home-assistant/core/commits/dev"
|
||||||
: version.includes("b")
|
: documentationUrl(hass, "/latest-release-notes/");
|
||||||
? "https://next.home-assistant.io/latest-release-notes/"
|
|
||||||
: "https://www.home-assistant.io/latest-release-notes/";
|
|
||||||
}
|
}
|
||||||
if (entry === "os") {
|
if (entry === "os") {
|
||||||
return version.includes("dev")
|
return version?.includes("dev")
|
||||||
? "https://github.com/home-assistant/operating-system/commits/dev"
|
? "https://github.com/home-assistant/operating-system/commits/dev"
|
||||||
: `https://github.com/home-assistant/operating-system/releases/tag/${version}`;
|
: `https://github.com/home-assistant/operating-system/releases/tag/${version}`;
|
||||||
}
|
}
|
||||||
if (entry === "supervisor") {
|
if (entry === "supervisor") {
|
||||||
return version.includes("dev")
|
return version?.includes("dev")
|
||||||
? "https://github.com/home-assistant/supervisor/commits/main"
|
? "https://github.com/home-assistant/supervisor/commits/main"
|
||||||
: `https://github.com/home-assistant/supervisor/releases/tag/${version}`;
|
: `https://github.com/home-assistant/supervisor/releases/tag/${version}`;
|
||||||
}
|
}
|
||||||
@@ -99,7 +103,7 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
|
|
||||||
@state() private _addonInfo?: HassioAddonDetails;
|
@state() private _addonInfo?: HassioAddonDetails;
|
||||||
|
|
||||||
@state() private _updating = false;
|
@state() private _action: "backup" | "update" | null = null;
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@@ -116,7 +120,7 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changelog = changelogUrl(this._updateType, this._version_latest);
|
const changelog = changelogUrl(this.hass, this._updateType, this._version);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
@@ -128,13 +132,7 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
${this._version === this._version_latest
|
${this._action === null
|
||||||
? html`<p>
|
|
||||||
${this.supervisor.localize("update_available.no_update", {
|
|
||||||
name: this._name,
|
|
||||||
})}
|
|
||||||
</p>`
|
|
||||||
: !this._updating
|
|
||||||
? html`
|
? html`
|
||||||
${this._changelogContent
|
${this._changelogContent
|
||||||
? html`
|
? html`
|
||||||
@@ -168,13 +166,18 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
: html`<ha-circular-progress alt="Updating" size="large" active>
|
: html`<ha-circular-progress alt="Updating" size="large" active>
|
||||||
</ha-circular-progress>
|
</ha-circular-progress>
|
||||||
<p class="progress-text">
|
<p class="progress-text">
|
||||||
${this.supervisor.localize("update_available.updating", {
|
${this._action === "update"
|
||||||
name: this._name,
|
? this.supervisor.localize("update_available.updating", {
|
||||||
version: this._version_latest,
|
name: this._name,
|
||||||
})}
|
version: this._version_latest,
|
||||||
|
})
|
||||||
|
: this.supervisor.localize(
|
||||||
|
"update_available.creating_backup",
|
||||||
|
{ name: this._name }
|
||||||
|
)}
|
||||||
</p>`}
|
</p>`}
|
||||||
</div>
|
</div>
|
||||||
${this._version !== this._version_latest && !this._updating
|
${this._action === null
|
||||||
? html`
|
? html`
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
${changelog
|
${changelog
|
||||||
@@ -221,9 +224,6 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get _shouldCreateBackup(): boolean {
|
get _shouldCreateBackup(): boolean {
|
||||||
if (this._updateType && !["core", "addon"].includes(this._updateType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
|
const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
|
||||||
if (checkbox) {
|
if (checkbox) {
|
||||||
return checkbox.checked;
|
return checkbox.checked;
|
||||||
@@ -310,16 +310,37 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
|
|
||||||
private async _update() {
|
private async _update() {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._updating = true;
|
if (this._shouldCreateBackup) {
|
||||||
|
let backupArgs: HassioPartialBackupCreateParams;
|
||||||
|
if (this._updateType === "addon") {
|
||||||
|
backupArgs = {
|
||||||
|
name: `addon_${this.addonSlug}_${this._version}`,
|
||||||
|
addons: [this.addonSlug!],
|
||||||
|
homeassistant: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
backupArgs = {
|
||||||
|
name: `${this._updateType}_${this._version}`,
|
||||||
|
folders: ["homeassistant"],
|
||||||
|
homeassistant: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this._action = "backup";
|
||||||
|
try {
|
||||||
|
await createHassioPartialBackup(this.hass, backupArgs);
|
||||||
|
} catch (err: any) {
|
||||||
|
this._error = extractApiErrorMessage(err);
|
||||||
|
this._action = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._action = "update";
|
||||||
try {
|
try {
|
||||||
if (this._updateType === "addon") {
|
if (this._updateType === "addon") {
|
||||||
await updateHassioAddon(
|
await updateHassioAddon(this.hass, this.addonSlug!);
|
||||||
this.hass,
|
|
||||||
this.addonSlug!,
|
|
||||||
this._shouldCreateBackup
|
|
||||||
);
|
|
||||||
} else if (this._updateType === "core") {
|
} else if (this._updateType === "core") {
|
||||||
await updateCore(this.hass, this._shouldCreateBackup);
|
await updateCore(this.hass);
|
||||||
} else if (this._updateType === "os") {
|
} else if (this._updateType === "os") {
|
||||||
await updateOS(this.hass);
|
await updateOS(this.hass);
|
||||||
} else if (this._updateType === "supervisor") {
|
} else if (this._updateType === "supervisor") {
|
||||||
@@ -328,7 +349,7 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
this._updating = false;
|
this._action = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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="20211215.0",
|
version="20211203.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",
|
||||||
|
@@ -208,7 +208,6 @@ export const DOMAINS_HIDE_DEFAULT_MORE_INFO = [
|
|||||||
export const DOMAINS_INPUT_ROW = [
|
export const DOMAINS_INPUT_ROW = [
|
||||||
"cover",
|
"cover",
|
||||||
"fan",
|
"fan",
|
||||||
"group",
|
|
||||||
"humidifier",
|
"humidifier",
|
||||||
"input_boolean",
|
"input_boolean",
|
||||||
"input_datetime",
|
"input_datetime",
|
||||||
|
@@ -46,7 +46,6 @@ class HaAlert extends LitElement {
|
|||||||
rtl: this.rtl,
|
rtl: this.rtl,
|
||||||
[this.alertType]: true,
|
[this.alertType]: true,
|
||||||
})}"
|
})}"
|
||||||
role="alert"
|
|
||||||
>
|
>
|
||||||
<div class="icon ${this.title ? "" : "no-title"}">
|
<div class="icon ${this.title ? "" : "no-title"}">
|
||||||
<slot name="icon">
|
<slot name="icon">
|
||||||
|
@@ -56,7 +56,6 @@ export class HaRelatedFilterButtonMenu extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@click=${this._handleClick}
|
@click=${this._handleClick}
|
||||||
.label=${this.hass.localize("ui.components.related-filter-menu.filter")}
|
|
||||||
.path=${mdiFilterVariant}
|
.path=${mdiFilterVariant}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<mwc-menu-surface
|
<mwc-menu-surface
|
||||||
|
@@ -18,9 +18,13 @@ export class HaChip extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="mdc-chip ${this.noText ? "no-text" : ""}">
|
<div class="mdc-chip">
|
||||||
${this.hasIcon
|
${this.hasIcon
|
||||||
? html`<div class="mdc-chip__icon mdc-chip__icon--leading">
|
? html`<div
|
||||||
|
class="mdc-chip__icon mdc-chip__icon--leading ${this.noText
|
||||||
|
? "no-text"
|
||||||
|
: ""}"
|
||||||
|
>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
</div>`
|
</div>`
|
||||||
: null}
|
: null}
|
||||||
@@ -45,10 +49,6 @@ export class HaChip extends LitElement {
|
|||||||
color: var(--ha-chip-text-color, var(--primary-text-color));
|
color: var(--ha-chip-text-color, var(--primary-text-color));
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-chip.no-text {
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdc-chip:hover {
|
.mdc-chip:hover {
|
||||||
color: var(--ha-chip-text-color, var(--primary-text-color));
|
color: var(--ha-chip-text-color, var(--primary-text-color));
|
||||||
}
|
}
|
||||||
@@ -57,8 +57,8 @@ export class HaChip extends LitElement {
|
|||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
|
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
|
||||||
}
|
}
|
||||||
.mdc-chip.no-text
|
.mdc-chip
|
||||||
.mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
|
.mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden).no-text {
|
||||||
margin-right: -4px;
|
margin-right: -4px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -35,7 +35,7 @@ class HaCoverControls extends LitElement {
|
|||||||
hidden: !supportsOpen(this.stateObj),
|
hidden: !supportsOpen(this.stateObj),
|
||||||
})}
|
})}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.cover.open_cover"
|
"ui.dialogs.more_info_control.open_cover"
|
||||||
)}
|
)}
|
||||||
@click=${this._onOpenTap}
|
@click=${this._onOpenTap}
|
||||||
.disabled=${this._computeOpenDisabled()}
|
.disabled=${this._computeOpenDisabled()}
|
||||||
@@ -47,7 +47,7 @@ class HaCoverControls extends LitElement {
|
|||||||
hidden: !supportsStop(this.stateObj),
|
hidden: !supportsStop(this.stateObj),
|
||||||
})}
|
})}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.cover.stop_cover"
|
"ui.dialogs.more_info_control.stop_cover"
|
||||||
)}
|
)}
|
||||||
.path=${mdiStop}
|
.path=${mdiStop}
|
||||||
@click=${this._onStopTap}
|
@click=${this._onStopTap}
|
||||||
@@ -58,7 +58,7 @@ class HaCoverControls extends LitElement {
|
|||||||
hidden: !supportsClose(this.stateObj),
|
hidden: !supportsClose(this.stateObj),
|
||||||
})}
|
})}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.cover.close_cover"
|
"ui.dialogs.more_info_control.close_cover"
|
||||||
)}
|
)}
|
||||||
@click=${this._onCloseTap}
|
@click=${this._onCloseTap}
|
||||||
.disabled=${this._computeClosedDisabled()}
|
.disabled=${this._computeClosedDisabled()}
|
||||||
|
@@ -30,7 +30,7 @@ class HaCoverTiltControls extends LitElement {
|
|||||||
invisible: !supportsOpenTilt(this.stateObj),
|
invisible: !supportsOpenTilt(this.stateObj),
|
||||||
})}
|
})}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.cover.open_tilt_cover"
|
"ui.dialogs.more_info_control.open_tilt_cover"
|
||||||
)}
|
)}
|
||||||
.path=${mdiArrowTopRight}
|
.path=${mdiArrowTopRight}
|
||||||
@click=${this._onOpenTiltTap}
|
@click=${this._onOpenTiltTap}
|
||||||
@@ -40,9 +40,7 @@ class HaCoverTiltControls extends LitElement {
|
|||||||
class=${classMap({
|
class=${classMap({
|
||||||
invisible: !supportsStopTilt(this.stateObj),
|
invisible: !supportsStopTilt(this.stateObj),
|
||||||
})}
|
})}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize("ui.dialogs.more_info_control.stop_cover")}
|
||||||
"ui.dialogs.more_info_control.cover.stop_cover"
|
|
||||||
)}
|
|
||||||
.path=${mdiStop}
|
.path=${mdiStop}
|
||||||
@click=${this._onStopTiltTap}
|
@click=${this._onStopTiltTap}
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
@@ -52,7 +50,7 @@ class HaCoverTiltControls extends LitElement {
|
|||||||
invisible: !supportsCloseTilt(this.stateObj),
|
invisible: !supportsCloseTilt(this.stateObj),
|
||||||
})}
|
})}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.cover.close_tilt_cover"
|
"ui.dialogs.more_info_control.close_tilt_cover"
|
||||||
)}
|
)}
|
||||||
.path=${mdiArrowBottomLeft}
|
.path=${mdiArrowBottomLeft}
|
||||||
@click=${this._onCloseTiltTap}
|
@click=${this._onCloseTiltTap}
|
||||||
|
@@ -96,11 +96,7 @@ export class HaDateInput extends LitElement {
|
|||||||
attr-for-value="value"
|
attr-for-value="value"
|
||||||
.i18n=${i18n}
|
.i18n=${i18n}
|
||||||
>
|
>
|
||||||
<paper-input
|
<paper-input .label=${this.label} no-label-float>
|
||||||
.label=${this.label}
|
|
||||||
.disabled=${this.disabled}
|
|
||||||
no-label-float
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="suffix" .path=${mdiCalendar}></ha-svg-icon>
|
<ha-svg-icon slot="suffix" .path=${mdiCalendar}></ha-svg-icon>
|
||||||
</paper-input>
|
</paper-input>
|
||||||
</vaadin-date-picker-light>`;
|
</vaadin-date-picker-light>`;
|
||||||
|
@@ -122,20 +122,14 @@ class HaDurationInput extends LitElement {
|
|||||||
value %= 60;
|
value %= 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newValue: HaDurationData = {
|
|
||||||
hours,
|
|
||||||
minutes,
|
|
||||||
seconds: this._seconds,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.enableMillisecond || this._milliseconds) {
|
|
||||||
newValue.milliseconds = this._milliseconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
newValue[unit] = value;
|
|
||||||
|
|
||||||
fireEvent(this, "value-changed", {
|
fireEvent(this, "value-changed", {
|
||||||
value: newValue,
|
value: {
|
||||||
|
hours,
|
||||||
|
minutes,
|
||||||
|
seconds: this._seconds,
|
||||||
|
milliseconds: this._milliseconds,
|
||||||
|
...{ [unit]: value },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -66,7 +66,7 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
${isPassword
|
${isPassword
|
||||||
? html`<ha-icon-button
|
? html`<ha-icon-button
|
||||||
toggles
|
toggles
|
||||||
.label=${`${this._unmaskedPassword ? "Hide" : "Show"} password`}
|
.label="Click to toggle between masked and clear password"
|
||||||
@click=${this._toggleUnmaskedPassword}
|
@click=${this._toggleUnmaskedPassword}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
|
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Formfield } from "@material/mwc-formfield";
|
import { Formfield } from "@material/mwc-formfield";
|
||||||
import { css, CSSResultGroup } from "lit";
|
import { css, CSSResultGroup } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
|
||||||
|
|
||||||
@customElement("ha-formfield")
|
@customElement("ha-formfield")
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -14,7 +13,6 @@ export class HaFormfield extends Formfield {
|
|||||||
case "HA-CHECKBOX":
|
case "HA-CHECKBOX":
|
||||||
case "HA-RADIO":
|
case "HA-RADIO":
|
||||||
(input as any).checked = !(input as any).checked;
|
(input as any).checked = !(input as any).checked;
|
||||||
fireEvent(input, "change");
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
input.click();
|
input.click();
|
||||||
|
@@ -29,7 +29,7 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html` <!-- Collapsed representation for small screens -->
|
? html` <!-- Collapsed Representation for Small Screens -->
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
@click=${this._handleIconOverflowMenuOpened}
|
@click=${this._handleIconOverflowMenuOpened}
|
||||||
@closed=${this._handleIconOverflowMenuClosed}
|
@closed=${this._handleIconOverflowMenuClosed}
|
||||||
@@ -59,7 +59,8 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</ha-button-menu>`
|
</ha-button-menu>`
|
||||||
: html`
|
: html`
|
||||||
<!-- Icon representation for big screens -->
|
<!-- Icon Representation for Big Screens -->
|
||||||
|
|
||||||
${this.items.map((item) =>
|
${this.items.map((item) =>
|
||||||
item.narrowOnly
|
item.narrowOnly
|
||||||
? ""
|
? ""
|
||||||
@@ -69,12 +70,13 @@ export class HaIconOverflowMenu extends LitElement {
|
|||||||
${item.tooltip}
|
${item.tooltip}
|
||||||
</paper-tooltip>`
|
</paper-tooltip>`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-icon-button
|
<mwc-icon-button
|
||||||
@click=${item.action}
|
@click=${item.action}
|
||||||
.label=${item.label}
|
.label=${item.label}
|
||||||
.path=${item.path}
|
|
||||||
.disabled=${item.disabled}
|
.disabled=${item.disabled}
|
||||||
></ha-icon-button>
|
>
|
||||||
|
<ha-svg-icon .path=${item.path}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
</div> `
|
</div> `
|
||||||
)}
|
)}
|
||||||
`}
|
`}
|
||||||
|
@@ -39,7 +39,6 @@ export class HaPictureUpload extends LitElement {
|
|||||||
.uploading=${this._uploading}
|
.uploading=${this._uploading}
|
||||||
.value=${this.value ? html`<img .src=${this.value} />` : ""}
|
.value=${this.value ? html`<img .src=${this.value} />` : ""}
|
||||||
@file-picked=${this._handleFilePicked}
|
@file-picked=${this._handleFilePicked}
|
||||||
@change=${this._handleFileCleared}
|
|
||||||
accept="image/png, image/jpeg, image/gif"
|
accept="image/png, image/jpeg, image/gif"
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
`;
|
`;
|
||||||
@@ -54,10 +53,6 @@ export class HaPictureUpload extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleFileCleared() {
|
|
||||||
this.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _cropFile(file: File) {
|
private async _cropFile(file: File) {
|
||||||
if (!["image/png", "image/jpeg", "image/gif"].includes(file.type)) {
|
if (!["image/png", "image/jpeg", "image/gif"].includes(file.type)) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@material/mwc-select/mwc-select";
|
import "@material/mwc-select/mwc-select";
|
||||||
import "@material/mwc-textfield/mwc-textfield";
|
import type { Select } from "@material/mwc-select/mwc-select";
|
||||||
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
|
||||||
import { mdiCamera } from "@mdi/js";
|
|
||||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import type QrScanner from "qr-scanner";
|
import type QrScanner from "qr-scanner";
|
||||||
@@ -10,8 +8,6 @@ import { fireEvent } from "../common/dom/fire_event";
|
|||||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import "./ha-alert";
|
import "./ha-alert";
|
||||||
import "./ha-button-menu";
|
|
||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
|
|
||||||
@customElement("ha-qr-scanner")
|
@customElement("ha-qr-scanner")
|
||||||
class HaQrScanner extends LitElement {
|
class HaQrScanner extends LitElement {
|
||||||
@@ -29,8 +25,6 @@ class HaQrScanner extends LitElement {
|
|||||||
|
|
||||||
@query("#canvas-container", true) private _canvasContainer!: HTMLDivElement;
|
@query("#canvas-container", true) private _canvasContainer!: HTMLDivElement;
|
||||||
|
|
||||||
@query("mwc-textfield") private _manualInput?: TextField;
|
|
||||||
|
|
||||||
public disconnectedCallback(): void {
|
public disconnectedCallback(): void {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this._qrNotFoundCount = 0;
|
this._qrNotFoundCount = 0;
|
||||||
@@ -64,53 +58,34 @@ class HaQrScanner extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`${this._error
|
return html`${this._cameras && this._cameras.length > 1
|
||||||
|
? html`<mwc-select
|
||||||
|
.label=${this.localize(
|
||||||
|
"ui.panel.config.zwave_js.add_node.select_camera"
|
||||||
|
)}
|
||||||
|
fixedMenuPosition
|
||||||
|
naturalMenuWidth
|
||||||
|
@closed=${stopPropagation}
|
||||||
|
@selected=${this._cameraChanged}
|
||||||
|
>
|
||||||
|
${this._cameras!.map(
|
||||||
|
(camera) => html`
|
||||||
|
<mwc-list-item .value=${camera.id}>${camera.label}</mwc-list-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</mwc-select>`
|
||||||
|
: ""}
|
||||||
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
${navigator.mediaDevices
|
${navigator.mediaDevices
|
||||||
? html`<video></video>
|
? html`<video></video>
|
||||||
<div id="canvas-container">
|
<div id="canvas-container"></div>`
|
||||||
${this._cameras && this._cameras.length > 1
|
: html`<ha-alert alert-type="warning"
|
||||||
? html`<ha-button-menu
|
>${!window.isSecureContext
|
||||||
corner="BOTTOM_START"
|
? "You can only use your camera to scan a QR core when using HTTPS."
|
||||||
fixed
|
: "Your browser doesn't support QR scanning."}</ha-alert
|
||||||
@closed=${stopPropagation}
|
>`}`;
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.label=${this.localize(
|
|
||||||
"ui.components.qr-scanner.select_camera"
|
|
||||||
)}
|
|
||||||
.path=${mdiCamera}
|
|
||||||
></ha-icon-button>
|
|
||||||
${this._cameras!.map(
|
|
||||||
(camera) => html`
|
|
||||||
<mwc-list-item
|
|
||||||
.value=${camera.id}
|
|
||||||
@click=${this._cameraChanged}
|
|
||||||
>${camera.label}</mwc-list-item
|
|
||||||
>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</ha-button-menu>`
|
|
||||||
: ""}
|
|
||||||
</div>`
|
|
||||||
: html`<ha-alert alert-type="warning">
|
|
||||||
${!window.isSecureContext
|
|
||||||
? this.localize("ui.components.qr-scanner.only_https_supported")
|
|
||||||
: this.localize("ui.components.qr-scanner.not_supported")}
|
|
||||||
</ha-alert>
|
|
||||||
<p>${this.localize("ui.components.qr-scanner.manual_input")}</p>
|
|
||||||
<div class="row">
|
|
||||||
<mwc-textfield
|
|
||||||
.label=${this.localize("ui.components.qr-scanner.enter_qr_code")}
|
|
||||||
@keyup=${this._manualKeyup}
|
|
||||||
@paste=${this._manualPaste}
|
|
||||||
></mwc-textfield>
|
|
||||||
<mwc-button @click=${this._manualSubmit}
|
|
||||||
>${this.localize("ui.common.submit")}</mwc-button
|
|
||||||
>
|
|
||||||
</div>`}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadQrScanner() {
|
private async _loadQrScanner() {
|
||||||
@@ -159,49 +134,17 @@ class HaQrScanner extends LitElement {
|
|||||||
fireEvent(this, "qr-code-scanned", { value: qrCodeString });
|
fireEvent(this, "qr-code-scanned", { value: qrCodeString });
|
||||||
};
|
};
|
||||||
|
|
||||||
private _manualKeyup(ev: KeyboardEvent) {
|
|
||||||
if (ev.key === "Enter") {
|
|
||||||
this._qrCodeScanned((ev.target as TextField).value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _manualPaste(ev: ClipboardEvent) {
|
|
||||||
this._qrCodeScanned(
|
|
||||||
// @ts-ignore
|
|
||||||
(ev.clipboardData || window.clipboardData).getData("text")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _manualSubmit() {
|
|
||||||
this._qrCodeScanned(this._manualInput!.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _cameraChanged(ev: CustomEvent): void {
|
private _cameraChanged(ev: CustomEvent): void {
|
||||||
this._qrScanner?.setCamera((ev.target as any).value);
|
this._qrScanner?.setCamera((ev.target as Select).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
canvas {
|
canvas {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
#canvas-container {
|
mwc-select {
|
||||||
position: relative;
|
width: 100%;
|
||||||
}
|
margin-bottom: 16px;
|
||||||
ha-button-menu {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 8px;
|
|
||||||
right: 8px;
|
|
||||||
background: #727272b2;
|
|
||||||
color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
mwc-textfield {
|
|
||||||
flex: 1;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ import {
|
|||||||
mdiBell,
|
mdiBell,
|
||||||
mdiCalendar,
|
mdiCalendar,
|
||||||
mdiCart,
|
mdiCart,
|
||||||
mdiCellphoneCog,
|
|
||||||
mdiChartBox,
|
mdiChartBox,
|
||||||
mdiClose,
|
mdiClose,
|
||||||
mdiCog,
|
mdiCog,
|
||||||
@@ -44,10 +43,6 @@ import {
|
|||||||
PersistentNotification,
|
PersistentNotification,
|
||||||
subscribeNotifications,
|
subscribeNotifications,
|
||||||
} from "../data/persistent_notification";
|
} from "../data/persistent_notification";
|
||||||
import {
|
|
||||||
ExternalConfig,
|
|
||||||
getExternalConfig,
|
|
||||||
} from "../external_app/external_config";
|
|
||||||
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
|
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
import type { HomeAssistant, PanelInfo, Route } from "../types";
|
import type { HomeAssistant, PanelInfo, Route } from "../types";
|
||||||
@@ -192,8 +187,6 @@ class HaSidebar extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public editMode = false;
|
@property({ type: Boolean }) public editMode = false;
|
||||||
|
|
||||||
@state() private _externalConfig?: ExternalConfig;
|
|
||||||
|
|
||||||
@state() private _notifications?: PersistentNotification[];
|
@state() private _notifications?: PersistentNotification[];
|
||||||
|
|
||||||
@state() private _renderEmptySortable = false;
|
@state() private _renderEmptySortable = false;
|
||||||
@@ -240,7 +233,6 @@ class HaSidebar extends LitElement {
|
|||||||
changedProps.has("expanded") ||
|
changedProps.has("expanded") ||
|
||||||
changedProps.has("narrow") ||
|
changedProps.has("narrow") ||
|
||||||
changedProps.has("alwaysExpand") ||
|
changedProps.has("alwaysExpand") ||
|
||||||
changedProps.has("_externalConfig") ||
|
|
||||||
changedProps.has("_notifications") ||
|
changedProps.has("_notifications") ||
|
||||||
changedProps.has("editMode") ||
|
changedProps.has("editMode") ||
|
||||||
changedProps.has("_renderEmptySortable") ||
|
changedProps.has("_renderEmptySortable") ||
|
||||||
@@ -271,12 +263,6 @@ class HaSidebar extends LitElement {
|
|||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
if (this.hass && this.hass.auth.external) {
|
|
||||||
getExternalConfig(this.hass.auth.external).then((conf) => {
|
|
||||||
this._externalConfig = conf;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
subscribeNotifications(this.hass.connection, (notifications) => {
|
subscribeNotifications(this.hass.connection, (notifications) => {
|
||||||
this._notifications = notifications;
|
this._notifications = notifications;
|
||||||
});
|
});
|
||||||
@@ -372,7 +358,6 @@ class HaSidebar extends LitElement {
|
|||||||
: this._renderPanels(beforeSpacer)}
|
: this._renderPanels(beforeSpacer)}
|
||||||
${this._renderSpacer()}
|
${this._renderSpacer()}
|
||||||
${this._renderPanels(afterSpacer)}
|
${this._renderPanels(afterSpacer)}
|
||||||
${this._renderExternalConfiguration()}
|
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -402,7 +387,7 @@ class HaSidebar extends LitElement {
|
|||||||
) {
|
) {
|
||||||
return html`
|
return html`
|
||||||
<a
|
<a
|
||||||
role="option"
|
aria-role="option"
|
||||||
href=${`/${urlPath}`}
|
href=${`/${urlPath}`}
|
||||||
data-panel=${urlPath}
|
data-panel=${urlPath}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
@@ -507,7 +492,7 @@ class HaSidebar extends LitElement {
|
|||||||
>
|
>
|
||||||
<paper-icon-item
|
<paper-icon-item
|
||||||
class="notifications"
|
class="notifications"
|
||||||
role="option"
|
aria-role="option"
|
||||||
@click=${this._handleShowNotificationDrawer}
|
@click=${this._handleShowNotificationDrawer}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="item-icon" .path=${mdiBell}></ha-svg-icon>
|
<ha-svg-icon slot="item-icon" .path=${mdiBell}></ha-svg-icon>
|
||||||
@@ -538,7 +523,7 @@ class HaSidebar extends LitElement {
|
|||||||
href="/profile"
|
href="/profile"
|
||||||
data-panel="panel"
|
data-panel="panel"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
role="option"
|
aria-role="option"
|
||||||
aria-label=${this.hass.localize("panel.profile")}
|
aria-label=${this.hass.localize("panel.profile")}
|
||||||
@mouseenter=${this._itemMouseEnter}
|
@mouseenter=${this._itemMouseEnter}
|
||||||
@mouseleave=${this._itemMouseLeave}
|
@mouseleave=${this._itemMouseLeave}
|
||||||
@@ -557,43 +542,6 @@ class HaSidebar extends LitElement {
|
|||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderExternalConfiguration() {
|
|
||||||
return html`${!this.hass.user?.is_admin &&
|
|
||||||
this._externalConfig &&
|
|
||||||
this._externalConfig.hasSettingsScreen
|
|
||||||
? html`
|
|
||||||
<a
|
|
||||||
role="option"
|
|
||||||
aria-label=${this.hass.localize(
|
|
||||||
"ui.sidebar.external_app_configuration"
|
|
||||||
)}
|
|
||||||
href="#external-app-configuration"
|
|
||||||
tabindex="-1"
|
|
||||||
@click=${this._handleExternalAppConfiguration}
|
|
||||||
@mouseenter=${this._itemMouseEnter}
|
|
||||||
@mouseleave=${this._itemMouseLeave}
|
|
||||||
>
|
|
||||||
<paper-icon-item>
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="item-icon"
|
|
||||||
.path=${mdiCellphoneCog}
|
|
||||||
></ha-svg-icon>
|
|
||||||
<span class="item-text">
|
|
||||||
${this.hass.localize("ui.sidebar.external_app_configuration")}
|
|
||||||
</span>
|
|
||||||
</paper-icon-item>
|
|
||||||
</a>
|
|
||||||
`
|
|
||||||
: ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleExternalAppConfiguration(ev: Event) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.hass.auth.external!.fireMessage({
|
|
||||||
type: "config_screen/show",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _tooltip() {
|
private get _tooltip() {
|
||||||
return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement;
|
return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,6 @@ export interface DeviceRegistryEntry {
|
|||||||
model: string | null;
|
model: string | null;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
sw_version: string | null;
|
sw_version: string | null;
|
||||||
hw_version: string | null;
|
|
||||||
via_device_id: string | null;
|
via_device_id: string | null;
|
||||||
area_id: string | null;
|
area_id: string | null;
|
||||||
name_by_user: string | null;
|
name_by_user: string | null;
|
||||||
|
@@ -302,8 +302,7 @@ export const installHassioAddon = async (
|
|||||||
|
|
||||||
export const updateHassioAddon = async (
|
export const updateHassioAddon = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
slug: string,
|
slug: string
|
||||||
backup: boolean
|
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
||||||
await hass.callWS({
|
await hass.callWS({
|
||||||
@@ -311,13 +310,11 @@ export const updateHassioAddon = async (
|
|||||||
endpoint: `/store/addons/${slug}/update`,
|
endpoint: `/store/addons/${slug}/update`,
|
||||||
method: "post",
|
method: "post",
|
||||||
timeout: null,
|
timeout: null,
|
||||||
data: { backup: backup },
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await hass.callApi<HassioResponse<void>>(
|
await hass.callApi<HassioResponse<void>>(
|
||||||
"POST",
|
"POST",
|
||||||
`hassio/addons/${slug}/update`,
|
`hassio/addons/${slug}/update`
|
||||||
{ backup: backup }
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -156,7 +156,6 @@ export interface MediaPlayerThumbnail {
|
|||||||
|
|
||||||
export interface ControlButton {
|
export interface ControlButton {
|
||||||
icon: string;
|
icon: string;
|
||||||
// Used as key for action as well as tooltip and aria-label translation key
|
|
||||||
action: string;
|
action: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,18 +6,15 @@ export const restartCore = async (hass: HomeAssistant) => {
|
|||||||
await hass.callService("homeassistant", "restart");
|
await hass.callService("homeassistant", "restart");
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateCore = async (hass: HomeAssistant, backup: boolean) => {
|
export const updateCore = async (hass: HomeAssistant) => {
|
||||||
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
||||||
await hass.callWS({
|
await hass.callWS({
|
||||||
type: "supervisor/api",
|
type: "supervisor/api",
|
||||||
endpoint: "/core/update",
|
endpoint: "/core/update",
|
||||||
method: "post",
|
method: "post",
|
||||||
timeout: null,
|
timeout: null,
|
||||||
data: { backup: backup },
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await hass.callApi<HassioResponse<void>>("POST", `hassio/core/update`, {
|
await hass.callApi<HassioResponse<void>>("POST", `hassio/core/update`);
|
||||||
backup: backup,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -208,11 +208,11 @@ export const enum NodeStatus {
|
|||||||
export interface ZwaveJSProvisioningEntry {
|
export interface ZwaveJSProvisioningEntry {
|
||||||
/** The device specific key (DSK) in the form aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222 */
|
/** The device specific key (DSK) in the form aaaaa-bbbbb-ccccc-ddddd-eeeee-fffff-11111-22222 */
|
||||||
dsk: string;
|
dsk: string;
|
||||||
security_classes: SecurityClass[];
|
securityClasses: SecurityClass[];
|
||||||
additional_properties: {
|
/**
|
||||||
nodeId?: number;
|
* Additional properties to be stored in this provisioning entry, e.g. the device ID from a scanned QR code
|
||||||
[prop: string]: any;
|
*/
|
||||||
};
|
[prop: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestedGrant {
|
export interface RequestedGrant {
|
||||||
@@ -278,7 +278,7 @@ export const setZwaveDataCollectionPreference = (
|
|||||||
export const fetchZwaveProvisioningEntries = (
|
export const fetchZwaveProvisioningEntries = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry_id: string
|
entry_id: string
|
||||||
): Promise<ZwaveJSProvisioningEntry[]> =>
|
): Promise<any> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "zwave_js/get_provisioning_entries",
|
type: "zwave_js/get_provisioning_entries",
|
||||||
entry_id,
|
entry_id,
|
||||||
|
138
src/dialogs/developert-tools/ha-developer-tools-dialog.ts
Normal file
138
src/dialogs/developert-tools/ha-developer-tools-dialog.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import { mdiClose } from "@mdi/js";
|
||||||
|
import "@polymer/paper-tabs";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
|
import type { HomeAssistant, Route } from "../../types";
|
||||||
|
import "../../components/ha-dialog";
|
||||||
|
import "../../components/ha-tabs";
|
||||||
|
import "../../components/ha-icon-button";
|
||||||
|
import "../../panels/developer-tools/developer-tools-router";
|
||||||
|
import type { HaDialog } from "../../components/ha-dialog";
|
||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
|
||||||
|
@customElement("ha-developer-tools-dialog")
|
||||||
|
export class HaDeveloperToolsDialog extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _opened = false;
|
||||||
|
|
||||||
|
@state() private _route: Route = {
|
||||||
|
prefix: "/developer-tools",
|
||||||
|
path: "/state",
|
||||||
|
};
|
||||||
|
|
||||||
|
@query("ha-dialog", true) private _dialog!: HaDialog;
|
||||||
|
|
||||||
|
public async showDialog(): Promise<void> {
|
||||||
|
this._opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async closeDialog(): Promise<void> {
|
||||||
|
this._opened = false;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._opened) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-dialog open @closed=${this.closeDialog}>
|
||||||
|
<div class="header">
|
||||||
|
<ha-tabs
|
||||||
|
scrollable
|
||||||
|
attr-for-selected="page-name"
|
||||||
|
.selected=${this._route.path.substr(1)}
|
||||||
|
@iron-activate=${this.handlePageSelected}
|
||||||
|
>
|
||||||
|
<paper-tab page-name="state">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.developer-tools.tabs.states.title"
|
||||||
|
)}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="service">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.developer-tools.tabs.services.title"
|
||||||
|
)}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="template">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.developer-tools.tabs.templates.title"
|
||||||
|
)}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="event">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.developer-tools.tabs.events.title"
|
||||||
|
)}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="statistics">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.developer-tools.tabs.statistics.title"
|
||||||
|
)}
|
||||||
|
</paper-tab>
|
||||||
|
</ha-tabs>
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiClose}
|
||||||
|
@click=${this.closeDialog}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
|
<developer-tools-router
|
||||||
|
.route=${this._route}
|
||||||
|
.narrow=${document.body.clientWidth < 600}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></developer-tools-router>
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
this.hass.loadBackendTranslation("title");
|
||||||
|
this.hass.loadFragmentTranslation("developer-tools");
|
||||||
|
}
|
||||||
|
|
||||||
|
private handlePageSelected(ev) {
|
||||||
|
const newPage = ev.detail.item.getAttribute("page-name");
|
||||||
|
if (newPage !== this._route.path.substr(1)) {
|
||||||
|
this._route = {
|
||||||
|
prefix: "/developer-tools",
|
||||||
|
path: `/${newPage}`,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-min-width: 100vw;
|
||||||
|
--mdc-dialog-min-height: 100vh;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
ha-tabs {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-developer-tools-dialog": HaDeveloperToolsDialog;
|
||||||
|
}
|
||||||
|
}
|
12
src/dialogs/developert-tools/show-dialog-developer-tools.ts
Normal file
12
src/dialogs/developert-tools/show-dialog-developer-tools.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
|
export const loadDeveloperToolDialog = () =>
|
||||||
|
import("./ha-developer-tools-dialog");
|
||||||
|
|
||||||
|
export const showDeveloperToolDialog = (element: HTMLElement): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "ha-developer-tools-dialog",
|
||||||
|
dialogImport: loadDeveloperToolDialog,
|
||||||
|
dialogParams: {},
|
||||||
|
});
|
||||||
|
};
|
@@ -65,9 +65,6 @@ class MoreInfoMediaPlayer extends LitElement {
|
|||||||
action=${control.action}
|
action=${control.action}
|
||||||
@click=${this._handleClick}
|
@click=${this._handleClick}
|
||||||
.path=${control.icon}
|
.path=${control.icon}
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.card.media_player.${control.action}`
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
`
|
`
|
||||||
|
@@ -99,8 +99,6 @@ export class QuickBar extends LitElement {
|
|||||||
|
|
||||||
private _focusSet = false;
|
private _focusSet = false;
|
||||||
|
|
||||||
private _focusListElement?: ListItem | null;
|
|
||||||
|
|
||||||
public async showDialog(params: QuickBarParams) {
|
public async showDialog(params: QuickBarParams) {
|
||||||
this._commandMode = params.commandMode || this._toggleIfAlreadyOpened();
|
this._commandMode = params.commandMode || this._toggleIfAlreadyOpened();
|
||||||
this._initializeItemsIfNeeded();
|
this._initializeItemsIfNeeded();
|
||||||
@@ -319,8 +317,7 @@ export class QuickBar extends LitElement {
|
|||||||
} else if (ev.code === "ArrowDown") {
|
} else if (ev.code === "ArrowDown") {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this._getItemAtIndex(0)?.focus();
|
this._getItemAtIndex(0)?.focus();
|
||||||
this._focusSet = true;
|
this._getItemAtIndex(1)?.focus();
|
||||||
this._focusListElement = this._getItemAtIndex(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,11 +350,6 @@ export class QuickBar extends LitElement {
|
|||||||
this._initializeItemsIfNeeded();
|
this._initializeItemsIfNeeded();
|
||||||
this._filter = this._search;
|
this._filter = this._search;
|
||||||
} else {
|
} else {
|
||||||
if (this._focusSet && this._focusListElement) {
|
|
||||||
this._focusSet = false;
|
|
||||||
// @ts-ignore
|
|
||||||
this._focusListElement.rippleHandlers.endFocus();
|
|
||||||
}
|
|
||||||
this._debouncedSetFilter(this._search);
|
this._debouncedSetFilter(this._search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,14 +366,12 @@ export class QuickBar extends LitElement {
|
|||||||
private _setFocusFirstListItem() {
|
private _setFocusFirstListItem() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._getItemAtIndex(0)?.rippleHandlers.startFocus();
|
this._getItemAtIndex(0)?.rippleHandlers.startFocus();
|
||||||
this._focusListElement = this._getItemAtIndex(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleListItemKeyDown(ev: KeyboardEvent) {
|
private _handleListItemKeyDown(ev: KeyboardEvent) {
|
||||||
const isSingleCharacter = ev.key.length === 1;
|
const isSingleCharacter = ev.key.length === 1;
|
||||||
const isFirstListItem =
|
const isFirstListItem =
|
||||||
(ev.target as HTMLElement).getAttribute("index") === "0";
|
(ev.target as HTMLElement).getAttribute("index") === "0";
|
||||||
this._focusListElement = ev.target as ListItem;
|
|
||||||
if (ev.key === "ArrowUp") {
|
if (ev.key === "ArrowUp") {
|
||||||
if (isFirstListItem) {
|
if (isFirstListItem) {
|
||||||
this._filterInputField?.focus();
|
this._filterInputField?.focus();
|
||||||
@@ -521,13 +511,7 @@ export class QuickBar extends LitElement {
|
|||||||
if (page.component) {
|
if (page.component) {
|
||||||
const info = this._getNavigationInfoFromConfig(page);
|
const info = this._getNavigationInfoFromConfig(page);
|
||||||
|
|
||||||
// Add to list, but only if we do not already have an entry for the same path and component
|
if (info) {
|
||||||
if (
|
|
||||||
info &&
|
|
||||||
!items.some(
|
|
||||||
(e) => e.path === info.path && e.component === info.component
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
items.push(info);
|
items.push(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,25 +37,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearUrlParams = () => {
|
|
||||||
// Clear auth data from url if we have been able to establish a connection
|
|
||||||
if (location.search.includes("auth_callback=1")) {
|
|
||||||
const searchParams = new URLSearchParams(location.search);
|
|
||||||
// https://github.com/home-assistant/home-assistant-js-websocket/blob/master/lib/auth.ts
|
|
||||||
// Remove all data from QueryCallbackData type
|
|
||||||
searchParams.delete("auth_callback");
|
|
||||||
searchParams.delete("code");
|
|
||||||
searchParams.delete("state");
|
|
||||||
searchParams.delete("storeToken");
|
|
||||||
const search = searchParams.toString();
|
|
||||||
history.replaceState(
|
|
||||||
null,
|
|
||||||
"",
|
|
||||||
`${location.pathname}${search ? `?${search}` : ""}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const authProm = isExternal
|
const authProm = isExternal
|
||||||
? () =>
|
? () =>
|
||||||
import("../external_app/external_auth").then(({ createExternalAuth }) =>
|
import("../external_app/external_auth").then(({ createExternalAuth }) =>
|
||||||
@@ -71,7 +52,23 @@ const authProm = isExternal
|
|||||||
const connProm = async (auth) => {
|
const connProm = async (auth) => {
|
||||||
try {
|
try {
|
||||||
const conn = await createConnection({ auth });
|
const conn = await createConnection({ auth });
|
||||||
clearUrlParams();
|
// Clear auth data from url if we have been able to establish a connection
|
||||||
|
if (location.search.includes("auth_callback=1")) {
|
||||||
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
// https://github.com/home-assistant/home-assistant-js-websocket/blob/master/lib/auth.ts
|
||||||
|
// Remove all data from QueryCallbackData type
|
||||||
|
searchParams.delete("auth_callback");
|
||||||
|
searchParams.delete("code");
|
||||||
|
searchParams.delete("state");
|
||||||
|
searchParams.delete("storeToken");
|
||||||
|
const search = searchParams.toString();
|
||||||
|
history.replaceState(
|
||||||
|
null,
|
||||||
|
"",
|
||||||
|
`${location.pathname}${search ? `?${search}` : ""}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return { auth, conn };
|
return { auth, conn };
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err !== ERR_INVALID_AUTH) {
|
if (err !== ERR_INVALID_AUTH) {
|
||||||
@@ -88,7 +85,6 @@ const connProm = async (auth) => {
|
|||||||
}
|
}
|
||||||
auth = await authProm();
|
auth = await authProm();
|
||||||
const conn = await createConnection({ auth });
|
const conn = await createConnection({ auth });
|
||||||
clearUrlParams();
|
|
||||||
return { auth, conn };
|
return { auth, conn };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
* Auth class that connects to a native app for authentication.
|
* Auth class that connects to a native app for authentication.
|
||||||
*/
|
*/
|
||||||
import { Auth } from "home-assistant-js-websocket";
|
import { Auth } from "home-assistant-js-websocket";
|
||||||
import { ExternalMessaging, EMMessage } from "./external_messaging";
|
import { ExternalMessaging, InternalMessage } from "./external_messaging";
|
||||||
|
|
||||||
const CALLBACK_SET_TOKEN = "externalAuthSetToken";
|
const CALLBACK_SET_TOKEN = "externalAuthSetToken";
|
||||||
const CALLBACK_REVOKE_TOKEN = "externalAuthRevokeToken";
|
const CALLBACK_REVOKE_TOKEN = "externalAuthRevokeToken";
|
||||||
@@ -36,7 +36,7 @@ declare global {
|
|||||||
postMessage(payload: BasePayload);
|
postMessage(payload: BasePayload);
|
||||||
};
|
};
|
||||||
externalBus: {
|
externalBus: {
|
||||||
postMessage(payload: EMMessage);
|
postMessage(payload: InternalMessage);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { Connection } from "home-assistant-js-websocket";
|
|
||||||
import {
|
import {
|
||||||
externalForwardConnectionEvents,
|
externalForwardConnectionEvents,
|
||||||
externalForwardHaptics,
|
externalForwardHaptics,
|
||||||
@@ -8,50 +7,39 @@ const CALLBACK_EXTERNAL_BUS = "externalBus";
|
|||||||
|
|
||||||
interface CommandInFlight {
|
interface CommandInFlight {
|
||||||
resolve: (data: any) => void;
|
resolve: (data: any) => void;
|
||||||
reject: (err: EMError) => void;
|
reject: (err: ExternalError) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EMMessage {
|
export interface InternalMessage {
|
||||||
id?: number;
|
id?: number;
|
||||||
type: string;
|
type: string;
|
||||||
payload?: unknown;
|
payload?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EMError {
|
interface ExternalError {
|
||||||
code: string;
|
code: string;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EMMessageResultSuccess {
|
interface ExternalMessageResult {
|
||||||
id: number;
|
id: number;
|
||||||
type: "result";
|
type: "result";
|
||||||
success: true;
|
success: true;
|
||||||
result: unknown;
|
result: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EMMessageResultError {
|
interface ExternalMessageResultError {
|
||||||
id: number;
|
id: number;
|
||||||
type: "result";
|
type: "result";
|
||||||
success: false;
|
success: false;
|
||||||
error: EMError;
|
error: ExternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EMExternalMessageRestart {
|
type ExternalMessage = ExternalMessageResult | ExternalMessageResultError;
|
||||||
id: number;
|
|
||||||
type: "command";
|
|
||||||
command: "restart";
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExternalMessage =
|
|
||||||
| EMMessageResultSuccess
|
|
||||||
| EMMessageResultError
|
|
||||||
| EMExternalMessageRestart;
|
|
||||||
|
|
||||||
export class ExternalMessaging {
|
export class ExternalMessaging {
|
||||||
public commands: { [msgId: number]: CommandInFlight } = {};
|
public commands: { [msgId: number]: CommandInFlight } = {};
|
||||||
|
|
||||||
public connection?: Connection;
|
|
||||||
|
|
||||||
public cache: Record<string, any> = {};
|
public cache: Record<string, any> = {};
|
||||||
|
|
||||||
public msgId = 0;
|
public msgId = 0;
|
||||||
@@ -66,7 +54,7 @@ export class ExternalMessaging {
|
|||||||
* Send message to external app that expects a response.
|
* Send message to external app that expects a response.
|
||||||
* @param msg message to send
|
* @param msg message to send
|
||||||
*/
|
*/
|
||||||
public sendMessage<T>(msg: EMMessage): Promise<T> {
|
public sendMessage<T>(msg: InternalMessage): Promise<T> {
|
||||||
const msgId = ++this.msgId;
|
const msgId = ++this.msgId;
|
||||||
msg.id = msgId;
|
msg.id = msgId;
|
||||||
|
|
||||||
@@ -81,9 +69,7 @@ export class ExternalMessaging {
|
|||||||
* Send message to external app without expecting a response.
|
* Send message to external app without expecting a response.
|
||||||
* @param msg message to send
|
* @param msg message to send
|
||||||
*/
|
*/
|
||||||
public fireMessage(
|
public fireMessage(msg: InternalMessage) {
|
||||||
msg: EMMessage | EMMessageResultSuccess | EMMessageResultError
|
|
||||||
) {
|
|
||||||
if (!msg.id) {
|
if (!msg.id) {
|
||||||
msg.id = ++this.msgId;
|
msg.id = ++this.msgId;
|
||||||
}
|
}
|
||||||
@@ -96,43 +82,6 @@ export class ExternalMessaging {
|
|||||||
console.log("Receiving message from external app", msg);
|
console.log("Receiving message from external app", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type === "command") {
|
|
||||||
if (!this.connection) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn("Received command without having connection set", msg);
|
|
||||||
this.fireMessage({
|
|
||||||
id: msg.id,
|
|
||||||
type: "result",
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: "commands_not_init",
|
|
||||||
message: `Commands connection not set`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (msg.command === "restart") {
|
|
||||||
this.connection.socket.close();
|
|
||||||
this.fireMessage({
|
|
||||||
id: msg.id,
|
|
||||||
type: "result",
|
|
||||||
success: true,
|
|
||||||
result: null,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.warn("Received unknown command", msg.command, msg);
|
|
||||||
this.fireMessage({
|
|
||||||
id: msg.id,
|
|
||||||
type: "result",
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: "unknown_command",
|
|
||||||
message: `Unknown command ${msg.command}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pendingCmd = this.commands[msg.id];
|
const pendingCmd = this.commands[msg.id];
|
||||||
|
|
||||||
if (!pendingCmd) {
|
if (!pendingCmd) {
|
||||||
@@ -150,7 +99,7 @@ export class ExternalMessaging {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _sendExternal(msg: EMMessage) {
|
protected _sendExternal(msg: InternalMessage) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log("Sending message to external app", msg);
|
console.log("Sending message to external app", msg);
|
||||||
|
@@ -127,9 +127,7 @@ export class HassRouterPage extends ReactiveElement {
|
|||||||
|
|
||||||
// Update the url if we know where we're mounted.
|
// Update the url if we know where we're mounted.
|
||||||
if (route) {
|
if (route) {
|
||||||
navigate(`${route.prefix}/${result}${location.search}`, {
|
navigate(`${route.prefix}/${result}`, { replace: true });
|
||||||
replace: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -51,9 +51,7 @@ export class HomeAssistantAppEl extends QuickBarMixin(HassElement) {
|
|||||||
const path = curPath();
|
const path = curPath();
|
||||||
|
|
||||||
if (["", "/"].includes(path)) {
|
if (["", "/"].includes(path)) {
|
||||||
navigate(`/${getStorageDefaultPanelUrlPath()}${location.search}`, {
|
navigate(`/${getStorageDefaultPanelUrlPath()}`, { replace: true });
|
||||||
replace: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
this._route = {
|
this._route = {
|
||||||
prefix: "",
|
prefix: "",
|
||||||
|
@@ -95,11 +95,8 @@ class OnboardingCreateUser extends LitElement {
|
|||||||
private _handleValueChanged(
|
private _handleValueChanged(
|
||||||
ev: PolymerChangedEvent<HaFormDataContainer>
|
ev: PolymerChangedEvent<HaFormDataContainer>
|
||||||
): void {
|
): void {
|
||||||
const nameChanged = ev.detail.value.name !== this._newUser.name;
|
|
||||||
this._newUser = ev.detail.value;
|
this._newUser = ev.detail.value;
|
||||||
if (nameChanged) {
|
this._maybePopulateUsername();
|
||||||
this._maybePopulateUsername();
|
|
||||||
}
|
|
||||||
this._formError.password_confirm =
|
this._formError.password_confirm =
|
||||||
this._newUser.password !== this._newUser.password_confirm
|
this._newUser.password !== this._newUser.password_confirm
|
||||||
? this.localize(
|
? this.localize(
|
||||||
|
@@ -138,8 +138,7 @@ export default class HaAutomationConditionEditor extends LitElement {
|
|||||||
if (!ev.detail.isValid) {
|
if (!ev.detail.isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
fireEvent(this, "value-changed", { value: ev.detail.value });
|
||||||
fireEvent(this, "value-changed", { value: ev.detail.value, yaml: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -109,7 +109,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
<ha-automation-condition-editor
|
<ha-automation-condition-editor
|
||||||
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
@ui-mode-not-available=${this._handleUiModeNotAvailable}
|
||||||
@value-changed=${this._handleChangeEvent}
|
|
||||||
.yamlMode=${this._yamlMode}
|
.yamlMode=${this._yamlMode}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.condition=${this.condition}
|
.condition=${this.condition}
|
||||||
@@ -128,12 +127,6 @@ export default class HaAutomationConditionRow extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleChangeEvent(ev: CustomEvent) {
|
|
||||||
if (ev.detail.yaml) {
|
|
||||||
this._warnings = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@@ -1,27 +1,17 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { assert, literal, object, optional, string, union } from "superstruct";
|
|
||||||
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
|
||||||
import "../../../../../components/entity/ha-entity-attribute-picker";
|
import "../../../../../components/entity/ha-entity-attribute-picker";
|
||||||
import "../../../../../components/entity/ha-entity-picker";
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
import "../../../../../components/ha-duration-input";
|
|
||||||
import { StateCondition } from "../../../../../data/automation";
|
import { StateCondition } from "../../../../../data/automation";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
import { HomeAssistant } from "../../../../../types";
|
||||||
import { forDictStruct } from "../../structs";
|
|
||||||
import {
|
import {
|
||||||
ConditionElement,
|
ConditionElement,
|
||||||
handleChangeEvent,
|
handleChangeEvent,
|
||||||
} from "../ha-automation-condition-row";
|
} from "../ha-automation-condition-row";
|
||||||
|
import "../../../../../components/ha-duration-input";
|
||||||
const stateConditionStruct = object({
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
condition: literal("state"),
|
|
||||||
entity_id: optional(string()),
|
|
||||||
attribute: optional(string()),
|
|
||||||
state: optional(string()),
|
|
||||||
for: optional(union([string(), forDictStruct])),
|
|
||||||
});
|
|
||||||
|
|
||||||
@customElement("ha-automation-condition-state")
|
@customElement("ha-automation-condition-state")
|
||||||
export class HaStateCondition extends LitElement implements ConditionElement {
|
export class HaStateCondition extends LitElement implements ConditionElement {
|
||||||
@@ -33,14 +23,19 @@ export class HaStateCondition extends LitElement implements ConditionElement {
|
|||||||
return { entity_id: "", state: "" };
|
return { entity_id: "", state: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public shouldUpdate(changedProperties: PropertyValues) {
|
public willUpdate(changedProperties: PropertyValues): boolean {
|
||||||
if (changedProperties.has("condition")) {
|
if (
|
||||||
try {
|
changedProperties.has("condition") &&
|
||||||
assert(this.condition, stateConditionStruct);
|
Array.isArray(this.condition?.state)
|
||||||
} catch (e: any) {
|
) {
|
||||||
fireEvent(this, "ui-mode-not-available", e);
|
fireEvent(
|
||||||
return false;
|
this,
|
||||||
}
|
"ui-mode-not-available",
|
||||||
|
Error(this.hass.localize("ui.errors.config.no_state_array_support"))
|
||||||
|
);
|
||||||
|
// We have to stop the update if state is an array.
|
||||||
|
// Otherwise the state will be changed to a comma-separated string by the input element.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
import { object, optional, number, string } from "superstruct";
|
|
||||||
|
|
||||||
export const baseTriggerStruct = object({
|
|
||||||
platform: string(),
|
|
||||||
id: optional(string()),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const forDictStruct = object({
|
|
||||||
days: optional(number()),
|
|
||||||
hours: optional(number()),
|
|
||||||
minutes: optional(number()),
|
|
||||||
seconds: optional(number()),
|
|
||||||
});
|
|
@@ -68,7 +68,7 @@ export const handleChangeEvent = (element: TriggerElement, ev: CustomEvent) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let newTrigger: Trigger;
|
let newTrigger: Trigger;
|
||||||
if (newVal === undefined || newVal === "") {
|
if (!newVal) {
|
||||||
newTrigger = { ...element.trigger };
|
newTrigger = { ...element.trigger };
|
||||||
delete newTrigger[name];
|
delete newTrigger[name];
|
||||||
} else {
|
} else {
|
||||||
@@ -291,7 +291,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
|||||||
if (!ev.detail.isValid) {
|
if (!ev.detail.isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._warnings = undefined;
|
|
||||||
fireEvent(this, "value-changed", { value: ev.detail.value });
|
fireEvent(this, "value-changed", { value: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,41 +1,19 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import {
|
|
||||||
assert,
|
|
||||||
assign,
|
|
||||||
literal,
|
|
||||||
object,
|
|
||||||
optional,
|
|
||||||
string,
|
|
||||||
union,
|
|
||||||
} from "superstruct";
|
|
||||||
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
import { createDurationData } from "../../../../../common/datetime/create_duration_data";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import { hasTemplate } from "../../../../../common/string/has-template";
|
import { hasTemplate } from "../../../../../common/string/has-template";
|
||||||
import "../../../../../components/entity/ha-entity-attribute-picker";
|
import "../../../../../components/entity/ha-entity-attribute-picker";
|
||||||
import "../../../../../components/entity/ha-entity-picker";
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
import "../../../../../components/ha-duration-input";
|
|
||||||
import { StateTrigger } from "../../../../../data/automation";
|
import { StateTrigger } from "../../../../../data/automation";
|
||||||
import { HomeAssistant } from "../../../../../types";
|
import { HomeAssistant } from "../../../../../types";
|
||||||
import { baseTriggerStruct, forDictStruct } from "../../structs";
|
import "../../../../../components/ha-duration-input";
|
||||||
import {
|
import {
|
||||||
handleChangeEvent,
|
handleChangeEvent,
|
||||||
TriggerElement,
|
TriggerElement,
|
||||||
} from "../ha-automation-trigger-row";
|
} from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
const stateTriggerStruct = assign(
|
|
||||||
baseTriggerStruct,
|
|
||||||
object({
|
|
||||||
platform: literal("state"),
|
|
||||||
entity_id: optional(string()),
|
|
||||||
attribute: optional(string()),
|
|
||||||
from: optional(string()),
|
|
||||||
to: optional(string()),
|
|
||||||
for: optional(union([string(), forDictStruct])),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
@customElement("ha-automation-trigger-state")
|
@customElement("ha-automation-trigger-state")
|
||||||
export class HaStateTrigger extends LitElement implements TriggerElement {
|
export class HaStateTrigger extends LitElement implements TriggerElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -46,16 +24,9 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
|
|||||||
return { entity_id: "" };
|
return { entity_id: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public shouldUpdate(changedProperties: PropertyValues) {
|
public willUpdate(changedProperties: PropertyValues) {
|
||||||
if (!changedProperties.has("trigger")) {
|
if (!changedProperties.has("trigger")) {
|
||||||
return true;
|
return;
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.trigger.for &&
|
|
||||||
typeof this.trigger.for === "object" &&
|
|
||||||
this.trigger.for.milliseconds === 0
|
|
||||||
) {
|
|
||||||
delete this.trigger.for.milliseconds;
|
|
||||||
}
|
}
|
||||||
// Check for templates in trigger. If found, revert to YAML mode.
|
// Check for templates in trigger. If found, revert to YAML mode.
|
||||||
if (this.trigger && hasTemplate(this.trigger)) {
|
if (this.trigger && hasTemplate(this.trigger)) {
|
||||||
@@ -64,15 +35,7 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
|
|||||||
"ui-mode-not-available",
|
"ui-mode-not-available",
|
||||||
Error(this.hass.localize("ui.errors.config.no_template_editor_support"))
|
Error(this.hass.localize("ui.errors.config.no_template_editor_support"))
|
||||||
);
|
);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
assert(this.trigger, stateTriggerStruct);
|
|
||||||
} catch (e: any) {
|
|
||||||
fireEvent(this, "ui-mode-not-available", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
|
@@ -224,7 +224,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
back-path="/config"
|
back-path="/config"
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.blueprints}
|
.tabs=${configSections.automations}
|
||||||
.columns=${this._columns(this.narrow, this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||||
.data=${this._processedBlueprints(this.blueprints)}
|
.data=${this._processedBlueprints(this.blueprints)}
|
||||||
id="entity_id"
|
id="entity_id"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { mdiCloudLock } from "@mdi/js";
|
import { mdiCellphoneCog, mdiCloudLock } from "@mdi/js";
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
import "@polymer/app-layout/app-header/app-header";
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
import {
|
import {
|
||||||
@@ -110,10 +110,29 @@ class HaConfigDashboard extends LitElement {
|
|||||||
></ha-config-navigation>
|
></ha-config-navigation>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
${this._externalConfig?.hasSettingsScreen
|
||||||
|
? html`
|
||||||
|
<ha-config-navigation
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.showAdvanced=${this.showAdvanced}
|
||||||
|
.pages=${[
|
||||||
|
{
|
||||||
|
path: "#external-app-configuration",
|
||||||
|
name: "Companion App",
|
||||||
|
description: "Location and notifications",
|
||||||
|
iconPath: mdiCellphoneCog,
|
||||||
|
iconColor: "#37474F",
|
||||||
|
core: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
@click=${this._handleExternalAppConfiguration}
|
||||||
|
></ha-config-navigation>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
<ha-config-navigation
|
<ha-config-navigation
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.externalConfig=${this._externalConfig}
|
|
||||||
.showAdvanced=${this.showAdvanced}
|
.showAdvanced=${this.showAdvanced}
|
||||||
.pages=${configSections.dashboard}
|
.pages=${configSections.dashboard}
|
||||||
></ha-config-navigation>
|
></ha-config-navigation>
|
||||||
@@ -123,6 +142,13 @@ class HaConfigDashboard extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleExternalAppConfiguration(ev: Event) {
|
||||||
|
ev.preventDefault();
|
||||||
|
this.hass.auth.external!.fireMessage({
|
||||||
|
type: "config_screen/show",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@@ -131,7 +157,7 @@ class HaConfigDashboard extends LitElement {
|
|||||||
border-bottom: var(--app-header-border-bottom);
|
border-bottom: var(--app-header-border-bottom);
|
||||||
--header-height: 55px;
|
--header-height: 55px;
|
||||||
}
|
}
|
||||||
:host(:not([narrow])) ha-card:last-child {
|
ha-card:last-child {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
ha-config-section {
|
ha-config-section {
|
||||||
@@ -152,7 +178,7 @@ class HaConfigDashboard extends LitElement {
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
:host([narrow]) ha-card {
|
:host([narrow]) ha-card {
|
||||||
border-radius: 0;
|
background-color: var(--primary-background-color);
|
||||||
box-shadow: unset;
|
box-shadow: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ import { canShowPage } from "../../../common/config/can_show_page";
|
|||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
|
import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
|
||||||
import { ExternalConfig } from "../../../external_app/external_config";
|
|
||||||
import { PageNavigation } from "../../../layouts/hass-tabs-subpage";
|
import { PageNavigation } from "../../../layouts/hass-tabs-subpage";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
@@ -20,18 +19,12 @@ class HaConfigNavigation extends LitElement {
|
|||||||
|
|
||||||
@property() public pages!: PageNavigation[];
|
@property() public pages!: PageNavigation[];
|
||||||
|
|
||||||
@property() public externalConfig?: ExternalConfig;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.pages.map((page) =>
|
${this.pages.map((page) =>
|
||||||
(
|
canShowPage(this.hass, page)
|
||||||
page.path === "#external-app-configuration"
|
|
||||||
? this.externalConfig?.hasSettingsScreen
|
|
||||||
: canShowPage(this.hass, page)
|
|
||||||
)
|
|
||||||
? html`
|
? html`
|
||||||
<a href=${page.path} role="option" tabindex="-1">
|
<a href=${page.path} aria-role="option" tabindex="-1">
|
||||||
<paper-icon-item @click=${this._entryClicked}>
|
<paper-icon-item @click=${this._entryClicked}>
|
||||||
<div
|
<div
|
||||||
class=${page.iconColor ? "icon-background" : ""}
|
class=${page.iconColor ? "icon-background" : ""}
|
||||||
@@ -43,7 +36,8 @@ class HaConfigNavigation extends LitElement {
|
|||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
${page.name ||
|
${page.name ||
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.panel.config.dashboard.${page.translationKey}.title`
|
page.translationKey ||
|
||||||
|
`ui.panel.config.${page.component}.caption`
|
||||||
)}
|
)}
|
||||||
${page.component === "cloud" && (page.info as CloudStatus)
|
${page.component === "cloud" && (page.info as CloudStatus)
|
||||||
? page.info.logged_in
|
? page.info.logged_in
|
||||||
@@ -67,7 +61,7 @@ class HaConfigNavigation extends LitElement {
|
|||||||
<div secondary>
|
<div secondary>
|
||||||
${page.description ||
|
${page.description ||
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.panel.config.dashboard.${page.translationKey}.description`
|
`ui.panel.config.${page.component}.description`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
@@ -83,16 +77,6 @@ class HaConfigNavigation extends LitElement {
|
|||||||
|
|
||||||
private _entryClicked(ev) {
|
private _entryClicked(ev) {
|
||||||
ev.currentTarget.blur();
|
ev.currentTarget.blur();
|
||||||
if (
|
|
||||||
ev.currentTarget.parentElement.href.endsWith(
|
|
||||||
"#external-app-configuration"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.hass.auth.external!.fireMessage({
|
|
||||||
type: "config_screen/show",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -66,17 +66,6 @@ export class HaDeviceCard extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.device.hw_version
|
|
||||||
? html`
|
|
||||||
<div class="extra-info">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.integrations.config_entry.hardware",
|
|
||||||
"version",
|
|
||||||
this.device.hw_version
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
mdiAccount,
|
mdiAccount,
|
||||||
mdiBadgeAccountHorizontal,
|
mdiBadgeAccountHorizontal,
|
||||||
mdiCellphoneCog,
|
|
||||||
mdiCog,
|
mdiCog,
|
||||||
mdiDevices,
|
mdiDevices,
|
||||||
mdiHomeAssistant,
|
mdiHomeAssistant,
|
||||||
@@ -49,69 +48,73 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
dashboard: [
|
dashboard: [
|
||||||
{
|
{
|
||||||
path: "/config/integrations",
|
path: "/config/integrations",
|
||||||
translationKey: "devices",
|
name: "Devices & Services",
|
||||||
|
description: "Integrations, devices, entities and areas",
|
||||||
iconPath: mdiDevices,
|
iconPath: mdiDevices,
|
||||||
iconColor: "#0D47A1",
|
iconColor: "#0D47A1",
|
||||||
core: true,
|
core: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/config/automation",
|
path: "/config/automation",
|
||||||
translationKey: "automations",
|
name: "Automations & Scenes",
|
||||||
|
description: "Automations, blueprints, scenes and scripts",
|
||||||
iconPath: mdiRobot,
|
iconPath: mdiRobot,
|
||||||
iconColor: "#518C43",
|
iconColor: "#518C43",
|
||||||
|
components: ["automation", "blueprint", "scene", "script"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/config/helpers",
|
||||||
|
name: "Automation Helpers",
|
||||||
|
description: "Elements that help build automations",
|
||||||
|
iconPath: mdiTools,
|
||||||
|
iconColor: "#4D2EA4",
|
||||||
core: true,
|
core: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/config/blueprint",
|
|
||||||
translationKey: "blueprints",
|
|
||||||
iconPath: mdiPaletteSwatch,
|
|
||||||
iconColor: "#64B5F6",
|
|
||||||
component: "blueprint",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/hassio",
|
path: "/hassio",
|
||||||
translationKey: "supervisor",
|
name: "Add-ons & Backups (Supervisor)",
|
||||||
|
description: "Create backups, check logs or reboot your system",
|
||||||
iconPath: mdiHomeAssistant,
|
iconPath: mdiHomeAssistant,
|
||||||
iconColor: "#4084CD",
|
iconColor: "#4084CD",
|
||||||
component: "hassio",
|
component: "hassio",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/config/lovelace/dashboards",
|
path: "/config/lovelace/dashboards",
|
||||||
translationKey: "dashboards",
|
name: "Dashboards",
|
||||||
|
description: "Create customized sets of cards to control your home",
|
||||||
iconPath: mdiViewDashboard,
|
iconPath: mdiViewDashboard,
|
||||||
iconColor: "#B1345C",
|
iconColor: "#B1345C",
|
||||||
component: "lovelace",
|
component: "lovelace",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/config/energy",
|
path: "/config/energy",
|
||||||
translationKey: "energy",
|
name: "Energy",
|
||||||
|
description: "Monitor your energy production and consumption",
|
||||||
iconPath: mdiLightningBolt,
|
iconPath: mdiLightningBolt,
|
||||||
iconColor: "#F1C447",
|
iconColor: "#F1C447",
|
||||||
component: "energy",
|
component: "energy",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/config/tags",
|
path: "/config/tags",
|
||||||
translationKey: "tags",
|
name: "Tags",
|
||||||
|
description:
|
||||||
|
"Trigger automations when a NFC tag, QR code, etc. is scanned",
|
||||||
iconPath: mdiNfcVariant,
|
iconPath: mdiNfcVariant,
|
||||||
iconColor: "#616161",
|
iconColor: "#616161",
|
||||||
component: "tag",
|
component: "tag",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/config/person",
|
path: "/config/person",
|
||||||
translationKey: "people",
|
name: "People & Zones",
|
||||||
|
description: "Manage the people and zones that Home Assistant tracks",
|
||||||
iconPath: mdiAccount,
|
iconPath: mdiAccount,
|
||||||
iconColor: "#E48629",
|
iconColor: "#E48629",
|
||||||
components: ["person", "zone", "users"],
|
components: ["person", "zone", "users"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "#external-app-configuration",
|
path: "/config/core",
|
||||||
translationKey: "companion",
|
name: "Settings",
|
||||||
iconPath: mdiCellphoneCog,
|
description: "Basic settings, server controls, logs and info",
|
||||||
iconColor: "#8E24AA",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/config/server_control",
|
|
||||||
translationKey: "settings",
|
|
||||||
iconPath: mdiCog,
|
iconPath: mdiCog,
|
||||||
iconColor: "#4A5963",
|
iconColor: "#4A5963",
|
||||||
core: true,
|
core: true,
|
||||||
@@ -152,6 +155,13 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
automations: [
|
automations: [
|
||||||
|
{
|
||||||
|
component: "blueprint",
|
||||||
|
path: "/config/blueprint",
|
||||||
|
translationKey: "ui.panel.config.blueprint.caption",
|
||||||
|
iconPath: mdiPaletteSwatch,
|
||||||
|
iconColor: "#518C43",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: "automation",
|
component: "automation",
|
||||||
path: "/config/automation",
|
path: "/config/automation",
|
||||||
@@ -173,6 +183,8 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
iconPath: mdiScriptText,
|
iconPath: mdiScriptText,
|
||||||
iconColor: "#518C43",
|
iconColor: "#518C43",
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
helpers: [
|
||||||
{
|
{
|
||||||
component: "helpers",
|
component: "helpers",
|
||||||
path: "/config/helpers",
|
path: "/config/helpers",
|
||||||
@@ -182,15 +194,6 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
core: true,
|
core: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
blueprints: [
|
|
||||||
{
|
|
||||||
component: "blueprint",
|
|
||||||
path: "/config/blueprint",
|
|
||||||
translationKey: "ui.panel.config.blueprint.caption",
|
|
||||||
iconPath: mdiPaletteSwatch,
|
|
||||||
iconColor: "#518C43",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tags: [
|
tags: [
|
||||||
{
|
{
|
||||||
component: "tag",
|
component: "tag",
|
||||||
@@ -444,19 +447,9 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
this.hass.loadBackendTranslation("title");
|
this.hass.loadBackendTranslation("title");
|
||||||
if (isComponentLoaded(this.hass, "cloud")) {
|
if (isComponentLoaded(this.hass, "cloud")) {
|
||||||
this._updateCloudStatus();
|
this._updateCloudStatus();
|
||||||
this.addEventListener("connection-status", (ev) => {
|
|
||||||
if (ev.detail === "connected") {
|
|
||||||
this._updateCloudStatus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (isComponentLoaded(this.hass, "hassio")) {
|
if (isComponentLoaded(this.hass, "hassio")) {
|
||||||
this._loadSupervisorUpdates();
|
this._loadSupervisorUpdates();
|
||||||
this.addEventListener("connection-status", (ev) => {
|
|
||||||
if (ev.detail === "connected") {
|
|
||||||
this._loadSupervisorUpdates();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this._supervisorUpdates = null;
|
this._supervisorUpdates = null;
|
||||||
}
|
}
|
||||||
|
@@ -132,7 +132,7 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
back-path="/config"
|
back-path="/config"
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.automations}
|
.tabs=${configSections.helpers}
|
||||||
.columns=${this._columns(this.narrow, this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||||
.data=${this._getItems(this._stateItems)}
|
.data=${this._getItems(this._stateItems)}
|
||||||
@row-click=${this._openEditDialog}
|
@row-click=${this._openEditDialog}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { property } from "lit/decorators";
|
import { property } from "lit/decorators";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
import "../../../components/ha-logo-svg";
|
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
@@ -41,14 +40,13 @@ class HaConfigInfo extends LitElement {
|
|||||||
href=${documentationUrl(this.hass, "")}
|
href=${documentationUrl(this.hass, "")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
><img
|
||||||
<ha-logo-svg
|
src="/static/icons/favicon-192x192.png"
|
||||||
title=${this.hass.localize(
|
height="192"
|
||||||
|
alt=${this.hass.localize(
|
||||||
"ui.panel.config.info.home_assistant_logo"
|
"ui.panel.config.info.home_assistant_logo"
|
||||||
)}
|
)}
|
||||||
>
|
/></a>
|
||||||
</ha-logo-svg>
|
|
||||||
</a>
|
|
||||||
<br />
|
<br />
|
||||||
<h2>Home Assistant ${hass.connection.haVersion}</h2>
|
<h2>Home Assistant ${hass.connection.haVersion}</h2>
|
||||||
<p>
|
<p>
|
||||||
@@ -195,11 +193,6 @@ class HaConfigInfo extends LitElement {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
ha-logo-svg {
|
|
||||||
padding: 12px;
|
|
||||||
height: 180px;
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -95,7 +95,7 @@ class OZWConfigDashboard extends LitElement {
|
|||||||
<ha-card>
|
<ha-card>
|
||||||
<a
|
<a
|
||||||
href="/config/ozw/network/${instance.ozw_instance}"
|
href="/config/ozw/network/${instance.ozw_instance}"
|
||||||
role="option"
|
aria-role="option"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<paper-icon-item>
|
<paper-icon-item>
|
||||||
|
@@ -129,11 +129,7 @@ class HaConfigZwave extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
<span
|
<span
|
||||||
>[[localize('ui.panel.config.zwave.node_management.header')]]</span
|
>[[localize('ui.panel.config.zwave.node_management.header')]]</span
|
||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button class="toggle-help-icon" on-click="toggleHelp">
|
||||||
class="toggle-help-icon"
|
|
||||||
on-click="toggleHelp"
|
|
||||||
label="[[localize('ui.common.help')]]"
|
|
||||||
>
|
|
||||||
<ha-icon icon="hass:help-circle"></ha-icon>
|
<ha-icon icon="hass:help-circle"></ha-icon>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import type { TextField } from "@material/mwc-textfield/mwc-textfield";
|
||||||
import "@material/mwc-textfield/mwc-textfield";
|
import "@material/mwc-textfield/mwc-textfield";
|
||||||
import { mdiAlertCircle, mdiCheckCircle, mdiQrcodeScan } from "@mdi/js";
|
import { mdiAlertCircle, mdiCheckCircle, mdiQrcodeScan } from "@mdi/js";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
@@ -44,8 +45,6 @@ export interface ZWaveJSAddNodeDevice {
|
|||||||
class DialogZWaveJSAddNode extends LitElement {
|
class DialogZWaveJSAddNode extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _params?: ZWaveJSAddNodeDialogParams;
|
|
||||||
|
|
||||||
@state() private _entryId?: string;
|
@state() private _entryId?: string;
|
||||||
|
|
||||||
@state() private _status?:
|
@state() private _status?:
|
||||||
@@ -92,7 +91,6 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog(params: ZWaveJSAddNodeDialogParams): Promise<void> {
|
public async showDialog(params: ZWaveJSAddNodeDialogParams): Promise<void> {
|
||||||
this._params = params;
|
|
||||||
this._entryId = params.entry_id;
|
this._entryId = params.entry_id;
|
||||||
this._status = "loading";
|
this._status = "loading";
|
||||||
this._checkSmartStartSupport();
|
this._checkSmartStartSupport();
|
||||||
@@ -178,16 +176,21 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
Search device
|
Search device
|
||||||
</mwc-button>`
|
</mwc-button>`
|
||||||
: this._status === "qr_scan"
|
: this._status === "qr_scan"
|
||||||
? html`${this._error
|
? html`<ha-qr-scanner
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
|
||||||
: ""}
|
|
||||||
<ha-qr-scanner
|
|
||||||
.localize=${this.hass.localize}
|
.localize=${this.hass.localize}
|
||||||
@qr-code-scanned=${this._qrCodeScanned}
|
@qr-code-scanned=${this._qrCodeScanned}
|
||||||
></ha-qr-scanner>
|
></ha-qr-scanner>
|
||||||
<mwc-button slot="secondaryAction" @click=${this._startOver}>
|
<p>
|
||||||
${this.hass.localize("ui.panel.config.zwave_js.common.back")}
|
If scanning doesn't work, you can enter the QR code value
|
||||||
</mwc-button>`
|
manually:
|
||||||
|
</p>
|
||||||
|
<mwc-textfield
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.zwave_js.add_node.enter_qr_code"
|
||||||
|
)}
|
||||||
|
.disabled=${this._qrProcessing}
|
||||||
|
@keydown=${this._qrKeyDown}
|
||||||
|
></mwc-textfield>`
|
||||||
: this._status === "validate_dsk_enter_pin"
|
: this._status === "validate_dsk_enter_pin"
|
||||||
? html`
|
? html`
|
||||||
<p>
|
<p>
|
||||||
@@ -197,9 +200,9 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
</p>
|
</p>
|
||||||
${
|
${
|
||||||
this._error
|
this._error
|
||||||
? html`<ha-alert alert-type="error">
|
? html`<ha-alert alert-type="error"
|
||||||
${this._error}
|
>${this._error}</ha-alert
|
||||||
</ha-alert>`
|
>`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
@@ -268,7 +271,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
We have not found any device in inclusion mode. Make sure the
|
We have not found any device in inclusion mode. Make sure the
|
||||||
device is active and in inclusion mode.
|
device is active and in inclusion mode.
|
||||||
</p>
|
</p>
|
||||||
<mwc-button slot="primaryAction" @click=${this._startOver}>
|
<mwc-button slot="primaryAction" @click=${this._startInclusion}>
|
||||||
Retry
|
Retry
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
@@ -367,7 +370,7 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
||||||
${this.hass.localize("ui.common.close")}
|
${this.hass.localize("ui.panel.config.zwave_js.common.close")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: this._status === "failed"
|
: this._status === "failed"
|
||||||
@@ -504,6 +507,15 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
this._status = "qr_scan";
|
this._status = "qr_scan";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _qrKeyDown(ev: KeyboardEvent) {
|
||||||
|
if (this._qrProcessing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
this._handleQrCodeScanned((ev.target as TextField).value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _qrCodeScanned(ev: CustomEvent): void {
|
private _qrCodeScanned(ev: CustomEvent): void {
|
||||||
if (this._qrProcessing) {
|
if (this._qrProcessing) {
|
||||||
return;
|
return;
|
||||||
@@ -550,16 +562,17 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
provisioningInfo
|
provisioningInfo
|
||||||
);
|
);
|
||||||
this._status = "provisioned";
|
this._status = "provisioned";
|
||||||
if (this._params?.addedCallback) {
|
|
||||||
this._params.addedCallback();
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err.message;
|
this._error = err.message;
|
||||||
this._status = "failed";
|
this._status = "failed";
|
||||||
}
|
}
|
||||||
} else if (provisioningInfo.version === 0) {
|
} else if (provisioningInfo.version === 0) {
|
||||||
this._inclusionStrategy = InclusionStrategy.Security_S2;
|
this._inclusionStrategy = InclusionStrategy.Security_S2;
|
||||||
this._startInclusion(provisioningInfo);
|
// this._startInclusion(provisioningInfo);
|
||||||
|
this._startInclusion(undefined, undefined, {
|
||||||
|
dsk: "34673-15546-46480-39591-32400-22155-07715-45994",
|
||||||
|
security_classes: [0, 1, 7],
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this._error = "This QR code is not supported";
|
this._error = "This QR code is not supported";
|
||||||
this._status = "failed";
|
this._status = "failed";
|
||||||
@@ -619,10 +632,6 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
).supported;
|
).supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _startOver(_ev: Event) {
|
|
||||||
this._startInclusion();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _startInclusion(
|
private _startInclusion(
|
||||||
qrProvisioningInformation?: QRProvisioningInformation,
|
qrProvisioningInformation?: QRProvisioningInformation,
|
||||||
qrCodeString?: string,
|
qrCodeString?: string,
|
||||||
@@ -684,9 +693,6 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
if (message.event === "interview completed") {
|
if (message.event === "interview completed") {
|
||||||
this._unsubscribe();
|
this._unsubscribe();
|
||||||
this._status = "finished";
|
this._status = "finished";
|
||||||
if (this._params?.addedCallback) {
|
|
||||||
this._params.addedCallback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.event === "interview stage completed") {
|
if (message.event === "interview stage completed") {
|
||||||
|
@@ -2,7 +2,6 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
|||||||
|
|
||||||
export interface ZWaveJSAddNodeDialogParams {
|
export interface ZWaveJSAddNodeDialogParams {
|
||||||
entry_id: string;
|
entry_id: string;
|
||||||
addedCallback?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadAddNodeDialog = () => import("./dialog-zwave_js-add-node");
|
export const loadAddNodeDialog = () => import("./dialog-zwave_js-add-node");
|
||||||
|
@@ -411,7 +411,6 @@ class ZWaveJSConfigDashboard extends LitElement {
|
|||||||
private async _addNodeClicked() {
|
private async _addNodeClicked() {
|
||||||
showZWaveJSAddNodeDialog(this, {
|
showZWaveJSAddNodeDialog(this, {
|
||||||
entry_id: this.configEntryId!,
|
entry_id: this.configEntryId!,
|
||||||
addedCallback: () => this._fetchData(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -327,9 +327,6 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
if (!("states" in item.metadata)) {
|
if (!("states" in item.metadata)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Object.keys(item.metadata.states).length !== 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!(0 in item.metadata.states) || !(1 in item.metadata.states)) {
|
if (!(0 in item.metadata.states) || !(1 in item.metadata.states)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { mdiCheckCircle, mdiCloseCircleOutline, mdiDelete } from "@mdi/js";
|
import { mdiDelete } from "@mdi/js";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -42,42 +42,17 @@ class ZWaveJSProvisioned extends LitElement {
|
|||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(narrow: boolean): DataTableColumnContainer => ({
|
(narrow: boolean): DataTableColumnContainer => ({
|
||||||
included: {
|
|
||||||
title: this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.provisioned.included"
|
|
||||||
),
|
|
||||||
type: "icon",
|
|
||||||
width: "100px",
|
|
||||||
template: (_info, provisioningEntry: any) =>
|
|
||||||
provisioningEntry.additional_properties.nodeId
|
|
||||||
? html`
|
|
||||||
<ha-svg-icon
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.provisioned.included"
|
|
||||||
)}
|
|
||||||
.path=${mdiCheckCircle}
|
|
||||||
></ha-svg-icon>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<ha-svg-icon
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.provisioned.not_included"
|
|
||||||
)}
|
|
||||||
.path=${mdiCloseCircleOutline}
|
|
||||||
></ha-svg-icon>
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
dsk: {
|
dsk: {
|
||||||
title: this.hass.localize("ui.panel.config.zwave_js.provisioned.dsk"),
|
title: this.hass.localize("ui.panel.config.zwave_js.provisioned.dsk"),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
grows: true,
|
grows: true,
|
||||||
},
|
},
|
||||||
security_classes: {
|
securityClasses: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.zwave_js.provisioned.security_classes"
|
"ui.panel.config.zwave_js.provisioned.security_classes"
|
||||||
),
|
),
|
||||||
width: "30%",
|
width: "15%",
|
||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
@@ -85,7 +60,7 @@ class ZWaveJSProvisioned extends LitElement {
|
|||||||
securityClasses
|
securityClasses
|
||||||
.map((secClass) =>
|
.map((secClass) =>
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`ui.panel.config.zwave_js.security_classes.${SecurityClass[secClass]}.title`
|
`ui.panel.config.zwave_js.security_classes.${SecurityClass[secClass]}`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.join(", "),
|
.join(", "),
|
||||||
@@ -95,7 +70,6 @@ class ZWaveJSProvisioned extends LitElement {
|
|||||||
"ui.panel.config.zwave_js.provisioned.unprovison"
|
"ui.panel.config.zwave_js.provisioned.unprovison"
|
||||||
),
|
),
|
||||||
type: "icon-button",
|
type: "icon-button",
|
||||||
width: "100px",
|
|
||||||
template: (_info, provisioningEntry: any) => html`
|
template: (_info, provisioningEntry: any) => html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@@ -123,8 +97,6 @@ class ZWaveJSProvisioned extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _unprovision = async (ev) => {
|
private _unprovision = async (ev) => {
|
||||||
const dsk = ev.currentTarget.provisioningEntry.dsk;
|
|
||||||
|
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.zwave_js.provisioned.confirm_unprovision_title"
|
"ui.panel.config.zwave_js.provisioned.confirm_unprovision_title"
|
||||||
@@ -141,8 +113,11 @@ class ZWaveJSProvisioned extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await unprovisionZwaveSmartStartNode(this.hass, this.configEntryId, dsk);
|
await unprovisionZwaveSmartStartNode(
|
||||||
this._fetchData();
|
this.hass,
|
||||||
|
this.configEntryId,
|
||||||
|
ev.currentTarget.provisioningEntry.dsk
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,6 @@ import "@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 { componentsWithService } from "../../../common/config/components_with_service";
|
import { componentsWithService } from "../../../common/config/components_with_service";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
|
||||||
import "../../../components/buttons/ha-call-service-button";
|
import "../../../components/buttons/ha-call-service-button";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import { checkCoreConfig } from "../../../data/core";
|
import { checkCoreConfig } from "../../../data/core";
|
||||||
@@ -158,20 +157,18 @@ export class HaConfigServerControl extends LitElement {
|
|||||||
"ui.panel.config.server_control.section.server_management.restart"
|
"ui.panel.config.server_control.section.server_management.restart"
|
||||||
)}
|
)}
|
||||||
</ha-call-service-button>
|
</ha-call-service-button>
|
||||||
${!isComponentLoaded(this.hass, "hassio")
|
<ha-call-service-button
|
||||||
? html`<ha-call-service-button
|
class="warning"
|
||||||
class="warning"
|
.hass=${this.hass}
|
||||||
.hass=${this.hass}
|
domain="homeassistant"
|
||||||
domain="homeassistant"
|
service="stop"
|
||||||
service="stop"
|
confirmation=${this.hass.localize(
|
||||||
confirmation=${this.hass.localize(
|
"ui.panel.config.server_control.section.server_management.confirm_stop"
|
||||||
"ui.panel.config.server_control.section.server_management.confirm_stop"
|
)}
|
||||||
)}
|
>${this.hass.localize(
|
||||||
>${this.hass.localize(
|
"ui.panel.config.server_control.section.server_management.stop"
|
||||||
"ui.panel.config.server_control.section.server_management.stop"
|
)}
|
||||||
)}
|
</ha-call-service-button>
|
||||||
</ha-call-service-button>`
|
|
||||||
: ""}
|
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
|
@@ -106,7 +106,6 @@ class DialogUserDetail extends LitElement {
|
|||||||
.dir=${computeRTLDirection(this.hass)}
|
.dir=${computeRTLDirection(this.hass)}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.disabled=${user.system_generated}
|
|
||||||
.checked=${this._localOnly}
|
.checked=${this._localOnly}
|
||||||
@change=${this._localOnlyChanged}
|
@change=${this._localOnlyChanged}
|
||||||
>
|
>
|
||||||
|
@@ -18,7 +18,6 @@ import "../../../components/ha-code-editor";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/ha-checkbox";
|
import "../../../components/ha-checkbox";
|
||||||
import "../../../components/ha-expansion-panel";
|
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||||
@@ -41,10 +40,6 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-expansion-panel {
|
|
||||||
margin: 0 8px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputs {
|
.inputs {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
@@ -140,77 +135,72 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
[[localize('ui.panel.developer-tools.tabs.states.description1')]]<br />
|
||||||
|
[[localize('ui.panel.developer-tools.tabs.states.description2')]]
|
||||||
|
</p>
|
||||||
|
<div class="state-wrapper flex layout horizontal">
|
||||||
|
<div class="inputs">
|
||||||
|
<ha-entity-picker
|
||||||
|
autofocus
|
||||||
|
hass="[[hass]]"
|
||||||
|
value="{{_entityId}}"
|
||||||
|
on-change="entityIdChanged"
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>
|
||||||
|
<paper-input
|
||||||
|
label="[[localize('ui.panel.developer-tools.tabs.states.state')]]"
|
||||||
|
required
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
spellcheck="false"
|
||||||
|
value="{{_state}}"
|
||||||
|
class="state-input"
|
||||||
|
></paper-input>
|
||||||
|
<p>
|
||||||
|
[[localize('ui.panel.developer-tools.tabs.states.state_attributes')]]
|
||||||
|
</p>
|
||||||
|
<ha-code-editor
|
||||||
|
mode="yaml"
|
||||||
|
value="[[_stateAttributes]]"
|
||||||
|
error="[[!validJSON]]"
|
||||||
|
on-value-changed="_yamlChanged"
|
||||||
|
></ha-code-editor>
|
||||||
|
<div class="button-row">
|
||||||
|
<mwc-button
|
||||||
|
on-click="handleSetState"
|
||||||
|
disabled="[[!validJSON]]"
|
||||||
|
raised
|
||||||
|
>[[localize('ui.panel.developer-tools.tabs.states.set_state')]]</mwc-button
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
on-click="entityIdChanged"
|
||||||
|
label="[[localize('ui.common.refresh')]]"
|
||||||
|
path="[[refreshIcon()]]"
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<template is="dom-if" if="[[_entity]]">
|
||||||
|
<p>
|
||||||
|
<b
|
||||||
|
>[[localize('ui.panel.developer-tools.tabs.states.last_changed')]]:</b
|
||||||
|
><br />[[lastChangedString(_entity)]]
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b
|
||||||
|
>[[localize('ui.panel.developer-tools.tabs.states.last_updated')]]:</b
|
||||||
|
><br />[[lastUpdatedString(_entity)]]
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
[[localize('ui.panel.developer-tools.tabs.states.current_entities')]]
|
[[localize('ui.panel.developer-tools.tabs.states.current_entities')]]
|
||||||
</h1>
|
</h1>
|
||||||
<ha-expansion-panel
|
|
||||||
header="Set state"
|
|
||||||
outlined
|
|
||||||
expanded="[[_expanded]]"
|
|
||||||
on-expanded-changed="expandedChanged"
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.developer-tools.tabs.states.description1')]]<br />
|
|
||||||
[[localize('ui.panel.developer-tools.tabs.states.description2')]]
|
|
||||||
</p>
|
|
||||||
<div class="state-wrapper flex layout horizontal">
|
|
||||||
<div class="inputs">
|
|
||||||
<ha-entity-picker
|
|
||||||
autofocus
|
|
||||||
hass="[[hass]]"
|
|
||||||
value="{{_entityId}}"
|
|
||||||
on-change="entityIdChanged"
|
|
||||||
allow-custom-entity
|
|
||||||
></ha-entity-picker>
|
|
||||||
<paper-input
|
|
||||||
label="[[localize('ui.panel.developer-tools.tabs.states.state')]]"
|
|
||||||
required
|
|
||||||
autocapitalize="none"
|
|
||||||
autocomplete="off"
|
|
||||||
autocorrect="off"
|
|
||||||
spellcheck="false"
|
|
||||||
value="{{_state}}"
|
|
||||||
class="state-input"
|
|
||||||
></paper-input>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.developer-tools.tabs.states.state_attributes')]]
|
|
||||||
</p>
|
|
||||||
<ha-code-editor
|
|
||||||
mode="yaml"
|
|
||||||
value="[[_stateAttributes]]"
|
|
||||||
error="[[!validJSON]]"
|
|
||||||
on-value-changed="_yamlChanged"
|
|
||||||
></ha-code-editor>
|
|
||||||
<div class="button-row">
|
|
||||||
<mwc-button
|
|
||||||
on-click="handleSetState"
|
|
||||||
disabled="[[!validJSON]]"
|
|
||||||
raised
|
|
||||||
>[[localize('ui.panel.developer-tools.tabs.states.set_state')]]</mwc-button
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
on-click="entityIdChanged"
|
|
||||||
label="[[localize('ui.common.refresh')]]"
|
|
||||||
path="[[refreshIcon()]]"
|
|
||||||
></ha-icon-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
<template is="dom-if" if="[[_entity]]">
|
|
||||||
<p>
|
|
||||||
<b
|
|
||||||
>[[localize('ui.panel.developer-tools.tabs.states.last_changed')]]:</b
|
|
||||||
><br />[[lastChangedString(_entity)]]
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<b
|
|
||||||
>[[localize('ui.panel.developer-tools.tabs.states.last_updated')]]:</b
|
|
||||||
><br />[[lastUpdatedString(_entity)]]
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table class="entities">
|
<table class="entities">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -358,11 +348,6 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
"computeEntities(hass, _entityFilter, _stateFilter, _attributeFilter)",
|
"computeEntities(hass, _entityFilter, _stateFilter, _attributeFilter)",
|
||||||
},
|
},
|
||||||
|
|
||||||
_expanded: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
narrow: {
|
narrow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
@@ -386,7 +371,6 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
this._entity = state;
|
this._entity = state;
|
||||||
this._state = state.state;
|
this._state = state.state;
|
||||||
this._stateAttributes = dump(state.attributes);
|
this._stateAttributes = dump(state.attributes);
|
||||||
this._expanded = true;
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,11 +388,6 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
this._entity = state;
|
this._entity = state;
|
||||||
this._state = state.state;
|
this._state = state.state;
|
||||||
this._stateAttributes = dump(state.attributes);
|
this._stateAttributes = dump(state.attributes);
|
||||||
this._expanded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedChanged(ev) {
|
|
||||||
this._expanded = ev.detail.expanded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entityMoreInfo(ev) {
|
entityMoreInfo(ev) {
|
||||||
|
@@ -26,7 +26,7 @@ import {
|
|||||||
rgb2hex,
|
rgb2hex,
|
||||||
rgb2lab,
|
rgb2lab,
|
||||||
} from "../../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
import {
|
import {
|
||||||
EnergyData,
|
EnergyData,
|
||||||
getEnergyDataCollection,
|
getEnergyDataCollection,
|
||||||
@@ -247,15 +247,10 @@ export class HuiEnergyGasGraphCard
|
|||||||
const data: ChartDataset<"bar" | "line">[] = [];
|
const data: ChartDataset<"bar" | "line">[] = [];
|
||||||
const entity = this.hass.states[source.stat_energy_from];
|
const entity = this.hass.states[source.stat_energy_from];
|
||||||
|
|
||||||
const modifiedColor =
|
const borderColor =
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(gasColor)), idx)))
|
||||||
? labBrighten(rgb2lab(hex2rgb(gasColor)), idx)
|
: gasColor;
|
||||||
: labDarken(rgb2lab(hex2rgb(gasColor)), idx)
|
|
||||||
: undefined;
|
|
||||||
const borderColor = modifiedColor
|
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
|
||||||
: gasColor;
|
|
||||||
|
|
||||||
let prevValue: number | null = null;
|
let prevValue: number | null = null;
|
||||||
let prevStart: string | null = null;
|
let prevStart: string | null = null;
|
||||||
|
@@ -1,3 +1,9 @@
|
|||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
ChartData,
|
ChartData,
|
||||||
ChartDataset,
|
ChartDataset,
|
||||||
@@ -11,26 +17,16 @@ import {
|
|||||||
isToday,
|
isToday,
|
||||||
startOfToday,
|
startOfToday,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { LovelaceCard } from "../../types";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { EnergySolarGraphCardConfig } from "../types";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import {
|
import {
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
lab2rgb,
|
lab2rgb,
|
||||||
rgb2hex,
|
rgb2hex,
|
||||||
rgb2lab,
|
rgb2lab,
|
||||||
} from "../../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
import { formatTime } from "../../../../common/datetime/format_time";
|
|
||||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|
||||||
import {
|
|
||||||
formatNumber,
|
|
||||||
numberFormatToLocale,
|
|
||||||
} from "../../../../common/number/format_number";
|
|
||||||
import "../../../../components/chart/ha-chart-base";
|
|
||||||
import "../../../../components/ha-card";
|
|
||||||
import {
|
import {
|
||||||
EnergyData,
|
EnergyData,
|
||||||
EnergySolarForecasts,
|
EnergySolarForecasts,
|
||||||
@@ -38,11 +34,15 @@ import {
|
|||||||
getEnergySolarForecasts,
|
getEnergySolarForecasts,
|
||||||
SolarSourceTypeEnergyPreference,
|
SolarSourceTypeEnergyPreference,
|
||||||
} from "../../../../data/energy";
|
} from "../../../../data/energy";
|
||||||
import { FrontendLocaleData } from "../../../../data/translation";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
|
import "../../../../components/chart/ha-chart-base";
|
||||||
|
import {
|
||||||
|
formatNumber,
|
||||||
|
numberFormatToLocale,
|
||||||
|
} from "../../../../common/number/format_number";
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { FrontendLocaleData } from "../../../../data/translation";
|
||||||
import { LovelaceCard } from "../../types";
|
import { formatTime } from "../../../../common/datetime/format_time";
|
||||||
import { EnergySolarGraphCardConfig } from "../types";
|
|
||||||
|
|
||||||
@customElement("hui-energy-solar-graph-card")
|
@customElement("hui-energy-solar-graph-card")
|
||||||
export class HuiEnergySolarGraphCard
|
export class HuiEnergySolarGraphCard
|
||||||
@@ -258,15 +258,10 @@ export class HuiEnergySolarGraphCard
|
|||||||
const data: ChartDataset<"bar" | "line">[] = [];
|
const data: ChartDataset<"bar" | "line">[] = [];
|
||||||
const entity = this.hass.states[source.stat_energy_from];
|
const entity = this.hass.states[source.stat_energy_from];
|
||||||
|
|
||||||
const modifiedColor =
|
const borderColor =
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx)))
|
||||||
? labBrighten(rgb2lab(hex2rgb(solarColor)), idx)
|
: solarColor;
|
||||||
: labDarken(rgb2lab(hex2rgb(solarColor)), idx)
|
|
||||||
: undefined;
|
|
||||||
const borderColor = modifiedColor
|
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
|
||||||
: solarColor;
|
|
||||||
|
|
||||||
let prevValue: number | null = null;
|
let prevValue: number | null = null;
|
||||||
let prevStart: string | null = null;
|
let prevStart: string | null = null;
|
||||||
|
@@ -17,7 +17,7 @@ import {
|
|||||||
rgb2lab,
|
rgb2lab,
|
||||||
hex2rgb,
|
hex2rgb,
|
||||||
} from "../../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import { formatNumber } from "../../../../common/number/format_number";
|
import { formatNumber } from "../../../../common/number/format_number";
|
||||||
import "../../../../components/chart/statistics-chart";
|
import "../../../../components/chart/statistics-chart";
|
||||||
@@ -170,17 +170,12 @@ export class HuiEnergySourcesTableCard
|
|||||||
this._data!.stats[source.stat_energy_from]
|
this._data!.stats[source.stat_energy_from]
|
||||||
) || 0;
|
) || 0;
|
||||||
totalSolar += energy;
|
totalSolar += energy;
|
||||||
|
const color =
|
||||||
const modifiedColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(
|
||||||
? labBrighten(rgb2lab(hex2rgb(solarColor)), idx)
|
lab2rgb(labDarken(rgb2lab(hex2rgb(solarColor)), idx))
|
||||||
: labDarken(rgb2lab(hex2rgb(solarColor)), idx)
|
)
|
||||||
: undefined;
|
: solarColor;
|
||||||
const color = modifiedColor
|
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
|
||||||
: solarColor;
|
|
||||||
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
return html`<tr class="mdc-data-table__row">
|
||||||
<td class="mdc-data-table__cell cell-bullet">
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
<div
|
<div
|
||||||
@@ -234,26 +229,22 @@ export class HuiEnergySourcesTableCard
|
|||||||
this._data!.stats[source.stat_energy_to]
|
this._data!.stats[source.stat_energy_to]
|
||||||
) || 0;
|
) || 0;
|
||||||
totalBattery += energyFrom - energyTo;
|
totalBattery += energyFrom - energyTo;
|
||||||
|
const fromColor =
|
||||||
const modifiedFromColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(
|
||||||
? labBrighten(rgb2lab(hex2rgb(batteryFromColor)), idx)
|
lab2rgb(
|
||||||
: labDarken(rgb2lab(hex2rgb(batteryFromColor)), idx)
|
labDarken(rgb2lab(hex2rgb(batteryFromColor)), idx)
|
||||||
: undefined;
|
)
|
||||||
const fromColor = modifiedFromColor
|
)
|
||||||
? rgb2hex(lab2rgb(modifiedFromColor))
|
: batteryFromColor;
|
||||||
: batteryFromColor;
|
const toColor =
|
||||||
const modifiedToColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(
|
||||||
? labBrighten(rgb2lab(hex2rgb(batteryToColor)), idx)
|
lab2rgb(
|
||||||
: labDarken(rgb2lab(hex2rgb(batteryToColor)), idx)
|
labDarken(rgb2lab(hex2rgb(batteryToColor)), idx)
|
||||||
: undefined;
|
)
|
||||||
const toColor = modifiedToColor
|
)
|
||||||
? rgb2hex(lab2rgb(modifiedToColor))
|
: batteryToColor;
|
||||||
: batteryToColor;
|
|
||||||
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
return html`<tr class="mdc-data-table__row">
|
||||||
<td class="mdc-data-table__cell cell-bullet">
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
<div
|
<div
|
||||||
@@ -340,17 +331,14 @@ export class HuiEnergySourcesTableCard
|
|||||||
if (cost !== null) {
|
if (cost !== null) {
|
||||||
totalGridCost += cost;
|
totalGridCost += cost;
|
||||||
}
|
}
|
||||||
|
const color =
|
||||||
const modifiedColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(
|
||||||
? labBrighten(rgb2lab(hex2rgb(consumptionColor)), idx)
|
lab2rgb(
|
||||||
: labDarken(rgb2lab(hex2rgb(consumptionColor)), idx)
|
labDarken(rgb2lab(hex2rgb(consumptionColor)), idx)
|
||||||
: undefined;
|
)
|
||||||
const color = modifiedColor
|
)
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
: consumptionColor;
|
||||||
: consumptionColor;
|
|
||||||
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
return html`<tr class="mdc-data-table__row">
|
||||||
<td class="mdc-data-table__cell cell-bullet">
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
<div
|
<div
|
||||||
@@ -403,17 +391,12 @@ export class HuiEnergySourcesTableCard
|
|||||||
if (cost !== null) {
|
if (cost !== null) {
|
||||||
totalGridCost += cost;
|
totalGridCost += cost;
|
||||||
}
|
}
|
||||||
|
const color =
|
||||||
const modifiedColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(
|
||||||
? labBrighten(rgb2lab(hex2rgb(returnColor)), idx)
|
lab2rgb(labDarken(rgb2lab(hex2rgb(returnColor)), idx))
|
||||||
: labDarken(rgb2lab(hex2rgb(returnColor)), idx)
|
)
|
||||||
: undefined;
|
: returnColor;
|
||||||
const color = modifiedColor
|
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
|
||||||
: returnColor;
|
|
||||||
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
return html`<tr class="mdc-data-table__row">
|
||||||
<td class="mdc-data-table__cell cell-bullet">
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
<div
|
<div
|
||||||
@@ -490,17 +473,12 @@ export class HuiEnergySourcesTableCard
|
|||||||
if (cost !== null) {
|
if (cost !== null) {
|
||||||
totalGasCost += cost;
|
totalGasCost += cost;
|
||||||
}
|
}
|
||||||
|
const color =
|
||||||
const modifiedColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(
|
||||||
? labBrighten(rgb2lab(hex2rgb(gasColor)), idx)
|
lab2rgb(labDarken(rgb2lab(hex2rgb(gasColor)), idx))
|
||||||
: labDarken(rgb2lab(hex2rgb(gasColor)), idx)
|
)
|
||||||
: undefined;
|
: gasColor;
|
||||||
const color = modifiedColor
|
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
|
||||||
: gasColor;
|
|
||||||
|
|
||||||
return html`<tr class="mdc-data-table__row">
|
return html`<tr class="mdc-data-table__row">
|
||||||
<td class="mdc-data-table__cell cell-bullet">
|
<td class="mdc-data-table__cell cell-bullet">
|
||||||
<div
|
<div
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import {
|
import {
|
||||||
addHours,
|
startOfToday,
|
||||||
differenceInDays,
|
|
||||||
endOfToday,
|
endOfToday,
|
||||||
isToday,
|
isToday,
|
||||||
startOfToday,
|
differenceInDays,
|
||||||
|
addHours,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
rgb2hex,
|
rgb2hex,
|
||||||
rgb2lab,
|
rgb2lab,
|
||||||
} from "../../../../common/color/convert-color";
|
} from "../../../../common/color/convert-color";
|
||||||
import { labBrighten, labDarken } from "../../../../common/color/lab";
|
import { labDarken } from "../../../../common/color/lab";
|
||||||
import { formatTime } from "../../../../common/datetime/format_time";
|
import { formatTime } from "../../../../common/datetime/format_time";
|
||||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import {
|
import {
|
||||||
@@ -477,16 +477,10 @@ export class HuiEnergyUsageGraphCard
|
|||||||
Object.entries(sources).forEach(([statId, source], idx) => {
|
Object.entries(sources).forEach(([statId, source], idx) => {
|
||||||
const data: ChartDataset<"bar">[] = [];
|
const data: ChartDataset<"bar">[] = [];
|
||||||
const entity = this.hass.states[statId];
|
const entity = this.hass.states[statId];
|
||||||
|
const borderColor =
|
||||||
const modifiedColor =
|
|
||||||
idx > 0
|
idx > 0
|
||||||
? this.hass.themes.darkMode
|
? rgb2hex(lab2rgb(labDarken(rgb2lab(hex2rgb(colors[type])), idx)))
|
||||||
? labBrighten(rgb2lab(hex2rgb(colors[type])), idx)
|
: colors[type];
|
||||||
: labDarken(rgb2lab(hex2rgb(colors[type])), idx)
|
|
||||||
: undefined;
|
|
||||||
const borderColor = modifiedColor
|
|
||||||
? rgb2hex(lab2rgb(modifiedColor))
|
|
||||||
: colors[type];
|
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
label:
|
label:
|
||||||
|
@@ -25,7 +25,6 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||||||
import { domainIcon } from "../../../common/entity/domain_icon";
|
import { domainIcon } from "../../../common/entity/domain_icon";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { formatNumber } from "../../../common/number/format_number";
|
import { formatNumber } from "../../../common/number/format_number";
|
||||||
import { subscribeOne } from "../../../common/util/subscribe-one";
|
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
@@ -84,11 +83,8 @@ export class HuiAreaCard
|
|||||||
return document.createElement("hui-area-card-editor");
|
return document.createElement("hui-area-card-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getStubConfig(
|
public static getStubConfig(): AreaCardConfig {
|
||||||
hass: HomeAssistant
|
return { type: "area", area: "" };
|
||||||
): Promise<AreaCardConfig> {
|
|
||||||
const areas = await subscribeOne(hass.connection, subscribeAreaRegistry);
|
|
||||||
return { type: "area", area: areas[0]?.area_id || "" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -362,12 +358,12 @@ export class HuiAreaCard
|
|||||||
});
|
});
|
||||||
|
|
||||||
let cameraEntityId: string | undefined;
|
let cameraEntityId: string | undefined;
|
||||||
if (this._config.show_camera && "camera" in entitiesByDomain) {
|
if ("camera" in entitiesByDomain) {
|
||||||
cameraEntityId = entitiesByDomain.camera[0].entity_id;
|
cameraEntityId = entitiesByDomain.camera[0].entity_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card class=${area.picture || cameraEntityId ? "image" : ""}>
|
<ha-card class=${area.picture ? "image" : ""}>
|
||||||
${area.picture || cameraEntityId
|
${area.picture || cameraEntityId
|
||||||
? html`<hui-image
|
? html`<hui-image
|
||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
|
@@ -235,9 +235,6 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
|||||||
<div>
|
<div>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.lovelace.cards.show_more_info"
|
|
||||||
)}
|
|
||||||
class="more-info"
|
class="more-info"
|
||||||
@click=${this._handleMoreInfo}
|
@click=${this._handleMoreInfo}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
@@ -19,7 +19,7 @@ import {
|
|||||||
svg,
|
svg,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { UNIT_F } from "../../../common/const";
|
import { UNIT_F } from "../../../common/const";
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
@@ -427,7 +427,6 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
@click=${this._handleAction}
|
@click=${this._handleAction}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
.path=${modeIcons[mode]}
|
.path=${modeIcons[mode]}
|
||||||
.label=${this.hass!.localize(`component.climate.state._.${mode}`)}
|
|
||||||
>
|
>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
`;
|
`;
|
||||||
|
@@ -79,7 +79,6 @@ export interface EntitiesCardConfig extends LovelaceCardConfig {
|
|||||||
export interface AreaCardConfig extends LovelaceCardConfig {
|
export interface AreaCardConfig extends LovelaceCardConfig {
|
||||||
area: string;
|
area: string;
|
||||||
navigation_path?: string;
|
navigation_path?: string;
|
||||||
show_camera?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ButtonCardConfig extends LovelaceCardConfig {
|
export interface ButtonCardConfig extends LovelaceCardConfig {
|
||||||
|
@@ -50,7 +50,6 @@ export class HuiButtonsBase extends LitElement {
|
|||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.overrideIcon=${entityConf.icon}
|
.overrideIcon=${entityConf.icon}
|
||||||
.overrideImage=${entityConf.image}
|
.overrideImage=${entityConf.image}
|
||||||
class=${name ? "" : "no-text"}
|
|
||||||
stateColor
|
stateColor
|
||||||
slot="icon"
|
slot="icon"
|
||||||
></state-badge>
|
></state-badge>
|
||||||
@@ -86,21 +85,9 @@ export class HuiButtonsBase extends LitElement {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
state-badge {
|
state-badge {
|
||||||
display: inline-flex;
|
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
|
text-align: start;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
margin-left: -4px;
|
|
||||||
margin-top: -2px;
|
|
||||||
}
|
|
||||||
state-badge.no-text {
|
|
||||||
width: 26px;
|
|
||||||
height: 26px;
|
|
||||||
margin-left: -3px;
|
|
||||||
margin-top: -3px;
|
|
||||||
}
|
}
|
||||||
ha-chip {
|
ha-chip {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
import { assert, assign, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-area-picker";
|
import "../../../../components/ha-area-picker";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
@@ -11,8 +11,6 @@ import { LovelaceCardEditor } from "../../types";
|
|||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { EditorTarget } from "../types";
|
import { EditorTarget } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import "../../../../components/ha-formfield";
|
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
|
||||||
|
|
||||||
const cardConfigStruct = assign(
|
const cardConfigStruct = assign(
|
||||||
baseLovelaceCardConfig,
|
baseLovelaceCardConfig,
|
||||||
@@ -20,7 +18,6 @@ const cardConfigStruct = assign(
|
|||||||
area: optional(string()),
|
area: optional(string()),
|
||||||
navigation_path: optional(string()),
|
navigation_path: optional(string()),
|
||||||
theme: optional(string()),
|
theme: optional(string()),
|
||||||
show_camera: optional(boolean()),
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -50,10 +47,6 @@ export class HuiAreaCardEditor
|
|||||||
return this._config!.theme || "";
|
return this._config!.theme || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
get _show_camera(): boolean {
|
|
||||||
return this._config!.show_camera || false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass || !this._config) {
|
if (!this.hass || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -71,18 +64,6 @@ export class HuiAreaCardEditor
|
|||||||
)}
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-area-picker>
|
></ha-area-picker>
|
||||||
<ha-formfield
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.lovelace.editor.card.area.show_camera"
|
|
||||||
)}
|
|
||||||
.dir=${computeRTLDirection(this.hass)}
|
|
||||||
>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this._show_camera}
|
|
||||||
.configValue=${"show_camera"}
|
|
||||||
@change=${this._valueChanged}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<paper-input
|
<paper-input
|
||||||
.label=${this.hass!.localize(
|
.label=${this.hass!.localize(
|
||||||
"ui.panel.lovelace.editor.action-editor.navigation_path"
|
"ui.panel.lovelace.editor.action-editor.navigation_path"
|
||||||
@@ -107,8 +88,7 @@ export class HuiAreaCardEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = ev.target! as EditorTarget;
|
const target = ev.target! as EditorTarget;
|
||||||
const value =
|
const value = ev.detail.value;
|
||||||
target.checked !== undefined ? target.checked : ev.detail.value;
|
|
||||||
|
|
||||||
if (this[`_${target.configValue}`] === value) {
|
if (this[`_${target.configValue}`] === value) {
|
||||||
return;
|
return;
|
||||||
|
@@ -13,7 +13,7 @@ export const getCardStubConfig = async (
|
|||||||
const elClass = await getCardElementClass(type);
|
const elClass = await getCardElementClass(type);
|
||||||
|
|
||||||
if (elClass && elClass.getStubConfig) {
|
if (elClass && elClass.getStubConfig) {
|
||||||
const classStubConfig = await elClass.getStubConfig(
|
const classStubConfig = elClass.getStubConfig(
|
||||||
hass,
|
hass,
|
||||||
entities,
|
entities,
|
||||||
entitiesFallback
|
entitiesFallback
|
||||||
|
@@ -13,7 +13,7 @@ export const getHeaderFooterStubConfig = async (
|
|||||||
const elClass = await getHeaderFooterElementClass(type);
|
const elClass = await getHeaderFooterElementClass(type);
|
||||||
|
|
||||||
if (elClass && elClass.getStubConfig) {
|
if (elClass && elClass.getStubConfig) {
|
||||||
const classStubConfig = await elClass.getStubConfig(
|
const classStubConfig = elClass.getStubConfig(
|
||||||
hass,
|
hass,
|
||||||
entities,
|
entities,
|
||||||
entitiesFallback
|
entitiesFallback
|
||||||
|
@@ -91,7 +91,6 @@ export const coreCards: Card[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "area",
|
type: "area",
|
||||||
showElement: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "conditional",
|
type: "conditional",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
import { setValue } from "../../../data/input_text";
|
import { setValue } from "../../../data/input_text";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
@@ -67,12 +67,6 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
|
|||||||
const element = this._inputEl;
|
const element = this._inputEl;
|
||||||
const stateObj = this.hass!.states[this._config!.entity];
|
const stateObj = this.hass!.states[this._config!.entity];
|
||||||
|
|
||||||
// Filter out invalid text states
|
|
||||||
if (element.value && UNAVAILABLE_STATES.includes(element.value)) {
|
|
||||||
element.value = stateObj.state;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.value !== stateObj.state) {
|
if (element.value !== stateObj.state) {
|
||||||
setValue(this.hass!, stateObj.entity_id, element.value!);
|
setValue(this.hass!, stateObj.entity_id, element.value!);
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,6 @@ import "../../../components/ha-slider";
|
|||||||
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
||||||
import {
|
import {
|
||||||
computeMediaDescription,
|
computeMediaDescription,
|
||||||
ControlButton,
|
|
||||||
MediaPlayerEntity,
|
MediaPlayerEntity,
|
||||||
SUPPORT_NEXT_TRACK,
|
SUPPORT_NEXT_TRACK,
|
||||||
SUPPORT_PAUSE,
|
SUPPORT_PAUSE,
|
||||||
@@ -109,7 +108,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entityState = stateObj.state;
|
const entityState = stateObj.state;
|
||||||
const controlButton = this._computeControlButton(stateObj);
|
|
||||||
|
|
||||||
const buttons = html`
|
const buttons = html`
|
||||||
${!this._narrow &&
|
${!this._narrow &&
|
||||||
@@ -118,9 +116,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiSkipPrevious}
|
.path=${mdiSkipPrevious}
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.card.media_player.media_previous_track"
|
|
||||||
)}
|
|
||||||
@click=${this._previousTrack}
|
@click=${this._previousTrack}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -135,10 +130,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
supportsFeature(stateObj, SUPPORT_PAUSE)))
|
supportsFeature(stateObj, SUPPORT_PAUSE)))
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${controlButton.icon}
|
.path=${this._computeControlIcon(stateObj)}
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.card.media_player.${controlButton.action}`
|
|
||||||
)}
|
|
||||||
@click=${this._playPauseStop}
|
@click=${this._playPauseStop}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -148,9 +140,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiSkipNext}
|
.path=${mdiSkipNext}
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.card.media_player.media_next_track"
|
|
||||||
)}
|
|
||||||
@click=${this._nextTrack}
|
@click=${this._nextTrack}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -173,7 +162,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiPower}
|
.path=${mdiPower}
|
||||||
.label=${this.hass.localize("ui.card.media_player.turn_on")}
|
|
||||||
@click=${this._togglePower}
|
@click=${this._togglePower}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -187,7 +175,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiPower}
|
.path=${mdiPower}
|
||||||
.label=${this.hass.localize("ui.card.media_player.turn_off")}
|
|
||||||
@click=${this._togglePower}
|
@click=${this._togglePower}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -206,13 +193,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
.path=${stateObj.attributes.is_volume_muted
|
.path=${stateObj.attributes.is_volume_muted
|
||||||
? mdiVolumeOff
|
? mdiVolumeOff
|
||||||
: mdiVolumeHigh}
|
: mdiVolumeHigh}
|
||||||
.label=${this.hass.localize(
|
|
||||||
`ui.card.media_player.${
|
|
||||||
stateObj.attributes.is_volume_muted
|
|
||||||
? "media_volume_mute"
|
|
||||||
: "media_volume_unmute"
|
|
||||||
}`
|
|
||||||
)}
|
|
||||||
@click=${this._toggleMute}
|
@click=${this._toggleMute}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -234,16 +214,10 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiVolumeMinus}
|
.path=${mdiVolumeMinus}
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.card.media_player.media_volume_down"
|
|
||||||
)}
|
|
||||||
@click=${this._volumeDown}
|
@click=${this._volumeDown}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiVolumePlus}
|
.path=${mdiVolumePlus}
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.card.media_player.media_volume_up"
|
|
||||||
)}
|
|
||||||
@click=${this._volumeUp}
|
@click=${this._volumeUp}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`
|
`
|
||||||
@@ -275,14 +249,14 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
this._veryNarrow = (this.clientWidth || 0) < 225;
|
this._veryNarrow = (this.clientWidth || 0) < 225;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeControlButton(stateObj: HassEntity): ControlButton {
|
private _computeControlIcon(stateObj: HassEntity): string {
|
||||||
return stateObj.state === "on"
|
return stateObj.state === "on"
|
||||||
? { icon: mdiPlayPause, action: "media_play_pause" }
|
? mdiPlayPause
|
||||||
: stateObj.state !== "playing"
|
: stateObj.state !== "playing"
|
||||||
? { icon: mdiPlay, action: "media_play" }
|
? mdiPlay
|
||||||
: supportsFeature(stateObj, SUPPORT_PAUSE)
|
: supportsFeature(stateObj, SUPPORT_PAUSE)
|
||||||
? { icon: mdiPause, action: "media_pause" }
|
? mdiPause
|
||||||
: { icon: mdiStop, action: "media_stop" };
|
: mdiStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _togglePower(): void {
|
private _togglePower(): void {
|
||||||
|
@@ -698,7 +698,7 @@ class HUIRoot extends LitElement {
|
|||||||
|
|
||||||
private _navigateToView(path: string | number, replace?: boolean) {
|
private _navigateToView(path: string | number, replace?: boolean) {
|
||||||
if (!this.lovelace!.editMode) {
|
if (!this.lovelace!.editMode) {
|
||||||
navigate(`${this.route!.prefix}/${path}${location.search}`, { replace });
|
navigate(`${this.route!.prefix}/${path}`, { replace });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigate(`${this.route!.prefix}/${path}?${addSearchParam({ edit: "1" })}`, {
|
navigate(`${this.route!.prefix}/${path}?${addSearchParam({ edit: "1" })}`, {
|
||||||
|
@@ -95,7 +95,6 @@ export const derivedStyles = {
|
|||||||
"mdc-theme-text-disabled-on-light": "var(--disabled-text-color)",
|
"mdc-theme-text-disabled-on-light": "var(--disabled-text-color)",
|
||||||
"mdc-theme-text-primary-on-background": "var(--primary-text-color)",
|
"mdc-theme-text-primary-on-background": "var(--primary-text-color)",
|
||||||
"mdc-theme-text-secondary-on-background": "var(--secondary-text-color)",
|
"mdc-theme-text-secondary-on-background": "var(--secondary-text-color)",
|
||||||
"mdc-theme-text-hint-on-background": "var(--secondary-text-color)",
|
|
||||||
"mdc-theme-text-icon-on-background": "var(--secondary-text-color)",
|
"mdc-theme-text-icon-on-background": "var(--secondary-text-color)",
|
||||||
"mdc-theme-error": "var(--error-color)",
|
"mdc-theme-error": "var(--error-color)",
|
||||||
"app-header-text-color": "var(--text-primary-color)",
|
"app-header-text-color": "var(--text-primary-color)",
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
import { Constructor } from "../types";
|
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
|
||||||
|
|
||||||
export const ExternalMixin = <T extends Constructor<HassBaseEl>>(
|
|
||||||
superClass: T
|
|
||||||
) =>
|
|
||||||
class extends superClass {
|
|
||||||
protected hassConnected() {
|
|
||||||
super.hassConnected();
|
|
||||||
|
|
||||||
if (this.hass!.auth.external) {
|
|
||||||
this.hass!.auth.external.connection = this.hass!.connection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@@ -6,7 +6,6 @@ import DisconnectToastMixin from "./disconnect-toast-mixin";
|
|||||||
import { hapticMixin } from "./haptic-mixin";
|
import { hapticMixin } from "./haptic-mixin";
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
import { HassBaseEl } from "./hass-base-mixin";
|
||||||
import { loggingMixin } from "./logging-mixin";
|
import { loggingMixin } from "./logging-mixin";
|
||||||
import { ExternalMixin } from "./external-mixin";
|
|
||||||
import MoreInfoMixin from "./more-info-mixin";
|
import MoreInfoMixin from "./more-info-mixin";
|
||||||
import NotificationMixin from "./notification-mixin";
|
import NotificationMixin from "./notification-mixin";
|
||||||
import { panelTitleMixin } from "./panel-title-mixin";
|
import { panelTitleMixin } from "./panel-title-mixin";
|
||||||
@@ -32,5 +31,4 @@ export class HassElement extends ext(HassBaseEl, [
|
|||||||
hapticMixin,
|
hapticMixin,
|
||||||
panelTitleMixin,
|
panelTitleMixin,
|
||||||
loggingMixin,
|
loggingMixin,
|
||||||
ExternalMixin,
|
|
||||||
]) {}
|
]) {}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import tinykeys from "tinykeys";
|
import tinykeys from "tinykeys";
|
||||||
|
import { showDeveloperToolDialog } from "../dialogs/developert-tools/show-dialog-developer-tools";
|
||||||
import {
|
import {
|
||||||
QuickBarParams,
|
QuickBarParams,
|
||||||
showQuickBar,
|
showQuickBar,
|
||||||
@@ -32,6 +33,7 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
|||||||
tinykeys(window, {
|
tinykeys(window, {
|
||||||
e: (ev) => this._showQuickBar(ev),
|
e: (ev) => this._showQuickBar(ev),
|
||||||
c: (ev) => this._showQuickBar(ev, true),
|
c: (ev) => this._showQuickBar(ev, true),
|
||||||
|
d: () => showDeveloperToolDialog(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -131,7 +131,5 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
|||||||
(themeMeta.getAttribute("default-content") as string);
|
(themeMeta.getAttribute("default-content") as string);
|
||||||
themeMeta.setAttribute("content", themeColor);
|
themeMeta.setAttribute("content", themeColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hass!.auth.external?.fireMessage({ type: "theme-update" });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -195,14 +195,8 @@
|
|||||||
"turn_off": "Turn off",
|
"turn_off": "Turn off",
|
||||||
"media_play": "Play",
|
"media_play": "Play",
|
||||||
"media_play_pause": "Play/pause",
|
"media_play_pause": "Play/pause",
|
||||||
"media_pause": "Pause",
|
"media_next_track": "Next",
|
||||||
"media_stop": "Stop",
|
"media_previous_track": "Previous",
|
||||||
"media_next_track": "Next track",
|
|
||||||
"media_previous_track": "Previous track",
|
|
||||||
"media_volume_up": "Volume up",
|
|
||||||
"media_volume_down": "Volume down",
|
|
||||||
"media_volume_mute": "Volume mute",
|
|
||||||
"media_volume_unmute": "Volume unmute",
|
|
||||||
"text_to_speak": "Text to speak"
|
"text_to_speak": "Text to speak"
|
||||||
},
|
},
|
||||||
"persistent_notification": {
|
"persistent_notification": {
|
||||||
@@ -297,14 +291,12 @@
|
|||||||
"undo": "Undo",
|
"undo": "Undo",
|
||||||
"move": "Move",
|
"move": "Move",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"submit": "Submit",
|
|
||||||
"rename": "Rename",
|
"rename": "Rename",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"not_now": "Not now",
|
"not_now": "Not now",
|
||||||
"skip": "Skip",
|
"skip": "Skip",
|
||||||
"menu": "Menu",
|
"menu": "Menu",
|
||||||
"overflow_menu": "Overflow menu",
|
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"successfully_saved": "Successfully saved",
|
"successfully_saved": "Successfully saved",
|
||||||
"successfully_deleted": "Successfully deleted",
|
"successfully_deleted": "Successfully deleted",
|
||||||
@@ -429,7 +421,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"related-filter-menu": {
|
"related-filter-menu": {
|
||||||
"filter": "Filter",
|
|
||||||
"filter_by_entity": "Filter by entity",
|
"filter_by_entity": "Filter by entity",
|
||||||
"filter_by_device": "Filter by device",
|
"filter_by_device": "Filter by device",
|
||||||
"filter_by_area": "Filter by area",
|
"filter_by_area": "Filter by area",
|
||||||
@@ -547,13 +538,6 @@
|
|||||||
},
|
},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"expansion_header": "Attributes"
|
"expansion_header": "Attributes"
|
||||||
},
|
|
||||||
"qr-scanner": {
|
|
||||||
"select_camera": "Select camera",
|
|
||||||
"only_https_supported": "You can only use your camera to scan a QR code when using HTTPS.",
|
|
||||||
"not_supported": "Your browser doesn't support QR scanning.",
|
|
||||||
"manual_input": "You can scan the QR code with another QR scanner and paste the code in the input below",
|
|
||||||
"enter_qr_code": "Enter QR code value"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
@@ -694,8 +678,8 @@
|
|||||||
"open_cover": "Open cover",
|
"open_cover": "Open cover",
|
||||||
"close_cover": "Close cover",
|
"close_cover": "Close cover",
|
||||||
"open_tilt_cover": "Open cover tilt",
|
"open_tilt_cover": "Open cover tilt",
|
||||||
"close_tilt_cover": "Close cover tilt",
|
"close_tile_cover": "Close cover tilt",
|
||||||
"stop_cover": "Stop cover"
|
"stop_cover": "Stop cover from moving"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity_registry": {
|
"entity_registry": {
|
||||||
@@ -894,7 +878,8 @@
|
|||||||
"key_missing": "Required key ''{key}'' is missing.",
|
"key_missing": "Required key ''{key}'' is missing.",
|
||||||
"key_not_expected": "Key ''{key}'' is not expected or not supported by the visual editor.",
|
"key_not_expected": "Key ''{key}'' is not expected or not supported by the visual editor.",
|
||||||
"key_wrong_type": "The provided value for ''{key}'' is not supported by the visual editor. We support ({type_correct}) but received ({type_wrong}).",
|
"key_wrong_type": "The provided value for ''{key}'' is not supported by the visual editor. We support ({type_correct}) but received ({type_wrong}).",
|
||||||
"no_template_editor_support": "Templates not supported in visual editor"
|
"no_template_editor_support": "Templates not supported in visual editor",
|
||||||
|
"no_state_array_support": "Multiple state values not supported in visual editor"
|
||||||
},
|
},
|
||||||
"supervisor": {
|
"supervisor": {
|
||||||
"title": "Could not load the Supervisor panel!",
|
"title": "Could not load the Supervisor panel!",
|
||||||
@@ -928,7 +913,6 @@
|
|||||||
"dismiss": "Dismiss"
|
"dismiss": "Dismiss"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"external_app_configuration": "App Configuration",
|
|
||||||
"sidebar_toggle": "Sidebar Toggle",
|
"sidebar_toggle": "Sidebar Toggle",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
"hide_panel": "Hide panel",
|
"hide_panel": "Hide panel",
|
||||||
@@ -945,47 +929,9 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"header": "Configure Home Assistant",
|
"header": "Configure Home Assistant",
|
||||||
"dashboard": {
|
"advanced_mode": {
|
||||||
"devices": {
|
"hint_enable": "Missing config options? Enable advanced mode on",
|
||||||
"title": "Devices & Services",
|
"link_profile_page": "your profile page"
|
||||||
"description": "Integrations, devices, entities and areas"
|
|
||||||
},
|
|
||||||
"automations": {
|
|
||||||
"title": "Automations & Scenes",
|
|
||||||
"description": "Manage automations, scenes, scripts and helpers"
|
|
||||||
},
|
|
||||||
"blueprints": {
|
|
||||||
"title": "Blueprints",
|
|
||||||
"description": "Pre-made automations and scripts by the community"
|
|
||||||
},
|
|
||||||
"supervisor": {
|
|
||||||
"title": "Add-ons, Backups & Supervisor",
|
|
||||||
"description": "Create backups, check logs or reboot your system"
|
|
||||||
},
|
|
||||||
"dashboards": {
|
|
||||||
"title": "Dashboards",
|
|
||||||
"description": "Create customized sets of cards to control your home"
|
|
||||||
},
|
|
||||||
"energy": {
|
|
||||||
"title": "Energy",
|
|
||||||
"description": "Monitor your energy production and consumption"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"title": "Tags",
|
|
||||||
"description": "Trigger automations when a NFC tag, QR code, etc. is scanned"
|
|
||||||
},
|
|
||||||
"people": {
|
|
||||||
"title": "People & Zones",
|
|
||||||
"description": "Manage the people and zones that Home Assistant tracks"
|
|
||||||
},
|
|
||||||
"companion": {
|
|
||||||
"title": "Companion App",
|
|
||||||
"description": "Location and notifications"
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"title": "Settings",
|
|
||||||
"description": "Basic settings, server controls, logs and info"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"editor": {
|
"editor": {
|
||||||
@@ -1163,7 +1109,7 @@
|
|||||||
"cost_number": "Use a static price",
|
"cost_number": "Use a static price",
|
||||||
"cost_number_input": "Price per {unit}",
|
"cost_number_input": "Price per {unit}",
|
||||||
"gas_usage": "Gas usage",
|
"gas_usage": "Gas usage",
|
||||||
"m3_or_kWh": "ft³, m³, Wh, kWh or MWh"
|
"m3_or_kWh": "m³ or kWh"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"device_consumption": {
|
"device_consumption": {
|
||||||
@@ -1204,19 +1150,19 @@
|
|||||||
},
|
},
|
||||||
"entity_unexpected_unit_energy": {
|
"entity_unexpected_unit_energy": {
|
||||||
"title": "Unexpected unit of measurement",
|
"title": "Unexpected unit of measurement",
|
||||||
"description": "The following entities do not have the expected units of measurement 'Wh', 'kWh' or 'MWh':"
|
"description": "The following entities do not have the expected units of measurement 'kWh' or 'Wh':"
|
||||||
},
|
},
|
||||||
"entity_unexpected_unit_gas": {
|
"entity_unexpected_unit_gas": {
|
||||||
"title": "Unexpected unit of measurement",
|
"title": "Unexpected unit of measurement",
|
||||||
"description": "The following entities do not have the expected units of measurement 'Wh', 'kWh' or 'MWh' for an energy sensor or 'm³' or 'ft³' for a gas sensor:"
|
"description": "The following entities do not have the expected units of measurement 'kWh', 'm³' or 'ft³':"
|
||||||
},
|
},
|
||||||
"entity_unexpected_unit_energy_price": {
|
"entity_unexpected_unit_energy_price": {
|
||||||
"title": "Unexpected unit of measurement",
|
"title": "Unexpected unit of measurement",
|
||||||
"description": "The following entities do not have the expected units of measurement ''{currency}/kWh'', ''{currency}/Wh'' or ''{currency}/MWh'':"
|
"description": "The following entities do not have the expected units of measurement ''{currency}/kWh'' or ''{currency}/Wh'':"
|
||||||
},
|
},
|
||||||
"entity_unexpected_unit_gas_price": {
|
"entity_unexpected_unit_gas_price": {
|
||||||
"title": "Unexpected unit of measurement",
|
"title": "Unexpected unit of measurement",
|
||||||
"description": "The following entities do not have the expected units of measurement ''{currency}/kWh'', ''{currency}/Wh'', ''{currency}/MWh'', ''{currency}/m³'' or ''{currency}/ft³'':"
|
"description": "The following entities do not have the expected units of measurement ''{currency}/kWh'', ''{currency}/Wh'', ''{currency}/m³'' or ''{currency}/ft³'':"
|
||||||
},
|
},
|
||||||
"entity_unexpected_state_class": {
|
"entity_unexpected_state_class": {
|
||||||
"title": "Unexpected state class",
|
"title": "Unexpected state class",
|
||||||
@@ -1568,7 +1514,7 @@
|
|||||||
"extra_fields": {
|
"extra_fields": {
|
||||||
"above": "Above",
|
"above": "Above",
|
||||||
"below": "Below",
|
"below": "Below",
|
||||||
"for": "Duration (optional)",
|
"for": "Duration",
|
||||||
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]"
|
"zone": "[%key:ui::panel::config::automation::editor::triggers::type::zone::label%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1591,9 +1537,9 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"label": "State",
|
"label": "State",
|
||||||
"attribute": "Attribute (optional)",
|
"attribute": "Attribute (optional)",
|
||||||
"from": "From (optional)",
|
"from": "From",
|
||||||
"for": "For (optional)",
|
"for": "For",
|
||||||
"to": "To (optional)"
|
"to": "To"
|
||||||
},
|
},
|
||||||
"homeassistant": {
|
"homeassistant": {
|
||||||
"label": "Home Assistant",
|
"label": "Home Assistant",
|
||||||
@@ -2444,7 +2390,6 @@
|
|||||||
"manuf": "by {manufacturer}",
|
"manuf": "by {manufacturer}",
|
||||||
"via": "Connected via",
|
"via": "Connected via",
|
||||||
"firmware": "Firmware: {version}",
|
"firmware": "Firmware: {version}",
|
||||||
"hardware": "Hardware: {version}",
|
|
||||||
"unnamed_entry": "Unnamed entry",
|
"unnamed_entry": "Unnamed entry",
|
||||||
"unknown_via_device": "Unknown device",
|
"unknown_via_device": "Unknown device",
|
||||||
"area": "In {area}",
|
"area": "In {area}",
|
||||||
@@ -2533,7 +2478,7 @@
|
|||||||
"admin": "Administrator",
|
"admin": "Administrator",
|
||||||
"group": "Group",
|
"group": "Group",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
"local_only": "Can only log in from the local network",
|
"local_only": "Can only login from the local network",
|
||||||
"system_generated": "System generated",
|
"system_generated": "System generated",
|
||||||
"system_generated_users_not_removable": "Unable to remove system generated users.",
|
"system_generated_users_not_removable": "Unable to remove system generated users.",
|
||||||
"system_generated_users_not_editable": "Unable to update system generated users.",
|
"system_generated_users_not_editable": "Unable to update system generated users.",
|
||||||
@@ -2869,7 +2814,7 @@
|
|||||||
"node_id": "Device ID",
|
"node_id": "Device ID",
|
||||||
"home_id": "Home ID",
|
"home_id": "Home ID",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"back": "Back",
|
"close": "Close",
|
||||||
"add_node": "Add device",
|
"add_node": "Add device",
|
||||||
"remove_node": "Remove device",
|
"remove_node": "Remove device",
|
||||||
"reconfigure_server": "Re-configure Server",
|
"reconfigure_server": "Re-configure Server",
|
||||||
@@ -2938,6 +2883,8 @@
|
|||||||
"qr_code": "QR Code",
|
"qr_code": "QR Code",
|
||||||
"qr_code_paragraph": "If your device supports SmartStart you can scan the QR code for easy pairing.",
|
"qr_code_paragraph": "If your device supports SmartStart you can scan the QR code for easy pairing.",
|
||||||
"scan_qr_code": "Scan QR code",
|
"scan_qr_code": "Scan QR code",
|
||||||
|
"enter_qr_code": "Enter QR code value",
|
||||||
|
"select_camera": "Select camera",
|
||||||
"inclusion_failed": "The device could not be added.",
|
"inclusion_failed": "The device could not be added.",
|
||||||
"check_logs": "Please check the logs for more information.",
|
"check_logs": "Please check the logs for more information.",
|
||||||
"inclusion_finished": "The device has been added.",
|
"inclusion_finished": "The device has been added.",
|
||||||
@@ -2950,8 +2897,6 @@
|
|||||||
"dsk": "DSK",
|
"dsk": "DSK",
|
||||||
"security_classes": "Security classes",
|
"security_classes": "Security classes",
|
||||||
"unprovison": "Unprovison",
|
"unprovison": "Unprovison",
|
||||||
"included": "Included",
|
|
||||||
"not_included": "Not Included",
|
|
||||||
"confirm_unprovision_title": "Are you sure you want to unprovision the device?",
|
"confirm_unprovision_title": "Are you sure you want to unprovision the device?",
|
||||||
"confirm_unprovision_text": "If you unprovision the device it will not be added to Home Assistant when it is powered on. If it is already added to Home Assistant, removing the provisioned device will not remove it from Home Assistant."
|
"confirm_unprovision_text": "If you unprovision the device it will not be added to Home Assistant when it is powered on. If it is already added to Home Assistant, removing the provisioned device will not remove it from Home Assistant."
|
||||||
},
|
},
|
||||||
@@ -3031,8 +2976,7 @@
|
|||||||
"title": "Z-Wave JS Logs",
|
"title": "Z-Wave JS Logs",
|
||||||
"log_level": "Log Level",
|
"log_level": "Log Level",
|
||||||
"subscribed_to_logs": "Subscribed to Z-Wave JS Log Messages…",
|
"subscribed_to_logs": "Subscribed to Z-Wave JS Log Messages…",
|
||||||
"log_level_changed": "Log Level changed to: {level}",
|
"log_level_changed": "Log Level changed to: {level}"
|
||||||
"download_logs": "Download logs"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3311,8 +3255,7 @@
|
|||||||
},
|
},
|
||||||
"area": {
|
"area": {
|
||||||
"name": "Area",
|
"name": "Area",
|
||||||
"description": "The Area card automatically displays entities of a specific area.",
|
"description": "The Area card automatically displays entities of a specific area."
|
||||||
"show_camera": "Show camera feed instead of area picture"
|
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"name": "Calendar",
|
"name": "Calendar",
|
||||||
@@ -4277,8 +4220,7 @@
|
|||||||
"create_backup": "Create backup before updating",
|
"create_backup": "Create backup before updating",
|
||||||
"description": "You have {version} installed. Click update to update to version {newest_version}",
|
"description": "You have {version} installed. Click update to update to version {newest_version}",
|
||||||
"updating": "Updating {name} to version {version}",
|
"updating": "Updating {name} to version {version}",
|
||||||
"creating_backup": "Creating backup of {name}",
|
"creating_backup": "Creating backup of {name}"
|
||||||
"no_update": "No update available for {name}"
|
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"restart": {
|
"restart": {
|
||||||
|
@@ -2,16 +2,16 @@ import { assert } from "chai";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ExternalMessaging,
|
ExternalMessaging,
|
||||||
EMMessage,
|
InternalMessage,
|
||||||
} from "../../src/external_app/external_messaging";
|
} from "../../src/external_app/external_messaging";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
global.__DEV__ = true;
|
global.__DEV__ = true;
|
||||||
|
|
||||||
class MockExternalMessaging extends ExternalMessaging {
|
class MockExternalMessaging extends ExternalMessaging {
|
||||||
public mockSent: EMMessage[] = [];
|
public mockSent: InternalMessage[] = [];
|
||||||
|
|
||||||
protected _sendExternal(msg: EMMessage) {
|
protected _sendExternal(msg: InternalMessage) {
|
||||||
this.mockSent.push(msg);
|
this.mockSent.push(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user