mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-29 12:16:39 +00:00
Add related items to device info (#4637)
* Add related items to device info * Update ha-config-device-page.ts * remove log * Lint * Fix dialog logic showing triggers on script dialog Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
036eedc69d
commit
65994e7280
@ -47,12 +47,9 @@ export class HaChips extends LitElement {
|
||||
}
|
||||
|
||||
private _handleClick(ev) {
|
||||
fireEvent(
|
||||
this,
|
||||
"chip-clicked",
|
||||
{ index: ev.target.closest("button").index },
|
||||
{ bubbles: false }
|
||||
);
|
||||
fireEvent(this, "chip-clicked", {
|
||||
index: ev.target.closest("button").index,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
} from "home-assistant-js-websocket";
|
||||
|
||||
import { HomeAssistant, ServiceCallResponse } from "../types";
|
||||
import { navigate } from "../common/navigate";
|
||||
|
||||
export const SCENE_IGNORED_DOMAINS = [
|
||||
"sensor",
|
||||
@ -18,6 +19,22 @@ export const SCENE_IGNORED_DOMAINS = [
|
||||
"zone",
|
||||
];
|
||||
|
||||
let inititialSceneEditorData: Partial<SceneConfig> | undefined;
|
||||
|
||||
export const showSceneEditor = (
|
||||
el: HTMLElement,
|
||||
data?: Partial<SceneConfig>
|
||||
) => {
|
||||
inititialSceneEditorData = data;
|
||||
navigate(el, "/config/scene/edit/new");
|
||||
};
|
||||
|
||||
export const getSceneEditorInitData = () => {
|
||||
const data = inititialSceneEditorData;
|
||||
inititialSceneEditorData = undefined;
|
||||
return data;
|
||||
};
|
||||
|
||||
export interface SceneEntity extends HassEntityBase {
|
||||
attributes: HassEntityAttributeBase & { id?: string };
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
HassEntityBase,
|
||||
HassEntityAttributeBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { navigate } from "../common/navigate";
|
||||
|
||||
export interface ScriptEntity extends HassEntityBase {
|
||||
attributes: HassEntityAttributeBase & {
|
||||
@ -65,3 +66,19 @@ export const triggerScript = (
|
||||
|
||||
export const deleteScript = (hass: HomeAssistant, objectId: string) =>
|
||||
hass.callApi("DELETE", `config/script/config/${objectId}`);
|
||||
|
||||
let inititialScriptEditorData: Partial<ScriptConfig> | undefined;
|
||||
|
||||
export const showScriptEditor = (
|
||||
el: HTMLElement,
|
||||
data?: Partial<ScriptConfig>
|
||||
) => {
|
||||
inititialScriptEditorData = data;
|
||||
navigate(el, "/config/script/new");
|
||||
};
|
||||
|
||||
export const getScriptEditorInitData = () => {
|
||||
const data = inititialScriptEditorData;
|
||||
inititialScriptEditorData = undefined;
|
||||
return data;
|
||||
};
|
||||
|
@ -5,12 +5,14 @@ import { DeviceAutomation } from "../../../../data/device_automation";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-chips";
|
||||
import { showAutomationEditor } from "../../../../data/automation";
|
||||
import { showScriptEditor } from "../../../../data/script";
|
||||
|
||||
export abstract class HaDeviceAutomationCard<
|
||||
T extends DeviceAutomation
|
||||
> extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public deviceId?: string;
|
||||
@property() public script = false;
|
||||
@property() public automations: T[] = [];
|
||||
|
||||
protected headerKey = "";
|
||||
@ -46,20 +48,18 @@ export abstract class HaDeviceAutomationCard<
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
${this.hass.localize(this.headerKey)}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<ha-chips
|
||||
@chip-clicked=${this._handleAutomationClicked}
|
||||
.items=${this.automations.map((automation) =>
|
||||
this._localizeDeviceAutomation(this.hass, automation)
|
||||
)}
|
||||
>
|
||||
</ha-chips>
|
||||
</div>
|
||||
</ha-card>
|
||||
<h3>
|
||||
${this.hass.localize(this.headerKey)}
|
||||
</h3>
|
||||
<div class="content">
|
||||
<ha-chips
|
||||
@chip-clicked=${this._handleAutomationClicked}
|
||||
.items=${this.automations.map((automation) =>
|
||||
this._localizeDeviceAutomation(this.hass, automation)
|
||||
)}
|
||||
>
|
||||
</ha-chips>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -68,6 +68,10 @@ export abstract class HaDeviceAutomationCard<
|
||||
if (!automation) {
|
||||
return;
|
||||
}
|
||||
if (this.script) {
|
||||
showScriptEditor(this, { sequence: [automation] });
|
||||
return;
|
||||
}
|
||||
const data = {};
|
||||
data[this.type] = [automation];
|
||||
showAutomationEditor(this, data);
|
||||
|
@ -0,0 +1,184 @@
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
css,
|
||||
CSSResult,
|
||||
TemplateResult,
|
||||
property,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
|
||||
import "../../../../components/ha-dialog";
|
||||
import "./ha-device-triggers-card";
|
||||
import "./ha-device-conditions-card";
|
||||
import "./ha-device-actions-card";
|
||||
import { DeviceAutomationDialogParams } from "./show-dialog-device-automation";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
DeviceTrigger,
|
||||
DeviceCondition,
|
||||
DeviceAction,
|
||||
fetchDeviceTriggers,
|
||||
fetchDeviceConditions,
|
||||
fetchDeviceActions,
|
||||
} from "../../../../data/device_automation";
|
||||
|
||||
@customElement("dialog-device-automation")
|
||||
export class DialogDeviceAutomation extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() private _triggers: DeviceTrigger[] = [];
|
||||
@property() private _conditions: DeviceCondition[] = [];
|
||||
@property() private _actions: DeviceAction[] = [];
|
||||
@property() private _params?: DeviceAutomationDialogParams;
|
||||
|
||||
public async showDialog(params: DeviceAutomationDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
protected updated(changedProps): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (!changedProps.has("_params")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._triggers = [];
|
||||
this._conditions = [];
|
||||
this._actions = [];
|
||||
|
||||
if (!this._params) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { deviceId, script } = this._params;
|
||||
|
||||
fetchDeviceActions(this.hass, deviceId).then(
|
||||
(actions) => (this._actions = actions)
|
||||
);
|
||||
if (script) {
|
||||
return;
|
||||
}
|
||||
fetchDeviceTriggers(this.hass, deviceId).then(
|
||||
(triggers) => (this._triggers = triggers)
|
||||
);
|
||||
fetchDeviceConditions(this.hass, deviceId).then(
|
||||
(conditions) => (this._conditions = conditions)
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closing="${this._close}"
|
||||
.title=${this.hass.localize(
|
||||
`ui.panel.config.devices.${
|
||||
this._params.script ? "script" : "automation"
|
||||
}.create`
|
||||
)}
|
||||
>
|
||||
<div @chip-clicked=${this._close}>
|
||||
${this._triggers.length ||
|
||||
this._conditions.length ||
|
||||
this._actions.length
|
||||
? html`
|
||||
${this._triggers.length
|
||||
? html`
|
||||
<ha-device-triggers-card
|
||||
.hass=${this.hass}
|
||||
.automations=${this._triggers}
|
||||
></ha-device-triggers-card>
|
||||
`
|
||||
: ""}
|
||||
${this._conditions.length
|
||||
? html`
|
||||
<ha-device-conditions-card
|
||||
.hass=${this.hass}
|
||||
.automations=${this._conditions}
|
||||
></ha-device-conditions-card>
|
||||
`
|
||||
: ""}
|
||||
${this._actions.length
|
||||
? html`
|
||||
<ha-device-actions-card
|
||||
.hass=${this.hass}
|
||||
.automations=${this._actions}
|
||||
.script=${this._params.script}
|
||||
></ha-device-actions-card>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: html``}
|
||||
</div>
|
||||
<mwc-button slot="primaryAction" @click="${this._close}">
|
||||
Close
|
||||
</mwc-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._params = undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
||||
ha-dialog {
|
||||
--mdc-dialog-title-ink-color: var(--primary-text-color);
|
||||
--justify-action-buttons: space-between;
|
||||
}
|
||||
@media only screen and (min-width: 600px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: 600px;
|
||||
}
|
||||
}
|
||||
.form {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
.location {
|
||||
display: flex;
|
||||
}
|
||||
.location > * {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.location > *:first-child {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.location > *:last-child {
|
||||
margin-left: 4px;
|
||||
}
|
||||
ha-location-editor {
|
||||
margin-top: 16px;
|
||||
}
|
||||
ha-user-picker {
|
||||
margin-top: 16px;
|
||||
}
|
||||
mwc-button.warning {
|
||||
--mdc-theme-primary: var(--google-red-500);
|
||||
}
|
||||
.error {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
p {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-device-automation": DialogDeviceAutomation;
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
import "../../../../components/ha-card";
|
||||
|
||||
import {
|
||||
DeviceRegistryEntry,
|
||||
computeDeviceName,
|
||||
@ -27,63 +25,63 @@ export class HaDeviceCard extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="info">
|
||||
<div class="model">${this.device.model}</div>
|
||||
${this.device.manufacturer
|
||||
? html`
|
||||
<div class="manuf">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.manuf",
|
||||
"manufacturer",
|
||||
this.device.manufacturer
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.device.area_id
|
||||
? html`
|
||||
<div class="area">
|
||||
<div class="extra-info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.area",
|
||||
"area",
|
||||
this._computeArea(this.areas, this.device)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
${this.device.via_device_id
|
||||
? html`
|
||||
<div class="info">
|
||||
${this.device.model
|
||||
? html`
|
||||
<div class="model">${this.device.model}</div>
|
||||
`
|
||||
: ""}
|
||||
${this.device.manufacturer
|
||||
? html`
|
||||
<div class="manuf">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.manuf",
|
||||
"manufacturer",
|
||||
this.device.manufacturer
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.device.area_id
|
||||
? html`
|
||||
<div class="area">
|
||||
<div class="extra-info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.via"
|
||||
)}
|
||||
<span class="hub"
|
||||
>${this._computeDeviceName(
|
||||
this.devices,
|
||||
this.device.via_device_id
|
||||
)}</span
|
||||
>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.device.sw_version
|
||||
? html`
|
||||
<div class="extra-info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.firmware",
|
||||
"version",
|
||||
this.device.sw_version
|
||||
"ui.panel.config.integrations.config_entry.area",
|
||||
"area",
|
||||
this._computeArea(this.areas, this.device)
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.device.via_device_id
|
||||
? html`
|
||||
<div class="extra-info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.via"
|
||||
)}
|
||||
<span class="hub"
|
||||
>${this._computeDeviceName(
|
||||
this.devices,
|
||||
this.device.via_device_id
|
||||
)}</span
|
||||
>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.device.sw_version
|
||||
? html`
|
||||
<div class="extra-info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.firmware",
|
||||
"version",
|
||||
this.device.sw_version
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,9 @@ import {
|
||||
customElement,
|
||||
css,
|
||||
CSSResult,
|
||||
queryAll,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
||||
@ -19,15 +20,13 @@ import "@polymer/paper-item/paper-item-body";
|
||||
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-switch";
|
||||
import { showEntityRegistryDetailDialog } from "../../entities/show-dialog-entity-registry-detail";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
// tslint:disable-next-line
|
||||
import { HaSwitch } from "../../../../components/ha-switch";
|
||||
import { EntityRegistryStateEntry } from "../ha-config-device-page";
|
||||
import { addEntitiesToLovelaceView } from "../../../lovelace/editor/add-entities-to-view";
|
||||
import { createRowElement } from "../../../lovelace/create-element/create-row-element";
|
||||
import { LovelaceRow } from "../../../lovelace/entity-rows/types";
|
||||
|
||||
@customElement("ha-device-entities-card")
|
||||
export class HaDeviceEntitiesCard extends LitElement {
|
||||
@ -36,66 +35,61 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
@property() public entities!: EntityRegistryStateEntry[];
|
||||
@property() public narrow!: boolean;
|
||||
@property() private _showDisabled = false;
|
||||
@queryAll("#entities > *") private _entityRows?: LovelaceRow[];
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
if (!changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
this._entityRows?.forEach((element) => {
|
||||
element.hass = this.hass;
|
||||
});
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const disabledEntities: EntityRegistryStateEntry[] = [];
|
||||
return html`
|
||||
<ha-card>
|
||||
<paper-item>
|
||||
<ha-switch
|
||||
.checked=${this._showDisabled}
|
||||
@change=${this._showDisabledChanged}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_disabled"
|
||||
)}
|
||||
</ha-switch>
|
||||
</paper-item>
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.devices.entities.entities"
|
||||
)}
|
||||
>
|
||||
${this.entities.length
|
||||
? html`
|
||||
${this.entities.map((entry: EntityRegistryStateEntry) => {
|
||||
if (!this._showDisabled && entry.disabled_by) {
|
||||
return "";
|
||||
}
|
||||
const stateObj = this.hass.states[entry.entity_id];
|
||||
return html`
|
||||
<paper-icon-item
|
||||
.entry=${entry}
|
||||
class=${classMap({ "disabled-entry": !!entry.disabled_by })}
|
||||
>
|
||||
${stateObj
|
||||
? html`
|
||||
<state-badge
|
||||
@click=${this._openMoreInfo}
|
||||
.stateObj=${stateObj}
|
||||
slot="item-icon"
|
||||
></state-badge>
|
||||
`
|
||||
: html`
|
||||
<ha-icon
|
||||
slot="item-icon"
|
||||
.icon=${domainIcon(computeDomain(entry.entity_id))}
|
||||
></ha-icon>
|
||||
`}
|
||||
<paper-item-body two-line @click=${this._openMoreInfo}>
|
||||
<div class="name">${entry.stateName}</div>
|
||||
<div class="secondary entity-id">${entry.entity_id}</div>
|
||||
</paper-item-body>
|
||||
<div class="buttons">
|
||||
${stateObj
|
||||
? html`
|
||||
<paper-icon-button
|
||||
@click=${this._openMoreInfo}
|
||||
icon="hass:information-outline"
|
||||
></paper-icon-button>
|
||||
`
|
||||
: ""}
|
||||
<paper-icon-button
|
||||
@click=${this._openEditEntry}
|
||||
icon="hass:settings"
|
||||
></paper-icon-button>
|
||||
</div>
|
||||
</paper-icon-item>
|
||||
`;
|
||||
})}
|
||||
<div id="entities">
|
||||
${this.entities.map((entry: EntityRegistryStateEntry) => {
|
||||
if (entry.disabled_by) {
|
||||
disabledEntities.push(entry);
|
||||
return "";
|
||||
}
|
||||
return this.hass.states[entry.entity_id]
|
||||
? this._renderEntity(entry)
|
||||
: this._renderEntry(entry);
|
||||
})}
|
||||
</div>
|
||||
${disabledEntities.length
|
||||
? !this._showDisabled
|
||||
? html`
|
||||
<button
|
||||
class="show-more"
|
||||
@click=${this._toggleShowDisabled}
|
||||
>
|
||||
+${disabledEntities.length} disabled entities
|
||||
</button>
|
||||
`
|
||||
: html`
|
||||
${disabledEntities.map((entry) =>
|
||||
this._renderEntry(entry)
|
||||
)}
|
||||
<button
|
||||
class="show-more"
|
||||
@click=${this._toggleShowDisabled}
|
||||
>
|
||||
Hide disabled
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._addToLovelaceView}>
|
||||
${this.hass.localize(
|
||||
@ -119,22 +113,48 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _showDisabledChanged(ev: Event) {
|
||||
this._showDisabled = (ev.target as HaSwitch).checked;
|
||||
private _toggleShowDisabled() {
|
||||
this._showDisabled = !this._showDisabled;
|
||||
}
|
||||
|
||||
private _openEditEntry(ev: MouseEvent): void {
|
||||
const entry = (ev.currentTarget! as any).closest("paper-icon-item").entry;
|
||||
private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult {
|
||||
const element = createRowElement({ entity: entry.entity_id });
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
}
|
||||
// @ts-ignore
|
||||
element.entry = entry;
|
||||
element.addEventListener("hass-more-info", (ev) => this._openEditEntry(ev));
|
||||
|
||||
return html`
|
||||
<div>${element}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderEntry(entry: EntityRegistryStateEntry): TemplateResult {
|
||||
return html`
|
||||
<paper-icon-item .entry=${entry} @click=${this._openEditEntry}>
|
||||
<ha-icon
|
||||
slot="item-icon"
|
||||
.icon=${domainIcon(computeDomain(entry.entity_id))}
|
||||
></ha-icon>
|
||||
<paper-item-body>
|
||||
<div class="name">
|
||||
${entry.stateName || entry.entity_id}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
</paper-icon-item>
|
||||
`;
|
||||
}
|
||||
|
||||
private _openEditEntry(ev: Event): void {
|
||||
ev.stopPropagation();
|
||||
const entry = (ev.currentTarget! as any).entry;
|
||||
showEntityRegistryDetailDialog(this, {
|
||||
entry,
|
||||
});
|
||||
}
|
||||
|
||||
private _openMoreInfo(ev: MouseEvent) {
|
||||
const entry = (ev.currentTarget! as any).closest("paper-icon-item").entry;
|
||||
fireEvent(this, "hass-more-info", { entityId: entry.entity_id });
|
||||
}
|
||||
|
||||
private _addToLovelaceView(): void {
|
||||
addEntitiesToLovelaceView(
|
||||
this,
|
||||
@ -158,11 +178,32 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
.disabled-entry {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
state-badge {
|
||||
#entities > * {
|
||||
margin: 8px;
|
||||
}
|
||||
paper-icon-item {
|
||||
min-height: 40px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
paper-icon-item:not(.disabled-entry) paper-item-body {
|
||||
.name {
|
||||
font-size: 14px;
|
||||
}
|
||||
button.show-more {
|
||||
color: var(--primary-color);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border-width: initial;
|
||||
border-style: none;
|
||||
border-color: initial;
|
||||
border-image: initial;
|
||||
padding: 16px;
|
||||
font: inherit;
|
||||
}
|
||||
button.show-more:focus {
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
export interface DeviceAutomationDialogParams {
|
||||
deviceId: string;
|
||||
script?: boolean;
|
||||
}
|
||||
|
||||
export const loadDeviceAutomationDialog = () =>
|
||||
import(
|
||||
/* webpackChunkName: "device-automation-dialog" */ "./ha-device-automation-dialog"
|
||||
);
|
||||
|
||||
export const showDeviceAutomationDialog = (
|
||||
element: HTMLElement,
|
||||
detailParams: DeviceAutomationDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-device-automation",
|
||||
dialogImport: loadDeviceAutomationDialog,
|
||||
dialogParams: detailParams,
|
||||
});
|
||||
};
|
@ -9,14 +9,13 @@ import {
|
||||
|
||||
import memoizeOne from "memoize-one";
|
||||
|
||||
import "@polymer/paper-tooltip/paper-tooltip";
|
||||
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
import "../../../layouts/hass-error-screen";
|
||||
import "../ha-config-section";
|
||||
|
||||
import "./device-detail/ha-device-card";
|
||||
import "./device-detail/ha-device-triggers-card";
|
||||
import "./device-detail/ha-device-conditions-card";
|
||||
import "./device-detail/ha-device-actions-card";
|
||||
import "./device-detail/ha-device-entities-card";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { ConfigEntry } from "../../../data/config_entries";
|
||||
@ -33,19 +32,15 @@ import {
|
||||
loadDeviceRegistryDetailDialog,
|
||||
showDeviceRegistryDetailDialog,
|
||||
} from "../../../dialogs/device-registry-detail/show-dialog-device-registry-detail";
|
||||
|
||||
import {
|
||||
DeviceTrigger,
|
||||
DeviceAction,
|
||||
DeviceCondition,
|
||||
fetchDeviceTriggers,
|
||||
fetchDeviceConditions,
|
||||
fetchDeviceActions,
|
||||
} from "../../../data/device_automation";
|
||||
import "../../../components/ha-icon-next";
|
||||
import { compare } from "../../../common/string/compare";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { createValidEntityId } from "../../../common/entity/valid_entity_id";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { RelatedResult, findRelated } from "../../../data/search";
|
||||
import { SceneEntities, showSceneEditor } from "../../../data/scene";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
|
||||
|
||||
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
||||
stateName?: string;
|
||||
@ -59,12 +54,11 @@ export class HaConfigDevicePage extends LitElement {
|
||||
@property() public entities!: EntityRegistryEntry[];
|
||||
@property() public areas!: AreaRegistryEntry[];
|
||||
@property() public deviceId!: string;
|
||||
@property() public narrow!: boolean;
|
||||
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
|
||||
@property() public isWide!: boolean;
|
||||
@property() public showAdvanced!: boolean;
|
||||
@property() public route!: Route;
|
||||
@property() private _triggers: DeviceTrigger[] = [];
|
||||
@property() private _conditions: DeviceCondition[] = [];
|
||||
@property() private _actions: DeviceAction[] = [];
|
||||
@property() private _related?: RelatedResult;
|
||||
|
||||
private _device = memoizeOne(
|
||||
(
|
||||
@ -97,25 +91,10 @@ export class HaConfigDevicePage extends LitElement {
|
||||
loadDeviceRegistryDetailDialog();
|
||||
}
|
||||
|
||||
protected updated(changedProps): void {
|
||||
protected updated(changedProps) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("deviceId")) {
|
||||
if (this.deviceId) {
|
||||
fetchDeviceTriggers(this.hass, this.deviceId).then(
|
||||
(triggers) => (this._triggers = triggers)
|
||||
);
|
||||
fetchDeviceConditions(this.hass, this.deviceId).then(
|
||||
(conditions) => (this._conditions = conditions)
|
||||
);
|
||||
fetchDeviceActions(this.hass, this.deviceId).then(
|
||||
(actions) => (this._actions = actions)
|
||||
);
|
||||
} else {
|
||||
this._triggers = [];
|
||||
this._conditions = [];
|
||||
this._actions = [];
|
||||
}
|
||||
this._findRelated();
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,70 +125,176 @@ export class HaConfigDevicePage extends LitElement {
|
||||
icon="hass:settings"
|
||||
@click=${this._showSettings}
|
||||
></paper-icon-button>
|
||||
<ha-config-section .isWide=${!this.narrow}>
|
||||
<span slot="header"
|
||||
>${this.hass.localize("ui.panel.config.devices.info")}</span
|
||||
>
|
||||
<span slot="introduction">
|
||||
${this.hass.localize("ui.panel.config.devices.details")}
|
||||
</span>
|
||||
<ha-device-card
|
||||
.hass=${this.hass}
|
||||
.areas=${this.areas}
|
||||
.devices=${this.devices}
|
||||
.device=${device}
|
||||
></ha-device-card>
|
||||
|
||||
${entities.length
|
||||
? html`
|
||||
<div class="header">
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
<div class="device-info">
|
||||
<h1>${device.name_by_user || device.name}</h1>
|
||||
<ha-device-card
|
||||
.hass=${this.hass}
|
||||
.areas=${this.areas}
|
||||
.devices=${this.devices}
|
||||
.device=${device}
|
||||
></ha-device-card>
|
||||
</div>
|
||||
|
||||
${
|
||||
entities.length
|
||||
? html`
|
||||
<ha-device-entities-card
|
||||
.hass=${this.hass}
|
||||
.entities=${entities}
|
||||
>
|
||||
</ha-device-entities-card>
|
||||
`
|
||||
: html``
|
||||
}
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="column">
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.devices.automation.automations"
|
||||
)}
|
||||
>${
|
||||
this._related?.automation?.length
|
||||
? this._related.automation.map((automation) => {
|
||||
const state = this.hass.states[automation];
|
||||
return state
|
||||
? html`
|
||||
<div>
|
||||
<paper-item
|
||||
.automation=${state}
|
||||
@click=${this._openAutomation}
|
||||
.disabled=${!state.attributes.id}
|
||||
>
|
||||
<paper-item-body>
|
||||
${state.attributes.friendly_name ||
|
||||
automation}
|
||||
</paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
${!state.attributes.id
|
||||
? html`
|
||||
<paper-tooltip
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.devices.cant_edit"
|
||||
)}
|
||||
</paper-tooltip>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.devices.automation.no_automations"
|
||||
)}</paper-item
|
||||
>
|
||||
`
|
||||
}
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._showAutomationDialog}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.entities.entities"
|
||||
"ui.panel.config.devices.automation.create"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
<ha-device-entities-card
|
||||
.hass=${this.hass}
|
||||
.entities=${entities}
|
||||
>
|
||||
</ha-device-entities-card>
|
||||
`
|
||||
: html``}
|
||||
${this._triggers.length ||
|
||||
this._conditions.length ||
|
||||
this._actions.length
|
||||
? html`
|
||||
<div class="header">
|
||||
${this.hass.localize("ui.panel.config.devices.automations")}
|
||||
</ha-card>
|
||||
</div>
|
||||
<div class="column">
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.devices.scene.scenes"
|
||||
)}
|
||||
>${
|
||||
this._related?.scene?.length
|
||||
? this._related.scene.map((scene) => {
|
||||
const state = this.hass.states[scene];
|
||||
return state
|
||||
? html`
|
||||
<div>
|
||||
<paper-item
|
||||
.scene=${state}
|
||||
@click=${this._openScene}
|
||||
.disabled=${!state.attributes.id}
|
||||
>
|
||||
<paper-item-body>
|
||||
${state.attributes.friendly_name || scene}
|
||||
</paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
${!state.attributes.id
|
||||
? html`
|
||||
<paper-tooltip
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.devices.cant_edit"
|
||||
)}
|
||||
</paper-tooltip>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.devices.scene.no_scenes"
|
||||
)}</paper-item
|
||||
>
|
||||
`
|
||||
}
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._createScene}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.scene.create"
|
||||
)} </mwc-button>
|
||||
</div>
|
||||
${this._triggers.length
|
||||
? html`
|
||||
<ha-device-triggers-card
|
||||
.hass=${this.hass}
|
||||
.automations=${this._triggers}
|
||||
></ha-device-triggers-card>
|
||||
`
|
||||
: ""}
|
||||
${this._conditions.length
|
||||
? html`
|
||||
<ha-device-conditions-card
|
||||
.hass=${this.hass}
|
||||
.automations=${this._conditions}
|
||||
></ha-device-conditions-card>
|
||||
`
|
||||
: ""}
|
||||
${this._actions.length
|
||||
? html`
|
||||
<ha-device-actions-card
|
||||
.hass=${this.hass}
|
||||
.automations=${this._actions}
|
||||
></ha-device-actions-card>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: html``}
|
||||
</ha-card>
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.devices.script.scripts"
|
||||
)}
|
||||
>${
|
||||
this._related?.script?.length
|
||||
? this._related.script.map((script) => {
|
||||
const state = this.hass.states[script];
|
||||
return state
|
||||
? html`
|
||||
<paper-item
|
||||
.script=${script}
|
||||
@click=${this._openScript}
|
||||
>
|
||||
<paper-item-body>
|
||||
${state.attributes.friendly_name || script}
|
||||
</paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-item>
|
||||
`
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.script.no_scripts"
|
||||
)}</paper-item
|
||||
>
|
||||
`
|
||||
}
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._showScriptDialog}>
|
||||
${this.hass.localize("ui.panel.config.devices.script.create")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ha-config-section>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
</hass-tabs-subpage> `;
|
||||
}
|
||||
|
||||
private _computeEntityName(entity) {
|
||||
@ -220,6 +305,50 @@ export class HaConfigDevicePage extends LitElement {
|
||||
return state ? computeStateName(state) : null;
|
||||
}
|
||||
|
||||
private async _findRelated() {
|
||||
this._related = await findRelated(this.hass, "device", this.deviceId);
|
||||
}
|
||||
|
||||
private _createScene() {
|
||||
const entities: SceneEntities = {};
|
||||
this._entities(this.deviceId, this.entities).forEach((entity) => {
|
||||
entities[entity.entity_id] = "";
|
||||
});
|
||||
showSceneEditor(this, {
|
||||
entities,
|
||||
});
|
||||
}
|
||||
|
||||
private _openScene(ev: Event) {
|
||||
const state = (ev.currentTarget as any).scene;
|
||||
if (state.attributes.id) {
|
||||
navigate(this, `/config/scene/edit/${state.attributes.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
private _openScript(ev: Event) {
|
||||
const script = (ev.currentTarget as any).script;
|
||||
navigate(this, `/config/script/edit/${script}`);
|
||||
}
|
||||
|
||||
private _openAutomation(ev: Event) {
|
||||
const state = (ev.currentTarget as any).automation;
|
||||
if (state.attributes.id) {
|
||||
navigate(this, `/config/automation/edit/${state.attributes.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
private _showScriptDialog() {
|
||||
showDeviceAutomationDialog(this, { deviceId: this.deviceId, script: true });
|
||||
}
|
||||
|
||||
private _showAutomationDialog() {
|
||||
showDeviceAutomationDialog(this, {
|
||||
deviceId: this.deviceId,
|
||||
script: false,
|
||||
});
|
||||
}
|
||||
|
||||
private async _showSettings() {
|
||||
const device = this._device(this.deviceId, this.devices)!;
|
||||
showDeviceRegistryDetailDialog(this, {
|
||||
@ -282,20 +411,81 @@ export class HaConfigDevicePage extends LitElement {
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.header {
|
||||
font-family: var(--paper-font-display1_-_font-family);
|
||||
.container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: auto;
|
||||
max-width: 1000px;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.device-info {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.show-more {
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
font-family: var(--paper-font-headline_-_font-family);
|
||||
-webkit-font-smoothing: var(
|
||||
--paper-font-display1_-_-webkit-font-smoothing
|
||||
--paper-font-headline_-_-webkit-font-smoothing
|
||||
);
|
||||
font-size: var(--paper-font-display1_-_font-size);
|
||||
font-weight: var(--paper-font-display1_-_font-weight);
|
||||
letter-spacing: var(--paper-font-display1_-_letter-spacing);
|
||||
line-height: var(--paper-font-display1_-_line-height);
|
||||
font-size: var(--paper-font-headline_-_font-size);
|
||||
font-weight: var(--paper-font-headline_-_font-weight);
|
||||
letter-spacing: var(--paper-font-headline_-_letter-spacing);
|
||||
line-height: var(--paper-font-headline_-_line-height);
|
||||
opacity: var(--dark-primary-opacity);
|
||||
}
|
||||
|
||||
ha-config-section *:last-child {
|
||||
padding-bottom: 24px;
|
||||
.left,
|
||||
.column,
|
||||
.fullwidth {
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 33.33%;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 66.66%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.fullwidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.column {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.column > *:not(:first-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
:host([narrow]) .left,
|
||||
:host([narrow]) .right,
|
||||
:host([narrow]) .column {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host([narrow]) .container {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
paper-item.no-link {
|
||||
cursor: default;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
SceneEntities,
|
||||
applyScene,
|
||||
activateScene,
|
||||
getSceneEditorInitData,
|
||||
} from "../../../data/scene";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import {
|
||||
@ -391,6 +392,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
|
||||
super.updated(changedProps);
|
||||
|
||||
const oldscene = changedProps.get("scene") as SceneEntity;
|
||||
|
||||
if (
|
||||
changedProps.has("scene") &&
|
||||
this.scene &&
|
||||
@ -403,10 +405,16 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
|
||||
|
||||
if (changedProps.has("creatingNew") && this.creatingNew && this.hass) {
|
||||
this._dirty = false;
|
||||
const initData = getSceneEditorInitData();
|
||||
this._config = {
|
||||
name: this.hass.localize("ui.panel.config.scene.editor.default_name"),
|
||||
entities: {},
|
||||
...initData,
|
||||
};
|
||||
if (initData) {
|
||||
this._initEntities(this._config);
|
||||
this._dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProps.has("_entityRegistryEntries")) {
|
||||
@ -458,24 +466,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
|
||||
config.entities = {};
|
||||
}
|
||||
|
||||
this._entities = Object.keys(config.entities);
|
||||
|
||||
this._entities.forEach((entity) => {
|
||||
this._storeState(entity);
|
||||
});
|
||||
|
||||
const filteredEntityReg = this._entityRegistryEntries.filter((entityReg) =>
|
||||
this._entities.includes(entityReg.entity_id)
|
||||
);
|
||||
|
||||
for (const entityReg of filteredEntityReg) {
|
||||
if (!entityReg.device_id) {
|
||||
continue;
|
||||
}
|
||||
if (!this._devices.includes(entityReg.device_id)) {
|
||||
this._devices = [...this._devices, entityReg.device_id];
|
||||
}
|
||||
}
|
||||
this._initEntities(config);
|
||||
|
||||
const { context } = await activateScene(this.hass, this.scene!.entity_id);
|
||||
|
||||
@ -489,6 +480,25 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _initEntities(config: SceneConfig) {
|
||||
this._entities = Object.keys(config.entities);
|
||||
this._entities.forEach((entity) => this._storeState(entity));
|
||||
|
||||
const filteredEntityReg = this._entityRegistryEntries.filter((entityReg) =>
|
||||
this._entities.includes(entityReg.entity_id)
|
||||
);
|
||||
this._devices = [];
|
||||
|
||||
for (const entityReg of filteredEntityReg) {
|
||||
if (!entityReg.device_id) {
|
||||
continue;
|
||||
}
|
||||
if (!this._devices.includes(entityReg.device_id)) {
|
||||
this._devices = [...this._devices, entityReg.device_id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _entityPicked(ev: CustomEvent) {
|
||||
const entityId = ev.detail.value;
|
||||
(ev.target as any).value = "";
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
ScriptEntity,
|
||||
ScriptConfig,
|
||||
deleteScript,
|
||||
getScriptEditorInitData,
|
||||
} from "../../../data/script";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../layouts/ha-app-layout";
|
||||
@ -188,10 +189,12 @@ export class HaScriptEditor extends LitElement {
|
||||
}
|
||||
|
||||
if (changedProps.has("creatingNew") && this.creatingNew && this.hass) {
|
||||
this._dirty = false;
|
||||
const initData = getScriptEditorInitData();
|
||||
this._dirty = initData ? true : false;
|
||||
this._config = {
|
||||
alias: this.hass.localize("ui.panel.config.script.editor.default_name"),
|
||||
sequence: [{ service: "" }],
|
||||
...initData,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -39,11 +39,11 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
|
||||
return { entities: [] };
|
||||
}
|
||||
|
||||
@property() protected _config?: EntitiesCardConfig;
|
||||
@property() private _config?: EntitiesCardConfig;
|
||||
|
||||
protected _hass?: HomeAssistant;
|
||||
private _hass?: HomeAssistant;
|
||||
|
||||
protected _configEntities?: EntitiesCardEntityConfig[];
|
||||
private _configEntities?: EntitiesCardEntityConfig[];
|
||||
|
||||
set hass(hass: HomeAssistant) {
|
||||
this._hass = hass;
|
||||
|
@ -1271,6 +1271,9 @@
|
||||
"name": "Name",
|
||||
"update": "Update",
|
||||
"automation": {
|
||||
"automations": "Automations",
|
||||
"no_automations": "No automations",
|
||||
"create": "Create automation with device",
|
||||
"triggers": {
|
||||
"caption": "Do something when..."
|
||||
},
|
||||
@ -1281,15 +1284,25 @@
|
||||
"caption": "When something is triggered..."
|
||||
}
|
||||
},
|
||||
"script": {
|
||||
"scripts": "Scripts",
|
||||
"no_scripts": "No scripts",
|
||||
"create": "Create script with device"
|
||||
},
|
||||
"scene": {
|
||||
"scenes": "Scenes",
|
||||
"no_scenes": "No scenes",
|
||||
"create": "Create scene with device"
|
||||
},
|
||||
"cant_edit": "You can only edit items that are created in the UI.",
|
||||
"device_not_found": "Device not found.",
|
||||
"info": "Device info",
|
||||
"details": "Here are all the details of your device.",
|
||||
"entities": {
|
||||
"entities": "Entities",
|
||||
"add_entities_lovelace": "Add all device entities to Lovelace UI",
|
||||
"add_entities_lovelace": "Add to Lovelace",
|
||||
"none": "This device has no entities"
|
||||
},
|
||||
"automations": "Automations",
|
||||
"scripts": "Scripts",
|
||||
"scenes": "Scenes",
|
||||
"confirm_rename_entity_ids": "Do you also want to rename the entity id's of your entities?",
|
||||
"data_table": {
|
||||
"device": "Device",
|
||||
|
Loading…
x
Reference in New Issue
Block a user