mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Add support for target to automation call service action (#8372)
This commit is contained in:
parent
acefa39796
commit
99eff73b0d
@ -101,7 +101,7 @@
|
|||||||
"fuse.js": "^6.0.0",
|
"fuse.js": "^6.0.0",
|
||||||
"google-timezones-json": "^1.0.2",
|
"google-timezones-json": "^1.0.2",
|
||||||
"hls.js": "^0.13.2",
|
"hls.js": "^0.13.2",
|
||||||
"home-assistant-js-websocket": "^5.4.1",
|
"home-assistant-js-websocket": "^5.8.1",
|
||||||
"idb-keyval": "^3.2.0",
|
"idb-keyval": "^3.2.0",
|
||||||
"intl-messageformat": "^8.3.9",
|
"intl-messageformat": "^8.3.9",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import "@material/mwc-icon-button/mwc-icon-button";
|
|
||||||
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
|
||||||
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@ -38,7 +33,7 @@ import {
|
|||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-svg-icon";
|
import { HaComboBox } from "../ha-combo-box";
|
||||||
|
|
||||||
interface Device {
|
interface Device {
|
||||||
name: string;
|
name: string;
|
||||||
@ -115,7 +110,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
private _opened?: boolean;
|
private _opened?: boolean;
|
||||||
|
|
||||||
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
|
@query("ha-combo-box", true) private _comboBox!: HaComboBox;
|
||||||
|
|
||||||
private _init = false;
|
private _init = false;
|
||||||
|
|
||||||
@ -244,15 +239,11 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
public open() {
|
public open() {
|
||||||
this.updateComplete.then(() => {
|
this._comboBox?.open();
|
||||||
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public focus() {
|
public focus() {
|
||||||
this.updateComplete.then(() => {
|
this._comboBox?.focus();
|
||||||
this.shadowRoot?.querySelector("paper-input")?.focus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
@ -292,70 +283,28 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<vaadin-combo-box-light
|
<ha-combo-box
|
||||||
|
.hass=${this.hass}
|
||||||
|
.label=${this.label === undefined && this.hass
|
||||||
|
? this.hass.localize("ui.components.device-picker.device")
|
||||||
|
: this.label}
|
||||||
|
.value=${this._value}
|
||||||
|
.renderer=${rowRenderer}
|
||||||
item-value-path="id"
|
item-value-path="id"
|
||||||
item-id-path="id"
|
item-id-path="id"
|
||||||
item-label-path="name"
|
item-label-path="name"
|
||||||
.value=${this._value}
|
|
||||||
.renderer=${rowRenderer}
|
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
@value-changed=${this._deviceChanged}
|
@value-changed=${this._deviceChanged}
|
||||||
>
|
></ha-combo-box>
|
||||||
<paper-input
|
|
||||||
.label=${this.label === undefined && this.hass
|
|
||||||
? this.hass.localize("ui.components.device-picker.device")
|
|
||||||
: this.label}
|
|
||||||
class="input"
|
|
||||||
autocapitalize="none"
|
|
||||||
autocomplete="off"
|
|
||||||
autocorrect="off"
|
|
||||||
spellcheck="false"
|
|
||||||
>
|
|
||||||
${this.value
|
|
||||||
? html`
|
|
||||||
<mwc-icon-button
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.components.device-picker.clear"
|
|
||||||
)}
|
|
||||||
slot="suffix"
|
|
||||||
class="clear-button"
|
|
||||||
@click=${this._clearValue}
|
|
||||||
>
|
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
|
|
||||||
<mwc-icon-button
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.components.device-picker.show_devices"
|
|
||||||
)}
|
|
||||||
slot="suffix"
|
|
||||||
class="toggle-button"
|
|
||||||
>
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
|
||||||
></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
</paper-input>
|
|
||||||
</vaadin-combo-box-light>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearValue(ev: Event) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this._setValue("");
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _value() {
|
private get _value() {
|
||||||
return this.value || "";
|
return this.value || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>) {
|
|
||||||
this._opened = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _deviceChanged(ev: PolymerChangedEvent<string>) {
|
private _deviceChanged(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
const newValue = ev.detail.value;
|
const newValue = ev.detail.value;
|
||||||
|
|
||||||
if (newValue !== this._value) {
|
if (newValue !== this._value) {
|
||||||
@ -363,6 +312,10 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _openedChanged(ev: PolymerChangedEvent<boolean>) {
|
||||||
|
this._opened = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
private _setValue(value: string) {
|
private _setValue(value: string) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
|
||||||
import { EventsMixin } from "../mixins/events-mixin";
|
|
||||||
import "./ha-icon-button";
|
|
||||||
|
|
||||||
class HaComboBox extends EventsMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
paper-input > ha-icon-button {
|
|
||||||
--mdc-icon-button-size: 24px;
|
|
||||||
padding: 2px;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<vaadin-combo-box-light
|
|
||||||
items="[[_items]]"
|
|
||||||
item-value-path="[[itemValuePath]]"
|
|
||||||
item-label-path="[[itemLabelPath]]"
|
|
||||||
value="{{value}}"
|
|
||||||
opened="{{opened}}"
|
|
||||||
allow-custom-value="[[allowCustomValue]]"
|
|
||||||
on-change="_fireChanged"
|
|
||||||
>
|
|
||||||
<paper-input
|
|
||||||
autofocus="[[autofocus]]"
|
|
||||||
label="[[label]]"
|
|
||||||
class="input"
|
|
||||||
value="[[value]]"
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="suffix"
|
|
||||||
class="clear-button"
|
|
||||||
icon="hass:close"
|
|
||||||
hidden$="[[!value]]"
|
|
||||||
>Clear</ha-icon-button
|
|
||||||
>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="suffix"
|
|
||||||
class="toggle-button"
|
|
||||||
icon="[[_computeToggleIcon(opened)]]"
|
|
||||||
hidden$="[[!items.length]]"
|
|
||||||
>Toggle</ha-icon-button
|
|
||||||
>
|
|
||||||
</paper-input>
|
|
||||||
<template>
|
|
||||||
<style>
|
|
||||||
paper-item {
|
|
||||||
margin: -5px -10px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<paper-item>[[_computeItemLabel(item, itemLabelPath)]]</paper-item>
|
|
||||||
</template>
|
|
||||||
</vaadin-combo-box-light>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
allowCustomValue: Boolean,
|
|
||||||
items: {
|
|
||||||
type: Object,
|
|
||||||
observer: "_itemsChanged",
|
|
||||||
},
|
|
||||||
_items: Object,
|
|
||||||
itemLabelPath: String,
|
|
||||||
itemValuePath: String,
|
|
||||||
autofocus: Boolean,
|
|
||||||
label: String,
|
|
||||||
opened: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
observer: "_openedChanged",
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
notify: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_openedChanged(newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
this._items = this.items;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_itemsChanged(newVal) {
|
|
||||||
if (!this.opened) {
|
|
||||||
this._items = newVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeToggleIcon(opened) {
|
|
||||||
return opened ? "hass:menu-up" : "hass:menu-down";
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeItemLabel(item, itemLabelPath) {
|
|
||||||
return itemLabelPath ? item[itemLabelPath] : item;
|
|
||||||
}
|
|
||||||
|
|
||||||
_fireChanged(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.fire("change");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-combo-box", HaComboBox);
|
|
177
src/components/ha-combo-box.ts
Normal file
177
src/components/ha-combo-box.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import "@material/mwc-icon-button/mwc-icon-button";
|
||||||
|
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
query,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { PolymerChangedEvent } from "../polymer-types";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import "./ha-svg-icon";
|
||||||
|
|
||||||
|
const defaultRowRenderer = (
|
||||||
|
root: HTMLElement,
|
||||||
|
_owner,
|
||||||
|
model: { item: any }
|
||||||
|
) => {
|
||||||
|
if (!root.firstElementChild) {
|
||||||
|
root.innerHTML = `
|
||||||
|
<style>
|
||||||
|
paper-item {
|
||||||
|
margin: -5px -10px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-item></paper-item>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.querySelector("paper-item")!.textContent = model.item;
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-combo-box")
|
||||||
|
export class HaComboBox extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property() public value?: string;
|
||||||
|
|
||||||
|
@property() public items?: [];
|
||||||
|
|
||||||
|
@property() public filteredItems?: [];
|
||||||
|
|
||||||
|
@property({ attribute: "allow-custom-value", type: Boolean })
|
||||||
|
public allowCustomValue?: boolean;
|
||||||
|
|
||||||
|
@property({ attribute: "item-value-path" }) public itemValuePath?: string;
|
||||||
|
|
||||||
|
@property({ attribute: "item-label-path" }) public itemLabelPath?: string;
|
||||||
|
|
||||||
|
@property({ attribute: "item-id-path" }) public itemIdPath?: string;
|
||||||
|
|
||||||
|
@property() public renderer?: (
|
||||||
|
root: HTMLElement,
|
||||||
|
owner: HTMLElement,
|
||||||
|
model: { item: any }
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
private _opened?: boolean;
|
||||||
|
|
||||||
|
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
(this._comboBox as any)?.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public focus() {
|
||||||
|
this.updateComplete.then(() => {
|
||||||
|
this.shadowRoot?.querySelector("paper-input")?.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<vaadin-combo-box-light
|
||||||
|
.itemValuePath=${this.itemValuePath}
|
||||||
|
.itemIdPath=${this.itemIdPath}
|
||||||
|
.itemLabelPath=${this.itemLabelPath}
|
||||||
|
.value=${this.value}
|
||||||
|
.items=${this.items}
|
||||||
|
.filteredItems=${this.filteredItems}
|
||||||
|
.renderer=${this.renderer || defaultRowRenderer}
|
||||||
|
.allowCustomValue=${this.allowCustomValue}
|
||||||
|
@opened-changed=${this._openedChanged}
|
||||||
|
@filter-changed=${this._filterChanged}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.label}
|
||||||
|
class="input"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
spellcheck="false"
|
||||||
|
>
|
||||||
|
${this.value
|
||||||
|
? html`
|
||||||
|
<mwc-icon-button
|
||||||
|
.label=${this.hass.localize("ui.components.combo-box.clear")}
|
||||||
|
slot="suffix"
|
||||||
|
class="clear-button"
|
||||||
|
@click=${this._clearValue}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<mwc-icon-button
|
||||||
|
.label=${this.hass.localize("ui.components.combo-box.show")}
|
||||||
|
slot="suffix"
|
||||||
|
class="toggle-button"
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
</paper-input>
|
||||||
|
</vaadin-combo-box-light>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearValue(ev: Event) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", { value: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openedChanged(ev: PolymerChangedEvent<boolean>) {
|
||||||
|
this._opened = ev.detail.value;
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, ev.type, ev.detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterChanged(ev: PolymerChangedEvent<boolean>) {
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, ev.type, ev.detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const newValue = ev.detail.value;
|
||||||
|
|
||||||
|
if (newValue !== this.value) {
|
||||||
|
fireEvent(this, "value-changed", { value: newValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
paper-input > mwc-icon-button {
|
||||||
|
--mdc-icon-button-size: 24px;
|
||||||
|
padding: 2px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-combo-box": HaComboBox;
|
||||||
|
}
|
||||||
|
}
|
@ -46,7 +46,7 @@ export class HaNumberSelector extends LitElement {
|
|||||||
class=${classMap({ single: this.selector.number.mode === "box" })}
|
class=${classMap({ single: this.selector.number.mode === "box" })}
|
||||||
.min=${this.selector.number.min}
|
.min=${this.selector.number.min}
|
||||||
.max=${this.selector.number.max}
|
.max=${this.selector.number.max}
|
||||||
.value=${this._value}
|
.value=${this.value}
|
||||||
.step=${this.selector.number.step}
|
.step=${this.selector.number.step}
|
||||||
type="number"
|
type="number"
|
||||||
auto-validate
|
auto-validate
|
||||||
@ -65,16 +65,21 @@ export class HaNumberSelector extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleInputChange(ev) {
|
private _handleInputChange(ev) {
|
||||||
const value = ev.detail.value;
|
ev.stopPropagation();
|
||||||
if (this._value === value) {
|
const value =
|
||||||
|
ev.detail.value === "" || isNaN(ev.detail.value)
|
||||||
|
? undefined
|
||||||
|
: Number(ev.detail.value);
|
||||||
|
if (this.value === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSliderChange(ev) {
|
private _handleSliderChange(ev) {
|
||||||
const value = ev.target.value;
|
ev.stopPropagation();
|
||||||
if (this._value === value) {
|
const value = Number(ev.target.value);
|
||||||
|
if (this.value === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fireEvent(this, "value-changed", { value });
|
fireEvent(this, "value-changed", { value });
|
||||||
|
@ -3,7 +3,11 @@ import "@material/mwc-list/mwc-list-item";
|
|||||||
import "@material/mwc-tab-bar/mwc-tab-bar";
|
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||||
import "@material/mwc-tab/mwc-tab";
|
import "@material/mwc-tab/mwc-tab";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
import {
|
||||||
|
HassEntity,
|
||||||
|
HassServiceTarget,
|
||||||
|
UnsubscribeFunc,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -20,7 +24,6 @@ import {
|
|||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
} from "../../data/entity_registry";
|
} from "../../data/entity_registry";
|
||||||
import { TargetSelector } from "../../data/selector";
|
import { TargetSelector } from "../../data/selector";
|
||||||
import { Target } from "../../data/target";
|
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-target-picker";
|
import "../ha-target-picker";
|
||||||
@ -31,7 +34,7 @@ export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public selector!: TargetSelector;
|
@property() public selector!: TargetSelector;
|
||||||
|
|
||||||
@property() public value?: Target;
|
@property() public value?: HassServiceTarget;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@ -59,7 +62,8 @@ export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
|||||||
const oldSelector = changedProperties.get("selector");
|
const oldSelector = changedProperties.get("selector");
|
||||||
if (
|
if (
|
||||||
oldSelector !== this.selector &&
|
oldSelector !== this.selector &&
|
||||||
this.selector.target.device?.integration
|
(this.selector.target.device?.integration ||
|
||||||
|
this.selector.target.entity?.integration)
|
||||||
) {
|
) {
|
||||||
this._loadConfigEntries();
|
this._loadConfigEntries();
|
||||||
}
|
}
|
||||||
@ -84,11 +88,15 @@ export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _filterEntities(entity: HassEntity): boolean {
|
private _filterEntities(entity: HassEntity): boolean {
|
||||||
if (this.selector.target.entity?.integration) {
|
if (
|
||||||
|
this.selector.target.entity?.integration ||
|
||||||
|
this.selector.target.device?.integration
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
!this._entityPlaformLookup ||
|
!this._entityPlaformLookup ||
|
||||||
this._entityPlaformLookup[entity.entity_id] !==
|
this._entityPlaformLookup[entity.entity_id] !==
|
||||||
this.selector.target.entity.integration
|
(this.selector.target.entity?.integration ||
|
||||||
|
this.selector.target.device?.integration)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -118,7 +126,10 @@ export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
|||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.selector.target.device?.integration) {
|
if (
|
||||||
|
this.selector.target.device?.integration ||
|
||||||
|
this.selector.target.entity?.integration
|
||||||
|
) {
|
||||||
if (
|
if (
|
||||||
!this._configEntries?.some((entry) =>
|
!this._configEntries?.some((entry) =>
|
||||||
device.config_entries.includes(entry.entry_id)
|
device.config_entries.includes(entry.entry_id)
|
||||||
@ -132,14 +143,16 @@ export class HaTargetSelector extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
private async _loadConfigEntries() {
|
private async _loadConfigEntries() {
|
||||||
this._configEntries = (await getConfigEntries(this.hass)).filter(
|
this._configEntries = (await getConfigEntries(this.hass)).filter(
|
||||||
(entry) => entry.domain === this.selector.target.device?.integration
|
(entry) =>
|
||||||
|
entry.domain ===
|
||||||
|
(this.selector.target.device?.integration ||
|
||||||
|
this.selector.target.entity?.integration)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
ha-target-picker {
|
ha-target-picker {
|
||||||
margin: 0 -8px;
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
290
src/components/ha-service-control.ts
Normal file
290
src/components/ha-service-control.ts
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
import { HassService, HassServiceTarget } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
query,
|
||||||
|
} from "lit-element";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
|
import { computeObjectId } from "../common/entity/compute_object_id";
|
||||||
|
import { ENTITY_COMPONENT_DOMAINS } from "../data/entity";
|
||||||
|
import { Selector } from "../data/selector";
|
||||||
|
import { PolymerChangedEvent } from "../polymer-types";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import "./ha-selector/ha-selector";
|
||||||
|
import "./ha-service-picker";
|
||||||
|
import "./ha-settings-row";
|
||||||
|
import "./ha-yaml-editor";
|
||||||
|
import type { HaYamlEditor } from "./ha-yaml-editor";
|
||||||
|
|
||||||
|
interface ExtHassService extends Omit<HassService, "fields"> {
|
||||||
|
fields: {
|
||||||
|
key: string;
|
||||||
|
name?: string;
|
||||||
|
description: string;
|
||||||
|
required?: boolean;
|
||||||
|
default?: any;
|
||||||
|
example?: any;
|
||||||
|
selector?: Selector;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("ha-service-control")
|
||||||
|
export class HaServiceControl extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public value?: {
|
||||||
|
service: string;
|
||||||
|
target?: HassServiceTarget;
|
||||||
|
data?: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
@property({ reflect: true, type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
|
@internalProperty() private _serviceData?: ExtHassService;
|
||||||
|
|
||||||
|
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||||
|
|
||||||
|
protected updated(changedProperties: PropertyValues) {
|
||||||
|
if (!changedProperties.has("value")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._serviceData = this.value?.service
|
||||||
|
? this._getServiceInfo(this.value.service)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._serviceData &&
|
||||||
|
"target" in this._serviceData &&
|
||||||
|
this.value?.data?.entity_id
|
||||||
|
) {
|
||||||
|
this.value = {
|
||||||
|
...this.value,
|
||||||
|
target: { ...this.value.target, entity_id: this.value.data.entity_id },
|
||||||
|
};
|
||||||
|
delete this.value.data!.entity_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.value?.data) {
|
||||||
|
const yamlEditor = this._yamlEditor;
|
||||||
|
if (yamlEditor && yamlEditor.value !== this.value.data) {
|
||||||
|
yamlEditor.setValue(this.value.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _domainFilter = memoizeOne((service: string) => {
|
||||||
|
const domain = computeDomain(service);
|
||||||
|
return ENTITY_COMPONENT_DOMAINS.includes(domain) ? [domain] : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
private _getServiceInfo = memoizeOne((service: string):
|
||||||
|
| ExtHassService
|
||||||
|
| undefined => {
|
||||||
|
if (!service) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const domain = computeDomain(service);
|
||||||
|
const serviceName = computeObjectId(service);
|
||||||
|
const serviceDomains = this.hass.services;
|
||||||
|
if (!(domain in serviceDomains)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!(serviceName in serviceDomains[domain])) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = Object.entries(
|
||||||
|
serviceDomains[domain][serviceName].fields
|
||||||
|
).map(([key, value]) => {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
...value,
|
||||||
|
selector: value.selector as Selector | undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...serviceDomains[domain][serviceName],
|
||||||
|
fields,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const legacy =
|
||||||
|
this._serviceData?.fields.length &&
|
||||||
|
!this._serviceData.fields.some((field) => field.selector);
|
||||||
|
|
||||||
|
const entityId =
|
||||||
|
legacy &&
|
||||||
|
this._serviceData?.fields.find((field) => field.key === "entity_id");
|
||||||
|
|
||||||
|
return html`<ha-service-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this.value?.service}
|
||||||
|
@value-changed=${this._serviceChanged}
|
||||||
|
></ha-service-picker>
|
||||||
|
${this._serviceData && "target" in this._serviceData
|
||||||
|
? html`<ha-selector
|
||||||
|
.hass=${this.hass}
|
||||||
|
.selector=${this._serviceData.target
|
||||||
|
? { target: this._serviceData.target }
|
||||||
|
: {
|
||||||
|
target: {
|
||||||
|
entity: { domain: computeDomain(this.value!.service) },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
@value-changed=${this._targetChanged}
|
||||||
|
.value=${this.value?.target}
|
||||||
|
></ha-selector>`
|
||||||
|
: entityId
|
||||||
|
? html`<ha-entity-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this.value?.data?.entity_id}
|
||||||
|
.label=${entityId.description}
|
||||||
|
.includeDomains=${this._domainFilter(this.value!.service)}
|
||||||
|
@value-changed=${this._entityPicked}
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>`
|
||||||
|
: ""}
|
||||||
|
${legacy
|
||||||
|
? html`<ha-yaml-editor
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.actions.type.service.service_data"
|
||||||
|
)}
|
||||||
|
.name=${"data"}
|
||||||
|
.defaultValue=${this.value?.data}
|
||||||
|
@value-changed=${this._dataChanged}
|
||||||
|
></ha-yaml-editor>`
|
||||||
|
: this._serviceData?.fields.map((dataField) =>
|
||||||
|
dataField.selector
|
||||||
|
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||||
|
<span slot="heading">${dataField.name || dataField.key}</span>
|
||||||
|
<span slot="description">${dataField?.description}</span
|
||||||
|
><ha-selector
|
||||||
|
.hass=${this.hass}
|
||||||
|
.selector=${dataField.selector}
|
||||||
|
.key=${dataField.key}
|
||||||
|
@value-changed=${this._serviceDataChanged}
|
||||||
|
.value=${(this.value?.data &&
|
||||||
|
this.value.data[dataField.key]) ||
|
||||||
|
dataField.default}
|
||||||
|
></ha-selector
|
||||||
|
></ha-settings-row>`
|
||||||
|
: ""
|
||||||
|
)} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serviceChanged(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (ev.detail.value === this.value?.service) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { service: ev.detail.value || "", data: {} },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const newValue = ev.detail.value;
|
||||||
|
if (this.value?.data?.entity_id === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let value;
|
||||||
|
if (!newValue && this.value?.data) {
|
||||||
|
value = { ...this.value };
|
||||||
|
delete value.data.entity_id;
|
||||||
|
} else {
|
||||||
|
value = {
|
||||||
|
...this.value,
|
||||||
|
data: { ...this.value?.data, entity_id: ev.detail.value },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _targetChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const newValue = ev.detail.value;
|
||||||
|
if (this.value?.target === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let value;
|
||||||
|
if (!newValue) {
|
||||||
|
value = { ...this.value };
|
||||||
|
delete value.target;
|
||||||
|
} else {
|
||||||
|
value = { ...this.value, target: ev.detail.value };
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _serviceDataChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const key = (ev.currentTarget as any).key;
|
||||||
|
const value = ev.detail.value;
|
||||||
|
if (this.value?.data && this.value.data[key] === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = { ...this.value?.data, [key]: value };
|
||||||
|
|
||||||
|
if (value === "" || value === undefined) {
|
||||||
|
delete data[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.value,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dataChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!ev.detail.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.value,
|
||||||
|
data: ev.detail.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-settings-row {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
ha-settings-row {
|
||||||
|
--paper-time-input-justify-content: flex-end;
|
||||||
|
}
|
||||||
|
:host(:not([narrow])) ha-settings-row paper-input {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
:host(:not([narrow])) ha-settings-row ha-selector {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-service-control": HaServiceControl;
|
||||||
|
}
|
||||||
|
}
|
@ -1,60 +0,0 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
|
||||||
import "./ha-combo-box";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class HaServicePicker extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<ha-combo-box
|
|
||||||
label="[[localize('ui.components.service-picker.service')]]"
|
|
||||||
items="[[_services]]"
|
|
||||||
value="{{value}}"
|
|
||||||
allow-custom-value=""
|
|
||||||
></ha-combo-box>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: {
|
|
||||||
type: Object,
|
|
||||||
observer: "_hassChanged",
|
|
||||||
},
|
|
||||||
_services: Array,
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
notify: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_hassChanged(hass, oldHass) {
|
|
||||||
if (!hass) {
|
|
||||||
this._services = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (oldHass && hass.services === oldHass.services) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
Object.keys(hass.services)
|
|
||||||
.sort()
|
|
||||||
.forEach((domain) => {
|
|
||||||
const services = Object.keys(hass.services[domain]).sort();
|
|
||||||
|
|
||||||
for (let i = 0; i < services.length; i++) {
|
|
||||||
result.push(`${domain}.${services[i]}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._services = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-service-picker", HaServicePicker);
|
|
121
src/components/ha-service-picker.ts
Normal file
121
src/components/ha-service-picker.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { html, internalProperty, LitElement, property } from "lit-element";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import "./ha-combo-box";
|
||||||
|
|
||||||
|
const rowRenderer = (
|
||||||
|
root: HTMLElement,
|
||||||
|
_owner,
|
||||||
|
model: { item: { service: string; description: string } }
|
||||||
|
) => {
|
||||||
|
if (!root.firstElementChild) {
|
||||||
|
root.innerHTML = `
|
||||||
|
<style>
|
||||||
|
paper-item {
|
||||||
|
margin: -10px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-item>
|
||||||
|
<paper-item-body two-line="">
|
||||||
|
<div class='name'>[[item.description]]</div>
|
||||||
|
<div secondary>[[item.service]]</div>
|
||||||
|
</paper-item-body>
|
||||||
|
</paper-item>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.querySelector(".name")!.textContent = model.item.description;
|
||||||
|
root.querySelector("[secondary]")!.textContent = model.item.service;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HaServicePicker extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public value?: string;
|
||||||
|
|
||||||
|
@internalProperty() private _filter?: string;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-combo-box
|
||||||
|
.hass=${this.hass}
|
||||||
|
.label=${this.hass.localize("ui.components.service-picker.service")}
|
||||||
|
.filteredItems=${this._filteredServices(
|
||||||
|
this.hass.services,
|
||||||
|
this._filter
|
||||||
|
)}
|
||||||
|
.value=${this.value}
|
||||||
|
.renderer=${rowRenderer}
|
||||||
|
item-value-path="service"
|
||||||
|
item-label-path="description"
|
||||||
|
allow-custom-value
|
||||||
|
@filter-changed=${this._filterChanged}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-combo-box>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _services = memoizeOne((services: HomeAssistant["services"]): {
|
||||||
|
service: string;
|
||||||
|
description: string;
|
||||||
|
}[] => {
|
||||||
|
if (!services) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const result: { service: string; description: string }[] = [];
|
||||||
|
|
||||||
|
Object.keys(services)
|
||||||
|
.sort()
|
||||||
|
.forEach((domain) => {
|
||||||
|
const services_keys = Object.keys(services[domain]).sort();
|
||||||
|
|
||||||
|
for (const service of services_keys) {
|
||||||
|
result.push({
|
||||||
|
service: `${domain}.${service}`,
|
||||||
|
description:
|
||||||
|
services[domain][service].description || `${domain}.${service}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
private _filteredServices = memoizeOne(
|
||||||
|
(services: HomeAssistant["services"], filter?: string) => {
|
||||||
|
if (!services) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const processedServices = this._services(services);
|
||||||
|
|
||||||
|
if (!filter) {
|
||||||
|
return processedServices;
|
||||||
|
}
|
||||||
|
return processedServices.filter(
|
||||||
|
(service) =>
|
||||||
|
service.service.toLowerCase().includes(filter) ||
|
||||||
|
service.description.toLowerCase().includes(filter)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _filterChanged(ev: CustomEvent): void {
|
||||||
|
this._filter = ev.detail.value.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev) {
|
||||||
|
this.value = ev.detail.value;
|
||||||
|
fireEvent(this, "change");
|
||||||
|
fireEvent(this, "value-changed", { value: this.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-service-picker", HaServicePicker);
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-service-picker": HaServicePicker;
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,7 @@ export class HaSettingsRow extends LitElement {
|
|||||||
min-height: calc(
|
min-height: calc(
|
||||||
var(--paper-item-body-two-line-min-height, 72px) - 16px
|
var(--paper-item-body-two-line-min-height, 72px) - 16px
|
||||||
);
|
);
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
:host([narrow]) {
|
:host([narrow]) {
|
||||||
align-items: normal;
|
align-items: normal;
|
||||||
|
@ -10,7 +10,10 @@ import {
|
|||||||
mdiUnfoldMoreVertical,
|
mdiUnfoldMoreVertical,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import {
|
||||||
|
HassServiceTarget,
|
||||||
|
UnsubscribeFunc,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -41,7 +44,6 @@ import {
|
|||||||
EntityRegistryEntry,
|
EntityRegistryEntry,
|
||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
} from "../data/entity_registry";
|
} from "../data/entity_registry";
|
||||||
import { Target } from "../data/target";
|
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./device/ha-device-picker";
|
import "./device/ha-device-picker";
|
||||||
@ -56,7 +58,7 @@ import "./ha-svg-icon";
|
|||||||
export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public value?: Target;
|
@property() public value?: HassServiceTarget;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@ -530,6 +532,9 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
.items {
|
.items {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
.mdc-chip-set {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
.mdc-chip.add {
|
.mdc-chip.add {
|
||||||
color: rgba(0, 0, 0, 0.87);
|
color: rgba(0, 0, 0, 0.87);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
HassEntityAttributeBase,
|
HassEntityAttributeBase,
|
||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
|
HassServiceTarget,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { computeObjectId } from "../common/entity/compute_object_id";
|
import { computeObjectId } from "../common/entity/compute_object_id";
|
||||||
import { navigate } from "../common/navigate";
|
import { navigate } from "../common/navigate";
|
||||||
@ -36,6 +37,7 @@ export interface EventAction {
|
|||||||
export interface ServiceAction {
|
export interface ServiceAction {
|
||||||
service: string;
|
service: string;
|
||||||
entity_id?: string;
|
entity_id?: string;
|
||||||
|
target?: HassServiceTarget;
|
||||||
data?: Record<string, any>;
|
data?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
export interface Target {
|
|
||||||
entity_id?: string[];
|
|
||||||
device_id?: string[];
|
|
||||||
area_id?: string[];
|
|
||||||
}
|
|
@ -15,7 +15,8 @@ export const demoConfig: HassConfig = {
|
|||||||
time_zone: "America/Los_Angeles",
|
time_zone: "America/Los_Angeles",
|
||||||
config_dir: "/config",
|
config_dir: "/config",
|
||||||
version: "DEMO",
|
version: "DEMO",
|
||||||
whitelist_external_dirs: [],
|
allowlist_external_dirs: [],
|
||||||
|
allowlist_external_urls: [],
|
||||||
config_source: "storage",
|
config_source: "storage",
|
||||||
safe_mode: false,
|
safe_mode: false,
|
||||||
state: STATE_RUNNING,
|
state: STATE_RUNNING,
|
||||||
|
@ -42,7 +42,6 @@ import "./types/ha-automation-action-wait_template";
|
|||||||
const OPTIONS = [
|
const OPTIONS = [
|
||||||
"condition",
|
"condition",
|
||||||
"delay",
|
"delay",
|
||||||
"device_id",
|
|
||||||
"event",
|
"event",
|
||||||
"scene",
|
"scene",
|
||||||
"service",
|
"service",
|
||||||
@ -50,6 +49,7 @@ const OPTIONS = [
|
|||||||
"wait_for_trigger",
|
"wait_for_trigger",
|
||||||
"repeat",
|
"repeat",
|
||||||
"choose",
|
"choose",
|
||||||
|
"device_id",
|
||||||
];
|
];
|
||||||
|
|
||||||
const getType = (action: Action) => {
|
const getType = (action: Action) => {
|
||||||
@ -99,6 +99,8 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
|
|
||||||
@property() public totalActions!: number;
|
@property() public totalActions!: number;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@internalProperty() private _warnings?: string[];
|
@internalProperty() private _warnings?: string[];
|
||||||
|
|
||||||
@internalProperty() private _uiModeAvailable = true;
|
@internalProperty() private _uiModeAvailable = true;
|
||||||
@ -116,8 +118,9 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
this._yamlMode = true;
|
this._yamlMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._yamlMode && this._yamlEditor) {
|
const yamlEditor = this._yamlEditor;
|
||||||
this._yamlEditor.setValue(this.action);
|
if (this._yamlMode && yamlEditor && yamlEditor.value !== this.action) {
|
||||||
|
yamlEditor.setValue(this.action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,6 +245,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
${dynamicElement(`ha-automation-action-${type}`, {
|
${dynamicElement(`ha-automation-action-${type}`, {
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
action: this.action,
|
action: this.action,
|
||||||
|
narrow: this.narrow,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
@ -18,6 +18,8 @@ import { HaDeviceAction } from "./types/ha-automation-action-device_id";
|
|||||||
export default class HaAutomationAction extends LitElement {
|
export default class HaAutomationAction extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@property() public actions!: Action[];
|
@property() public actions!: Action[];
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@ -28,6 +30,7 @@ export default class HaAutomationAction extends LitElement {
|
|||||||
.index=${idx}
|
.index=${idx}
|
||||||
.totalActions=${this.actions.length}
|
.totalActions=${this.actions.length}
|
||||||
.action=${action}
|
.action=${action}
|
||||||
|
.narrow=${this.narrow}
|
||||||
@duplicate=${this._duplicateAction}
|
@duplicate=${this._duplicateAction}
|
||||||
@move-action=${this._move}
|
@move-action=${this._move}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
|
@ -1,30 +1,24 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
customElement,
|
customElement,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
query,
|
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { html } from "lit-html";
|
import { html } from "lit-html";
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import { any, assert, object, optional, string } from "superstruct";
|
import { any, assert, object, optional, string } from "superstruct";
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
|
||||||
import { computeObjectId } from "../../../../../common/entity/compute_object_id";
|
|
||||||
import "../../../../../components/entity/ha-entity-picker";
|
|
||||||
import "../../../../../components/ha-service-picker";
|
|
||||||
import "../../../../../components/ha-yaml-editor";
|
|
||||||
import type { HaYamlEditor } from "../../../../../components/ha-yaml-editor";
|
|
||||||
import { ServiceAction } from "../../../../../data/script";
|
import { ServiceAction } from "../../../../../data/script";
|
||||||
import type { PolymerChangedEvent } from "../../../../../polymer-types";
|
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import { EntityIdOrAll } from "../../../../../common/structs/is-entity-id";
|
import { EntityIdOrAll } from "../../../../../common/structs/is-entity-id";
|
||||||
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
import { ActionElement } from "../ha-automation-action-row";
|
||||||
|
import "../../../../../components/ha-service-control";
|
||||||
|
|
||||||
const actionStruct = object({
|
const actionStruct = object({
|
||||||
service: optional(string()),
|
service: optional(string()),
|
||||||
entity_id: optional(EntityIdOrAll),
|
entity_id: optional(EntityIdOrAll),
|
||||||
|
target: optional(any()),
|
||||||
data: optional(any()),
|
data: optional(any()),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -34,36 +28,14 @@ export class HaServiceAction extends LitElement implements ActionElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public action!: ServiceAction;
|
@property({ attribute: false }) public action!: ServiceAction;
|
||||||
|
|
||||||
@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
private _actionData?: ServiceAction["data"];
|
@internalProperty() private _action!: ServiceAction;
|
||||||
|
|
||||||
public static get defaultConfig() {
|
public static get defaultConfig() {
|
||||||
return { service: "", data: {} };
|
return { service: "", data: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
private _domain = memoizeOne((service: string) => [computeDomain(service)]);
|
|
||||||
|
|
||||||
private _getServiceData = memoizeOne((service: string) => {
|
|
||||||
if (!service) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const domain = computeDomain(service);
|
|
||||||
const serviceName = computeObjectId(service);
|
|
||||||
const serviceDomains = this.hass.services;
|
|
||||||
if (!(domain in serviceDomains)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (!(serviceName in serviceDomains[domain])) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const fields = serviceDomains[domain][serviceName].fields;
|
|
||||||
return Object.keys(fields).map((field) => {
|
|
||||||
return { key: field, ...fields[field] };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
if (!changedProperties.has("action")) {
|
if (!changedProperties.has("action")) {
|
||||||
return;
|
return;
|
||||||
@ -73,73 +45,32 @@ export class HaServiceAction extends LitElement implements ActionElement {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
fireEvent(this, "ui-mode-not-available", error);
|
fireEvent(this, "ui-mode-not-available", error);
|
||||||
}
|
}
|
||||||
if (this._actionData && this._actionData !== this.action.data) {
|
if (this.action.entity_id) {
|
||||||
if (this._yamlEditor) {
|
this._action = {
|
||||||
this._yamlEditor.setValue(this.action.data);
|
...this.action,
|
||||||
}
|
data: { ...this.action.data, entity_id: this.action.entity_id },
|
||||||
|
};
|
||||||
|
delete this._action.entity_id;
|
||||||
|
} else {
|
||||||
|
this._action = this.action;
|
||||||
}
|
}
|
||||||
this._actionData = this.action.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const { service, data, entity_id } = this.action;
|
|
||||||
|
|
||||||
const serviceData = this._getServiceData(service);
|
|
||||||
const entity = serviceData.find((attr) => attr.key === "entity_id");
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-service-picker
|
<ha-service-control
|
||||||
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${service}
|
.value=${this._action}
|
||||||
@value-changed=${this._serviceChanged}
|
@value-changed=${this._actionChanged}
|
||||||
></ha-service-picker>
|
></ha-service-control>
|
||||||
${entity
|
|
||||||
? html`
|
|
||||||
<ha-entity-picker
|
|
||||||
.hass=${this.hass}
|
|
||||||
.value=${entity_id}
|
|
||||||
.label=${entity.description}
|
|
||||||
@value-changed=${this._entityPicked}
|
|
||||||
.includeDomains=${this._domain(service)}
|
|
||||||
allow-custom-entity
|
|
||||||
></ha-entity-picker>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<ha-yaml-editor
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.panel.config.automation.editor.actions.type.service.service_data"
|
|
||||||
)}
|
|
||||||
.name=${"data"}
|
|
||||||
.defaultValue=${data}
|
|
||||||
@value-changed=${this._dataChanged}
|
|
||||||
></ha-yaml-editor>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dataChanged(ev: CustomEvent): void {
|
private _actionChanged(ev) {
|
||||||
ev.stopPropagation();
|
if (ev.detail.value === this._action) {
|
||||||
if (!ev.detail.isValid) {
|
ev.stopPropagation();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this._actionData = ev.detail.value;
|
|
||||||
handleChangeEvent(this, ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _serviceChanged(ev: PolymerChangedEvent<string>) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (ev.detail.value === this.action.service) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: { ...this.action, service: ev.detail.value },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _entityPicked(ev: PolymerChangedEvent<string>) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
fireEvent(this, "value-changed", {
|
|
||||||
value: { ...this.action, entity_id: ev.detail.value },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,10 +252,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
|
|||||||
if (!name) {
|
if (!name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newVal = ev.detail.value;
|
const newVal = ev.detail.value;
|
||||||
if (target.type === "number") {
|
|
||||||
newVal = Number(newVal);
|
|
||||||
}
|
|
||||||
if ((this.config![name] || "") === newVal) {
|
if ((this.config![name] || "") === newVal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
@property() public stateObj?: HassEntity;
|
@property() public stateObj?: HassEntity;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`<ha-config-section .isWide=${this.isWide}>
|
return html`<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html` <span slot="header">${this.config.alias}</span> `
|
? html` <span slot="header">${this.config.alias}</span> `
|
||||||
: ""}
|
: ""}
|
||||||
@ -151,7 +151,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.triggers.header"
|
"ui.panel.config.automation.editor.triggers.header"
|
||||||
@ -180,7 +180,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
></ha-automation-trigger>
|
></ha-automation-trigger>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.header"
|
"ui.panel.config.automation.editor.conditions.header"
|
||||||
@ -209,7 +209,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.actions.header"
|
"ui.panel.config.automation.editor.actions.header"
|
||||||
@ -235,6 +235,7 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
.actions=${this.config.action}
|
.actions=${this.config.action}
|
||||||
@value-changed=${this._actionChanged}
|
@value-changed=${this._actionChanged}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
</ha-config-section>`;
|
</ha-config-section>`;
|
||||||
}
|
}
|
||||||
|
@ -80,13 +80,16 @@ export class HaConfigSection extends LitElement {
|
|||||||
font-weight: var(--paper-font-subhead_-_font-weight);
|
font-weight: var(--paper-font-subhead_-_font-weight);
|
||||||
line-height: var(--paper-font-subhead_-_line-height);
|
line-height: var(--paper-font-subhead_-_line-height);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400px;
|
|
||||||
margin-right: 40px;
|
|
||||||
opacity: var(--dark-primary-opacity);
|
opacity: var(--dark-primary-opacity);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.horizontal .intro {
|
||||||
|
max-width: 400px;
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
margin-top: -24px;
|
margin-top: -24px;
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
>
|
>
|
||||||
${this._config
|
${this._config
|
||||||
? html`
|
? html`
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html` <span slot="header">${name}</span> `
|
? html` <span slot="header">${name}</span> `
|
||||||
: ""}
|
: ""}
|
||||||
@ -253,7 +253,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.scene.editor.devices.header"
|
"ui.panel.config.scene.editor.devices.header"
|
||||||
@ -324,7 +324,7 @@ export class HaSceneEditor extends SubscribeMixin(
|
|||||||
|
|
||||||
${this.showAdvanced
|
${this.showAdvanced
|
||||||
? html`
|
? html`
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.scene.editor.entities.header"
|
"ui.panel.config.scene.editor.entities.header"
|
||||||
|
@ -189,7 +189,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
>
|
>
|
||||||
${this._config
|
${this._config
|
||||||
? html`
|
? html`
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`
|
? html`
|
||||||
<span slot="header">${this._config.alias}</span>
|
<span slot="header">${this._config.alias}</span>
|
||||||
@ -313,7 +313,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
<ha-config-section vertical .isWide=${this.isWide}>
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.script.editor.sequence"
|
"ui.panel.config.script.editor.sequence"
|
||||||
@ -350,7 +350,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||||||
`
|
`
|
||||||
: this._mode === "yaml"
|
: this._mode === "yaml"
|
||||||
? html`
|
? html`
|
||||||
<ha-config-section .isWide=${false}>
|
<ha-config-section vertical .isWide=${false}>
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`<span slot="header">${this._config?.alias}</span>`
|
? html`<span slot="header">${this._config?.alias}</span>`
|
||||||
: ``}
|
: ``}
|
||||||
|
@ -51,17 +51,24 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
enableShortcuts: true,
|
enableShortcuts: true,
|
||||||
moreInfoEntityId: null,
|
moreInfoEntityId: null,
|
||||||
hassUrl: (path = "") => new URL(path, auth.data.hassUrl).toString(),
|
hassUrl: (path = "") => new URL(path, auth.data.hassUrl).toString(),
|
||||||
callService: async (domain, service, serviceData = {}) => {
|
callService: async (domain, service, serviceData = {}, target) => {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log("Calling service", domain, service, serviceData);
|
console.log(
|
||||||
|
"Calling service",
|
||||||
|
domain,
|
||||||
|
service,
|
||||||
|
serviceData,
|
||||||
|
target
|
||||||
|
);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return (await callService(
|
return (await callService(
|
||||||
conn,
|
conn,
|
||||||
domain,
|
domain,
|
||||||
service,
|
service,
|
||||||
serviceData
|
serviceData,
|
||||||
|
target
|
||||||
)) as Promise<ServiceCallResponse>;
|
)) as Promise<ServiceCallResponse>;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
@ -71,6 +78,7 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
domain,
|
domain,
|
||||||
service,
|
service,
|
||||||
serviceData,
|
serviceData,
|
||||||
|
target,
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
Connection,
|
Connection,
|
||||||
HassConfig,
|
HassConfig,
|
||||||
HassEntities,
|
HassEntities,
|
||||||
|
HassServiceTarget,
|
||||||
HassServices,
|
HassServices,
|
||||||
MessageBase,
|
MessageBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
@ -178,6 +179,7 @@ export interface ServiceCallRequest {
|
|||||||
domain: string;
|
domain: string;
|
||||||
service: string;
|
service: string;
|
||||||
serviceData?: Record<string, any>;
|
serviceData?: Record<string, any>;
|
||||||
|
target?: HassServiceTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HomeAssistant {
|
export interface HomeAssistant {
|
||||||
@ -216,7 +218,8 @@ export interface HomeAssistant {
|
|||||||
callService(
|
callService(
|
||||||
domain: ServiceCallRequest["domain"],
|
domain: ServiceCallRequest["domain"],
|
||||||
service: ServiceCallRequest["service"],
|
service: ServiceCallRequest["service"],
|
||||||
serviceData?: ServiceCallRequest["serviceData"]
|
serviceData?: ServiceCallRequest["serviceData"],
|
||||||
|
target?: ServiceCallRequest["target"]
|
||||||
): Promise<ServiceCallResponse>;
|
): Promise<ServiceCallResponse>;
|
||||||
callApi<T>(
|
callApi<T>(
|
||||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||||
|
@ -8174,10 +8174,10 @@ hmac-drbg@^1.0.0:
|
|||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
minimalistic-crypto-utils "^1.0.1"
|
||||||
|
|
||||||
home-assistant-js-websocket@^5.4.1:
|
home-assistant-js-websocket@^5.8.1:
|
||||||
version "5.4.1"
|
version "5.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-5.4.1.tgz#3f677391b38e4feb24f1670e3a9b695767332a51"
|
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-5.8.1.tgz#4c5930aa47e7089f5806bb3d190ebe53697d2edc"
|
||||||
integrity sha512-FTVoO5yMSa2dy1ffZDvJy/r79VTjwFOzyP/bPld5lDHKbNyXC8wgqpn8Kdf5ZQISYJf1T1dfH+v2NYEngn5NgQ==
|
integrity sha512-2H3q8NK3WrT50iYODv95iz0E2E+nAUOD452V6lhBxhUTQlVFBsuxNMRTTbIZp+6Xab7ad84uF0z+hHFmBMq/Sw==
|
||||||
|
|
||||||
homedir-polyfill@^1.0.1:
|
homedir-polyfill@^1.0.1:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user