mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
20220907.0 (#13646)
This commit is contained in:
commit
c5428d8581
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20220906.0"
|
||||
version = "20220907.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
@ -239,10 +239,13 @@ export const getStates = (
|
||||
}
|
||||
break;
|
||||
case "light":
|
||||
if (attribute === "effect") {
|
||||
if (attribute === "effect" && state.attributes.effect_list) {
|
||||
result.push(...state.attributes.effect_list);
|
||||
} else if (attribute === "color_mode") {
|
||||
result.push(...state.attributes.color_modes);
|
||||
} else if (
|
||||
attribute === "color_mode" &&
|
||||
state.attributes.supported_color_modes
|
||||
) {
|
||||
result.push(...state.attributes.supported_color_modes);
|
||||
}
|
||||
break;
|
||||
case "media_player":
|
||||
|
@ -7,6 +7,7 @@ import { getStates } from "../../common/entity/get_states";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import { formatAttributeValue } from "../../data/entity_attributes";
|
||||
|
||||
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
|
||||
|
||||
@ -55,7 +56,7 @@ class HaEntityStatePicker extends LitElement {
|
||||
this.hass.locale,
|
||||
key
|
||||
)
|
||||
: key,
|
||||
: formatAttributeValue(this.hass, key),
|
||||
}))
|
||||
: [];
|
||||
}
|
||||
@ -69,16 +70,7 @@ class HaEntityStatePicker extends LitElement {
|
||||
return html`
|
||||
<ha-combo-box
|
||||
.hass=${this.hass}
|
||||
.value=${this.value
|
||||
? this.entityId && this.hass.states[this.entityId]
|
||||
? computeStateDisplay(
|
||||
this.hass.localize,
|
||||
this.hass.states[this.entityId],
|
||||
this.hass.locale,
|
||||
this.value
|
||||
)
|
||||
: this.value
|
||||
: ""}
|
||||
.value=${this.value}
|
||||
.autofocus=${this.autofocus}
|
||||
.label=${this.label ??
|
||||
this.hass.localize("ui.components.entity.entity-state-picker.state")}
|
||||
|
@ -17,8 +17,9 @@ export interface IconOverflowMenuItem {
|
||||
narrowOnly?: boolean;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
onClick: CallableFunction;
|
||||
action: () => any;
|
||||
warning?: boolean;
|
||||
divider?: boolean;
|
||||
}
|
||||
|
||||
@customElement("ha-icon-overflow-menu")
|
||||
@ -46,23 +47,23 @@ export class HaIconOverflowMenu extends LitElement {
|
||||
slot="trigger"
|
||||
></ha-icon-button>
|
||||
|
||||
${this.items.map(
|
||||
(item) => html`
|
||||
<mwc-list-item
|
||||
graphic="icon"
|
||||
.disabled=${item.disabled}
|
||||
@click=${item.action}
|
||||
class=${classMap({ warning: Boolean(item.warning) })}
|
||||
>
|
||||
<div slot="graphic">
|
||||
<ha-svg-icon
|
||||
class=${classMap({ warning: Boolean(item.warning) })}
|
||||
.path=${item.path}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
${item.label}
|
||||
</mwc-list-item>
|
||||
`
|
||||
${this.items.map((item) =>
|
||||
item.divider
|
||||
? html`<li divider role="separator"></li>`
|
||||
: html`<mwc-list-item
|
||||
graphic="icon"
|
||||
?disabled=${item.disabled}
|
||||
@click=${item.action}
|
||||
class=${classMap({ warning: Boolean(item.warning) })}
|
||||
>
|
||||
<div slot="graphic">
|
||||
<ha-svg-icon
|
||||
class=${classMap({ warning: Boolean(item.warning) })}
|
||||
.path=${item.path}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
${item.label}
|
||||
</mwc-list-item> `
|
||||
)}
|
||||
</ha-button-menu>`
|
||||
: html`
|
||||
@ -70,6 +71,8 @@ export class HaIconOverflowMenu extends LitElement {
|
||||
${this.items.map((item) =>
|
||||
item.narrowOnly
|
||||
? ""
|
||||
: item.divider
|
||||
? html`<div role="separator"></div>`
|
||||
: html`<div>
|
||||
${item.tooltip
|
||||
? html`<paper-tooltip animation-delay="0" position="left">
|
||||
@ -80,7 +83,7 @@ export class HaIconOverflowMenu extends LitElement {
|
||||
@click=${item.action}
|
||||
.label=${item.label}
|
||||
.path=${item.path}
|
||||
.disabled=${item.disabled}
|
||||
?disabled=${item.disabled}
|
||||
></ha-icon-button>
|
||||
</div> `
|
||||
)}
|
||||
@ -114,6 +117,13 @@ export class HaIconOverflowMenu extends LitElement {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
li[role="separator"] {
|
||||
border-bottom-color: var(--divider-color);
|
||||
}
|
||||
div[role="separator"] {
|
||||
border-right: 1px solid var(--divider-color);
|
||||
width: 1px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -314,11 +314,25 @@ let inititialAutomationEditorData: Partial<AutomationConfig> | undefined;
|
||||
export const getAutomationConfig = (hass: HomeAssistant, id: string) =>
|
||||
hass.callApi<AutomationConfig>("GET", `config/automation/config/${id}`);
|
||||
|
||||
export const saveAutomationConfig = (
|
||||
hass: HomeAssistant,
|
||||
id: string,
|
||||
config: AutomationConfig
|
||||
) => hass.callApi<void>("POST", `config/automation/config/${id}`, config);
|
||||
|
||||
export const showAutomationEditor = (data?: Partial<AutomationConfig>) => {
|
||||
inititialAutomationEditorData = data;
|
||||
navigate("/config/automation/edit/new");
|
||||
};
|
||||
|
||||
export const duplicateAutomation = (config: AutomationConfig) => {
|
||||
showAutomationEditor({
|
||||
...config,
|
||||
id: undefined,
|
||||
alias: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
export const getAutomationEditorInitData = () => {
|
||||
const data = inititialAutomationEditorData;
|
||||
inititialAutomationEditorData = undefined;
|
||||
|
@ -85,6 +85,13 @@ enum Protocols {
|
||||
ZWaveLongRange = 1,
|
||||
}
|
||||
|
||||
enum NodeType {
|
||||
Controller,
|
||||
/** @deprecated Use `NodeType["End Node"]` instead */
|
||||
"Routing End Node",
|
||||
"End Node" = 1,
|
||||
}
|
||||
|
||||
export enum FirmwareUpdateStatus {
|
||||
Error_Timeout = -1,
|
||||
Error_Checksum = 0,
|
||||
@ -142,12 +149,12 @@ export interface ZWaveJSController {
|
||||
sdk_version: string;
|
||||
type: number;
|
||||
own_node_id: number;
|
||||
is_secondary: boolean;
|
||||
is_primary: boolean;
|
||||
is_using_home_id_from_other_network: boolean;
|
||||
is_sis_present: boolean;
|
||||
was_real_primary: boolean;
|
||||
is_static_update_controller: boolean;
|
||||
is_slave: boolean;
|
||||
is_suc: boolean;
|
||||
node_type: NodeType;
|
||||
firmware_version: string;
|
||||
manufacturer_id: number;
|
||||
product_id: number;
|
||||
|
@ -98,7 +98,9 @@ class MoreInfoClimate extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${stateObj.attributes.temperature !== undefined &&
|
||||
${supportTargetTemperature &&
|
||||
!supportTargetTemperatureRange &&
|
||||
stateObj.attributes.temperature !== undefined &&
|
||||
stateObj.attributes.temperature !== null
|
||||
? html`
|
||||
<ha-climate-control
|
||||
@ -112,10 +114,11 @@ class MoreInfoClimate extends LitElement {
|
||||
></ha-climate-control>
|
||||
`
|
||||
: ""}
|
||||
${(stateObj.attributes.target_temp_low !== undefined &&
|
||||
${supportTargetTemperatureRange &&
|
||||
((stateObj.attributes.target_temp_low !== undefined &&
|
||||
stateObj.attributes.target_temp_low !== null) ||
|
||||
(stateObj.attributes.target_temp_high !== undefined &&
|
||||
stateObj.attributes.target_temp_high !== null)
|
||||
(stateObj.attributes.target_temp_high !== undefined &&
|
||||
stateObj.attributes.target_temp_high !== null))
|
||||
? html`
|
||||
<ha-climate-control
|
||||
.hass=${this.hass}
|
||||
|
@ -31,6 +31,7 @@ import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-fab";
|
||||
@ -44,6 +45,7 @@ import {
|
||||
deleteAutomation,
|
||||
getAutomationConfig,
|
||||
getAutomationEditorInitData,
|
||||
saveAutomationConfig,
|
||||
showAutomationEditor,
|
||||
triggerAutomationActions,
|
||||
} from "../../../data/automation";
|
||||
@ -153,7 +155,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
<mwc-list-item graphic="icon" @click=${this._showInfo}>
|
||||
<mwc-list-item
|
||||
graphic="icon"
|
||||
.disabled=${!stateObj}
|
||||
@click=${this._showInfo}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
@ -198,7 +204,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
<mwc-list-item
|
||||
graphic="icon"
|
||||
@click=${this._promptAutomationMode}
|
||||
.disabled=${!this.automationId || this._mode === "yaml"}
|
||||
.disabled=${this._mode === "yaml"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.change_mode"
|
||||
@ -210,15 +216,18 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<mwc-list-item
|
||||
graphic="icon"
|
||||
@click=${this._toggleReOrderMode}
|
||||
.disabled=${this._mode !== "gui"}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.re_order")}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
${this._config && !("use_blueprint" in this._config)
|
||||
? html`<mwc-list-item
|
||||
graphic="icon"
|
||||
@click=${this._toggleReOrderMode}
|
||||
.disabled=${this._mode === "yaml"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.re_order"
|
||||
)}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
|
||||
<mwc-list-item
|
||||
.disabled=${!this.automationId}
|
||||
@ -447,7 +456,7 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
this._dirty = false;
|
||||
this._config = config;
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
await showAlertDialog(this, {
|
||||
text:
|
||||
err.status_code === 404
|
||||
? this.hass.localize(
|
||||
@ -458,7 +467,8 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
"err_no",
|
||||
err.status_code
|
||||
),
|
||||
}).then(() => history.back());
|
||||
});
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,11 +544,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
confirmText: this.hass!.localize("ui.common.leave"),
|
||||
dismissText: this.hass!.localize("ui.common.stay"),
|
||||
confirm: () => {
|
||||
setTimeout(() => history.back());
|
||||
afterNextRender(() => history.back());
|
||||
},
|
||||
});
|
||||
} else {
|
||||
history.back();
|
||||
afterNextRender(() => history.back());
|
||||
}
|
||||
};
|
||||
|
||||
@ -577,8 +587,10 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
}
|
||||
|
||||
private async _delete() {
|
||||
await deleteAutomation(this.hass, this.automationId as string);
|
||||
history.back();
|
||||
if (this.automationId) {
|
||||
await deleteAutomation(this.hass, this.automationId);
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
private _switchUiMode() {
|
||||
@ -631,26 +643,21 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
await this._promptAutomationAlias();
|
||||
}
|
||||
|
||||
this.hass!.callApi(
|
||||
"POST",
|
||||
"config/automation/config/" + id,
|
||||
this._config
|
||||
).then(
|
||||
() => {
|
||||
this._dirty = false;
|
||||
try {
|
||||
await saveAutomationConfig(this.hass, id, this._config!);
|
||||
} catch (errors: any) {
|
||||
this._errors = errors.body.message || errors.error || errors.body;
|
||||
showToast(this, {
|
||||
message: errors.body.message || errors.error || errors.body,
|
||||
});
|
||||
throw errors;
|
||||
}
|
||||
|
||||
if (!this.automationId) {
|
||||
navigate(`/config/automation/edit/${id}`, { replace: true });
|
||||
}
|
||||
},
|
||||
(errors) => {
|
||||
this._errors = errors.body.message || errors.error || errors.body;
|
||||
showToast(this, {
|
||||
message: errors.body.message || errors.error || errors.body,
|
||||
});
|
||||
throw errors;
|
||||
}
|
||||
);
|
||||
this._dirty = false;
|
||||
|
||||
if (!this.automationId) {
|
||||
navigate(`/config/automation/edit/${id}`, { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
private _subscribeAutomationConfig(ev) {
|
||||
|
@ -33,8 +33,8 @@ import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
AutomationEntity,
|
||||
deleteAutomation,
|
||||
duplicateAutomation,
|
||||
getAutomationConfig,
|
||||
showAutomationEditor,
|
||||
triggerAutomationActions,
|
||||
} from "../../../data/automation";
|
||||
import {
|
||||
@ -212,6 +212,9 @@ class HaAutomationPicker extends LitElement {
|
||||
),
|
||||
action: () => this._showTrace(automation),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
path: mdiContentDuplicate,
|
||||
label: this.hass.localize(
|
||||
@ -348,19 +351,45 @@ class HaAutomationPicker extends LitElement {
|
||||
}
|
||||
|
||||
private async _delete(automation) {
|
||||
await deleteAutomation(this.hass, automation.attributes.id);
|
||||
try {
|
||||
await deleteAutomation(this.hass, automation.attributes.id);
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
text:
|
||||
err.status_code === 400
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.load_error_not_deletable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.load_error_unknown",
|
||||
"err_no",
|
||||
err.status_code
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async duplicate(automation) {
|
||||
const config = await getAutomationConfig(
|
||||
this.hass,
|
||||
automation.attributes.id
|
||||
);
|
||||
showAutomationEditor({
|
||||
...config,
|
||||
id: undefined,
|
||||
alias: undefined,
|
||||
});
|
||||
try {
|
||||
const config = await getAutomationConfig(
|
||||
this.hass,
|
||||
automation.attributes.id
|
||||
);
|
||||
duplicateAutomation(config);
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
text:
|
||||
err.status_code === 404
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.load_error_not_duplicable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.load_error_unknown",
|
||||
"err_no",
|
||||
err.status_code
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _showHelp() {
|
||||
@ -389,7 +418,7 @@ class HaAutomationPicker extends LitElement {
|
||||
);
|
||||
|
||||
if (automation?.attributes.id) {
|
||||
navigate(`/config/automation/edit/${automation?.attributes.id}`);
|
||||
navigate(`/config/automation/edit/${automation.attributes.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {
|
||||
mdiDownload,
|
||||
mdiPencil,
|
||||
mdiRayEndArrow,
|
||||
mdiRayStartArrow,
|
||||
mdiRefresh,
|
||||
@ -34,7 +33,7 @@ import {
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../../../layouts/hass-subpage";
|
||||
|
||||
@customElement("ha-automation-trace")
|
||||
export class HaAutomationTrace extends LitElement {
|
||||
@ -90,89 +89,76 @@ export class HaAutomationTrace extends LitElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const actionButtons = html`
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize("ui.panel.config.automation.trace.refresh")}
|
||||
.path=${mdiRefresh}
|
||||
@click=${this._refreshTraces}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.download_trace"
|
||||
)}
|
||||
.path=${mdiDownload}
|
||||
.disabled=${!this._trace}
|
||||
@click=${this._downloadTrace}
|
||||
></ha-icon-button>
|
||||
`;
|
||||
|
||||
return html`
|
||||
${devButtons}
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configSections.automations}
|
||||
>
|
||||
${this.narrow
|
||||
? html`<span slot="header">${title}</span>
|
||||
<div slot="toolbar-icon">${actionButtons}</div>`
|
||||
<hass-subpage .hass=${this.hass} .narrow=${this.narrow} .header=${title}>
|
||||
${!this.narrow && stateObj?.attributes.id
|
||||
? html`
|
||||
<a
|
||||
class="trace-link"
|
||||
href="/config/automation/edit/${stateObj.attributes.id}"
|
||||
slot="toolbar-icon"
|
||||
>
|
||||
<mwc-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.edit_automation"
|
||||
)}
|
||||
</mwc-button>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.refresh"
|
||||
)}
|
||||
.path=${mdiRefresh}
|
||||
@click=${this._refreshTraces}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.download_trace"
|
||||
)}
|
||||
.path=${mdiDownload}
|
||||
.disabled=${!this._trace}
|
||||
@click=${this._downloadTrace}
|
||||
></ha-icon-button>
|
||||
<div class="toolbar">
|
||||
${!this.narrow
|
||||
? html`<div>
|
||||
${title}
|
||||
<a
|
||||
class="linkButton"
|
||||
href="/config/automation/edit/${this.automationId}"
|
||||
>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.edit_automation"
|
||||
)}
|
||||
.path=${mdiPencil}
|
||||
tabindex="-1"
|
||||
></ha-icon-button>
|
||||
</a>
|
||||
</div>`
|
||||
: ""}
|
||||
${this._traces && this._traces.length > 0
|
||||
? html`
|
||||
<div>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.older_trace"
|
||||
)}
|
||||
.path=${mdiRayEndArrow}
|
||||
.disabled=${this._traces[this._traces.length - 1].run_id ===
|
||||
this._runId}
|
||||
@click=${this._pickOlderTrace}
|
||||
></ha-icon-button>
|
||||
<select .value=${this._runId} @change=${this._pickTrace}>
|
||||
${repeat(
|
||||
this._traces,
|
||||
(trace) => trace.run_id,
|
||||
(trace) =>
|
||||
html`<option value=${trace.run_id}>
|
||||
${formatDateTimeWithSeconds(
|
||||
new Date(trace.timestamp.start),
|
||||
this.hass.locale
|
||||
)}
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.newer_trace"
|
||||
)}
|
||||
.path=${mdiRayStartArrow}
|
||||
.disabled=${this._traces[0].run_id === this._runId}
|
||||
@click=${this._pickNewerTrace}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.older_trace"
|
||||
)}
|
||||
.path=${mdiRayEndArrow}
|
||||
.disabled=${this._traces[this._traces.length - 1].run_id ===
|
||||
this._runId}
|
||||
@click=${this._pickOlderTrace}
|
||||
></ha-icon-button>
|
||||
<select .value=${this._runId} @change=${this._pickTrace}>
|
||||
${repeat(
|
||||
this._traces,
|
||||
(trace) => trace.run_id,
|
||||
(trace) =>
|
||||
html`<option value=${trace.run_id}>
|
||||
${formatDateTimeWithSeconds(
|
||||
new Date(trace.timestamp.start),
|
||||
this.hass.locale
|
||||
)}
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
<ha-icon-button
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.newer_trace"
|
||||
)}
|
||||
.path=${mdiRayStartArrow}
|
||||
.disabled=${this._traces[0].run_id === this._runId}
|
||||
@click=${this._pickNewerTrace}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: ""}
|
||||
${!this.narrow ? html`<div>${actionButtons}</div>` : ""}
|
||||
</div>
|
||||
|
||||
${this._traces === undefined
|
||||
@ -276,7 +262,7 @@ export class HaAutomationTrace extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
</hass-tabs-subpage>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -465,7 +451,7 @@ export class HaAutomationTrace extends LitElement {
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
height: var(--header-height);
|
||||
padding: 0 16px;
|
||||
@ -476,15 +462,6 @@ export class HaAutomationTrace extends LitElement {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar > * {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:host([narrow]) .toolbar > * {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.main {
|
||||
height: calc(100% - 56px);
|
||||
display: flex;
|
||||
@ -520,6 +497,9 @@ export class HaAutomationTrace extends LitElement {
|
||||
.linkButton {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.trace-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -4,12 +4,9 @@ import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-entity-toggle";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-textarea";
|
||||
import "../../../components/ha-textfield";
|
||||
import {
|
||||
Condition,
|
||||
ManualAutomationConfig,
|
||||
@ -215,23 +212,12 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
.link-button-row {
|
||||
padding: 14px;
|
||||
}
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-entity-toggle {
|
||||
margin-right: 8px;
|
||||
}
|
||||
ha-select,
|
||||
.max {
|
||||
margin-top: 16px;
|
||||
width: 200px;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -247,35 +233,6 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
.header a {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
ha-expansion-panel {
|
||||
--expansion-panel-summary-padding: 0 0 0 8px;
|
||||
--expansion-panel-content-padding: 0;
|
||||
}
|
||||
.card-content {
|
||||
padding: 16px;
|
||||
}
|
||||
.settings-icon {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 870px) {
|
||||
.settings-icon {
|
||||
display: inline-block;
|
||||
color: var(--secondary-text-color);
|
||||
opacity: 0.9;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.disabled-bar {
|
||||
background: var(--divider-color, #e0e0e0);
|
||||
text-align: center;
|
||||
border-top-right-radius: var(--ha-card-border-radius);
|
||||
border-top-left-radius: var(--ha-card-border-radius);
|
||||
}
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
|
@ -126,3 +126,9 @@ export class HaConfigSection extends LitElement {
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-section": HaConfigSection;
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +157,9 @@ class HaSceneDashboard extends LitElement {
|
||||
),
|
||||
action: () => this._activateScene(scene),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
path: mdiContentDuplicate,
|
||||
label: this.hass.localize(
|
||||
|
@ -29,6 +29,7 @@ import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import "../../../components/device/ha-device-picker";
|
||||
import "../../../components/entity/ha-entities-picker";
|
||||
import "../../../components/ha-area-picker";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
@ -279,7 +280,12 @@ export class HaSceneEditor extends SubscribeMixin(
|
||||
>
|
||||
${this._config
|
||||
? html`
|
||||
<div class="container">
|
||||
<div
|
||||
class=${classMap({
|
||||
container: true,
|
||||
narrow: !this.isWide,
|
||||
})}
|
||||
>
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<ha-textfield
|
||||
@ -954,15 +960,13 @@ export class HaSceneEditor extends SubscribeMixin(
|
||||
overflow: hidden;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 24px;
|
||||
}
|
||||
.container > * {
|
||||
padding: 28px 20px 0;
|
||||
max-width: 1040px;
|
||||
flex: 1 1 auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.narrow.container {
|
||||
max-width: 640px;
|
||||
}
|
||||
|
||||
.errors {
|
||||
padding: 20px;
|
||||
font-weight: bold;
|
||||
|
@ -45,13 +45,14 @@ export class HaBlueprintScriptEditor extends LitElement {
|
||||
|
||||
protected render() {
|
||||
const blueprint = this._blueprint;
|
||||
return html` <ha-config-section vertical .isWide=${this.isWide}>
|
||||
<span slot="header"
|
||||
>${this.hass.localize(
|
||||
return html`
|
||||
<ha-card
|
||||
outlined
|
||||
class="blueprint"
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.blueprint.header"
|
||||
)}</span
|
||||
)}
|
||||
>
|
||||
<ha-card outlined>
|
||||
<div class="blueprint-picker-container">
|
||||
${this._blueprints
|
||||
? Object.keys(this._blueprints).length
|
||||
@ -118,7 +119,7 @@ export class HaBlueprintScriptEditor extends LitElement {
|
||||
</p>`}`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>`;
|
||||
`;
|
||||
}
|
||||
|
||||
private async _getBlueprints() {
|
||||
@ -173,22 +174,50 @@ export class HaBlueprintScriptEditor extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
ha-card.blueprint {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.padding {
|
||||
padding: 16px;
|
||||
}
|
||||
.link-button-row {
|
||||
padding: 14px;
|
||||
}
|
||||
.blueprint-picker-container {
|
||||
padding: 16px;
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
ha-textfield,
|
||||
ha-blueprint-picker {
|
||||
display: block;
|
||||
}
|
||||
h3 {
|
||||
margin: 16px;
|
||||
}
|
||||
.introduction {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.introduction a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.description {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
ha-settings-row {
|
||||
--paper-time-input-justify-content: flex-end;
|
||||
--settings-row-content-width: 100%;
|
||||
--settings-row-prefix-display: contents;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
}
|
||||
:host(:not([narrow])) ha-settings-row ha-textfield,
|
||||
:host(:not([narrow])) ha-settings-row ha-selector {
|
||||
width: 60%;
|
||||
ha-alert {
|
||||
margin-bottom: 16px;
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
mdiCheck,
|
||||
@ -6,7 +5,10 @@ import {
|
||||
mdiContentSave,
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiHelpCircle,
|
||||
mdiInformationOutline,
|
||||
mdiPlay,
|
||||
mdiSort,
|
||||
mdiTransitConnection,
|
||||
} from "@mdi/js";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
@ -21,6 +23,7 @@ import {
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
@ -38,7 +41,6 @@ import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||
import {
|
||||
Action,
|
||||
deleteScript,
|
||||
getScriptConfig,
|
||||
getScriptEditorInitData,
|
||||
@ -59,6 +61,8 @@ import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { HaDeviceAction } from "../automation/action/types/ha-automation-action-device_id";
|
||||
import "./blueprint-script-editor";
|
||||
import "./manual-script-editor";
|
||||
import type { HaManualScriptEditor } from "./manual-script-editor";
|
||||
|
||||
export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@ -83,7 +87,10 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
|
||||
@state() private _mode: "gui" | "yaml" = "gui";
|
||||
|
||||
@query("ha-yaml-editor", true) private _editor?: HaYamlEditor;
|
||||
@query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
@query("manual-script-editor")
|
||||
private _manualEditor?: HaManualScriptEditor;
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
@ -175,23 +182,90 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
.backCallback=${this._backTapped}
|
||||
.header=${!this._config?.alias ? "" : this._config.alias}
|
||||
>
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
slot="toolbar-icon"
|
||||
@action=${this._handleMenuAction}
|
||||
activatable
|
||||
>
|
||||
${this.scriptEntityId && !this.narrow
|
||||
? html`
|
||||
<a
|
||||
class="trace-link"
|
||||
href="/config/script/trace/${this.scriptEntityId}"
|
||||
slot="toolbar-icon"
|
||||
>
|
||||
<mwc-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.show_trace"
|
||||
)}
|
||||
</mwc-button>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
|
||||
<mwc-list-item
|
||||
graphic="icon"
|
||||
.disabled=${!this.scriptEntityId}
|
||||
@click=${this._showInfo}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.script.editor.show_info")}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiInformationOutline}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
<mwc-list-item
|
||||
graphic="icon"
|
||||
.disabled=${!this.scriptEntityId}
|
||||
@click=${this._runScript}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.script.picker.run_script")}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
|
||||
${this.scriptEntityId && this.narrow
|
||||
? html`
|
||||
<a href="/config/script/trace/${this.scriptEntityId}">
|
||||
<mwc-list-item graphic="icon">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.show_trace"
|
||||
)}
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mdiTransitConnection}
|
||||
></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
${this._config && !("use_blueprint" in this._config)
|
||||
? html`
|
||||
<mwc-list-item
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.re_order"
|
||||
)}
|
||||
graphic="icon"
|
||||
.disabled=${this._mode !== "gui"}
|
||||
@click=${this._toggleReOrderMode}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.re_order"
|
||||
)}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSort}></ha-svg-icon>
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<li divider role="separator"></li>
|
||||
|
||||
<mwc-list-item
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)}
|
||||
graphic="icon"
|
||||
@click=${this._switchUiMode}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
|
||||
${this._mode === "gui"
|
||||
@ -209,6 +283,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
graphic="icon"
|
||||
@click=${this._switchYamlMode}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
|
||||
${this._mode === "yaml"
|
||||
@ -230,6 +305,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
"ui.panel.config.script.picker.duplicate"
|
||||
)}
|
||||
graphic="icon"
|
||||
@click=${this._duplicate}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.script.picker.duplicate")}
|
||||
<ha-svg-icon
|
||||
@ -245,6 +321,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
)}
|
||||
class=${classMap({ warning: Boolean(this.scriptEntityId) })}
|
||||
graphic="icon"
|
||||
@click=${this._deleteConfirm}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.script.picker.delete")}
|
||||
<ha-svg-icon
|
||||
@ -270,47 +347,20 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
>
|
||||
${this._config
|
||||
? html`
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<ha-form
|
||||
.schema=${schema}
|
||||
.data=${data}
|
||||
.hass=${this.hass}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
.computeHelper=${this._computeHelperCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
</div>
|
||||
${this.scriptEntityId
|
||||
? html`
|
||||
<div
|
||||
class="card-actions layout horizontal justified center"
|
||||
>
|
||||
<a
|
||||
href="/config/script/trace/${this
|
||||
.scriptEntityId}"
|
||||
>
|
||||
<mwc-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.show_trace"
|
||||
)}
|
||||
</mwc-button>
|
||||
</a>
|
||||
<mwc-button
|
||||
@click=${this._runScript}
|
||||
title=${this.hass.localize(
|
||||
"ui.panel.config.script.picker.run_script"
|
||||
)}
|
||||
?disabled=${this._dirty}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.picker.run_script"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ``}
|
||||
</ha-card>
|
||||
<div class="config-container">
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<ha-form
|
||||
.schema=${schema}
|
||||
.data=${data}
|
||||
.hass=${this.hass}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
.computeHelper=${this._computeHelperCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
|
||||
${"use_blueprint" in this._config
|
||||
? html`
|
||||
@ -323,36 +373,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
></blueprint-script-editor>
|
||||
`
|
||||
: html`
|
||||
<div class="header">
|
||||
<h2 id="sequence-heading" class="name">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.editor.sequence"
|
||||
)}
|
||||
</h2>
|
||||
<a
|
||||
href=${documentationUrl(
|
||||
this.hass,
|
||||
"/docs/scripts/"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.script.editor.link_available_actions"
|
||||
)}
|
||||
></ha-icon-button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ha-automation-action
|
||||
role="region"
|
||||
aria-labelledby="sequence-heading"
|
||||
.actions=${this._config.sequence}
|
||||
@value-changed=${this._sequenceChanged}
|
||||
<manual-script-editor
|
||||
.hass=${this.hass}
|
||||
></ha-automation-action>
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
.config=${this._config}
|
||||
@value-changed=${this._configChanged}
|
||||
></manual-script-editor>
|
||||
`}
|
||||
`
|
||||
: ""}
|
||||
@ -360,28 +387,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
`
|
||||
: this._mode === "yaml"
|
||||
? html`
|
||||
${!this.narrow
|
||||
? html`
|
||||
<ha-card outlined>
|
||||
<div class="card-header">${this._config?.alias}</div>
|
||||
<div
|
||||
class="card-actions layout horizontal justified center"
|
||||
>
|
||||
<mwc-button
|
||||
@click=${this._runScript}
|
||||
title=${this.hass.localize(
|
||||
"ui.panel.config.script.picker.run_script"
|
||||
)}
|
||||
?disabled=${this._dirty}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.picker.run_script"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: ``}
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
@ -518,6 +523,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
private async _showInfo() {
|
||||
if (!this.scriptEntityId) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "hass-more-info", { entityId: this.scriptEntityId });
|
||||
}
|
||||
|
||||
private async _runScript(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
await triggerScript(this.hass, this.scriptEntityId as string);
|
||||
@ -625,22 +637,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _sequenceChanged(ev: CustomEvent): void {
|
||||
this._config = {
|
||||
...this._config!,
|
||||
sequence: ev.detail.value as Action[],
|
||||
};
|
||||
this._errors = undefined;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _preprocessYaml() {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
private async _copyYaml(): Promise<void> {
|
||||
if (this._editor?.yaml) {
|
||||
await copyToClipboard(this._editor.yaml);
|
||||
if (this._yamlEditor?.yaml) {
|
||||
await copyToClipboard(this._yamlEditor.yaml);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
@ -715,20 +718,17 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
history.back();
|
||||
}
|
||||
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._mode = "gui";
|
||||
break;
|
||||
case 1:
|
||||
this._mode = "yaml";
|
||||
break;
|
||||
case 2:
|
||||
this._duplicate();
|
||||
break;
|
||||
case 3:
|
||||
this._deleteConfirm();
|
||||
break;
|
||||
private _switchUiMode() {
|
||||
this._mode = "gui";
|
||||
}
|
||||
|
||||
private _switchYamlMode() {
|
||||
this._mode = "yaml";
|
||||
}
|
||||
|
||||
private _toggleReOrderMode() {
|
||||
if (this._manualEditor) {
|
||||
this._manualEditor.reOrderMode = !this._manualEditor.reOrderMode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -787,15 +787,19 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
font-weight: bold;
|
||||
color: var(--error-color);
|
||||
}
|
||||
.content {
|
||||
padding: 16px 16px 20px;
|
||||
}
|
||||
.yaml-mode {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.config-container,
|
||||
manual-script-editor,
|
||||
blueprint-script-editor {
|
||||
margin: 0 auto;
|
||||
max-width: 1040px;
|
||||
padding: 28px 20px 0;
|
||||
}
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--code-mirror-height: 100%;
|
||||
@ -836,6 +840,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
||||
.header a {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.trace-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
ha-button-menu a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -155,6 +155,9 @@ class HaScriptPicker extends LitElement {
|
||||
),
|
||||
action: () => this._showTrace(script),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
},
|
||||
{
|
||||
path: mdiContentDuplicate,
|
||||
label: this.hass.localize(
|
||||
@ -290,16 +293,31 @@ class HaScriptPicker extends LitElement {
|
||||
}
|
||||
|
||||
private async _duplicate(script: any) {
|
||||
const config = await getScriptConfig(
|
||||
this.hass,
|
||||
computeObjectId(script.entity_id)
|
||||
);
|
||||
showScriptEditor({
|
||||
...config,
|
||||
alias: `${config?.alias} (${this.hass.localize(
|
||||
"ui.panel.config.script.picker.duplicate"
|
||||
)})`,
|
||||
});
|
||||
try {
|
||||
const config = await getScriptConfig(
|
||||
this.hass,
|
||||
computeObjectId(script.entity_id)
|
||||
);
|
||||
showScriptEditor({
|
||||
...config,
|
||||
alias: `${config?.alias} (${this.hass.localize(
|
||||
"ui.panel.config.script.picker.duplicate"
|
||||
)})`,
|
||||
});
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
text:
|
||||
err.status_code === 404
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.script.editor.load_error_not_duplicable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.script.editor.load_error_unknown",
|
||||
"err_no",
|
||||
err.status_code
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _deleteConfirm(script: any) {
|
||||
@ -312,7 +330,22 @@ class HaScriptPicker extends LitElement {
|
||||
}
|
||||
|
||||
private async _delete(script: any) {
|
||||
await deleteScript(this.hass, computeObjectId(script.entity_id));
|
||||
try {
|
||||
await deleteScript(this.hass, computeObjectId(script.entity_id));
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
text:
|
||||
err.status_code === 400
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.script.editor.load_error_not_deletable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.script.editor.load_error_unknown",
|
||||
"err_no",
|
||||
err.status_code
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {
|
||||
mdiDownload,
|
||||
mdiPencil,
|
||||
mdiRayEndArrow,
|
||||
mdiRayStartArrow,
|
||||
mdiRefresh,
|
||||
@ -34,7 +33,7 @@ import {
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../../../layouts/hass-subpage";
|
||||
|
||||
@customElement("ha-script-trace")
|
||||
export class HaScriptTrace extends LitElement {
|
||||
@ -88,81 +87,72 @@ export class HaScriptTrace extends LitElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const actionButtons = html`
|
||||
<ha-icon-button
|
||||
label="Refresh"
|
||||
@click=${this._refreshTraces}
|
||||
.path=${mdiRefresh}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.disabled=${!this._trace}
|
||||
label="Download Trace"
|
||||
@click=${this._downloadTrace}
|
||||
.path=${mdiDownload}
|
||||
></ha-icon-button>
|
||||
`;
|
||||
|
||||
return html`
|
||||
${devButtons}
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configSections.automations}
|
||||
>
|
||||
${this.narrow
|
||||
? html`<span slot="header"> ${title} </span>
|
||||
<div slot="toolbar-icon">${actionButtons}</div>`
|
||||
<hass-subpage .hass=${this.hass} .narrow=${this.narrow} .header=${title}>
|
||||
${!this.narrow && this.scriptEntityId
|
||||
? html`
|
||||
<a
|
||||
class="trace-link"
|
||||
href="/config/script/edit/${this.scriptEntityId}"
|
||||
slot="toolbar-icon"
|
||||
>
|
||||
<mwc-button>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.script.trace.edit_script"
|
||||
)}
|
||||
</mwc-button>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.refresh"
|
||||
)}
|
||||
.path=${mdiRefresh}
|
||||
@click=${this._refreshTraces}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.download_trace"
|
||||
)}
|
||||
.path=${mdiDownload}
|
||||
.disabled=${!this._trace}
|
||||
@click=${this._downloadTrace}
|
||||
></ha-icon-button>
|
||||
<div class="toolbar">
|
||||
${!this.narrow
|
||||
? html`<div>
|
||||
${title}
|
||||
<a
|
||||
class="linkButton"
|
||||
href="/config/script/edit/${this.scriptEntityId}"
|
||||
>
|
||||
<ha-icon-button
|
||||
label="Edit Script"
|
||||
tabindex="-1"
|
||||
.path=${mdiPencil}
|
||||
></ha-icon-button>
|
||||
</a>
|
||||
</div>`
|
||||
: ""}
|
||||
${this._traces && this._traces.length > 0
|
||||
? html`
|
||||
<div>
|
||||
<ha-icon-button
|
||||
.disabled=${this._traces[this._traces.length - 1].run_id ===
|
||||
this._runId}
|
||||
label="Older trace"
|
||||
@click=${this._pickOlderTrace}
|
||||
.path=${mdiRayEndArrow}
|
||||
></ha-icon-button>
|
||||
<select .value=${this._runId} @change=${this._pickTrace}>
|
||||
${repeat(
|
||||
this._traces,
|
||||
(trace) => trace.run_id,
|
||||
(trace) =>
|
||||
html`<option value=${trace.run_id}>
|
||||
${formatDateTimeWithSeconds(
|
||||
new Date(trace.timestamp.start),
|
||||
this.hass.locale
|
||||
)}
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
<ha-icon-button
|
||||
.disabled=${this._traces[0].run_id === this._runId}
|
||||
label="Newer trace"
|
||||
@click=${this._pickNewerTrace}
|
||||
.path=${mdiRayStartArrow}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-icon-button
|
||||
.disabled=${this._traces[this._traces.length - 1].run_id ===
|
||||
this._runId}
|
||||
label="Older trace"
|
||||
@click=${this._pickOlderTrace}
|
||||
.path=${mdiRayEndArrow}
|
||||
></ha-icon-button>
|
||||
<select .value=${this._runId} @change=${this._pickTrace}>
|
||||
${repeat(
|
||||
this._traces,
|
||||
(trace) => trace.run_id,
|
||||
(trace) =>
|
||||
html`<option value=${trace.run_id}>
|
||||
${formatDateTimeWithSeconds(
|
||||
new Date(trace.timestamp.start),
|
||||
this.hass.locale
|
||||
)}
|
||||
</option>`
|
||||
)}
|
||||
</select>
|
||||
<ha-icon-button
|
||||
.disabled=${this._traces[0].run_id === this._runId}
|
||||
label="Newer trace"
|
||||
@click=${this._pickNewerTrace}
|
||||
.path=${mdiRayStartArrow}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: ""}
|
||||
${!this.narrow ? html`<div>${actionButtons}</div>` : ""}
|
||||
</div>
|
||||
|
||||
${this._traces === undefined
|
||||
@ -266,7 +256,7 @@ export class HaScriptTrace extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
</hass-tabs-subpage>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -447,26 +437,14 @@ export class HaScriptTrace extends LitElement {
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 20px;
|
||||
justify-content: center;
|
||||
height: var(--header-height);
|
||||
padding: 0 16px;
|
||||
background-color: var(--primary-background-color);
|
||||
font-weight: 400;
|
||||
color: var(--app-header-text-color, white);
|
||||
border-bottom: var(--app-header-border-bottom, none);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar > * {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:host([narrow]) .toolbar > * {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.main {
|
||||
height: calc(100% - 56px);
|
||||
display: flex;
|
||||
@ -499,6 +477,9 @@ export class HaScriptTrace extends LitElement {
|
||||
.linkButton {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.trace-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
135
src/panels/config/script/manual-script-editor.ts
Normal file
135
src/panels/config/script/manual-script-editor.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { Action, ScriptConfig } from "../../../data/script";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import "../automation/action/ha-automation-action";
|
||||
|
||||
@customElement("manual-script-editor")
|
||||
export class HaManualScriptEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ attribute: false }) public config!: ScriptConfig;
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: "re-order-mode" })
|
||||
public reOrderMode = false;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
${this.reOrderMode
|
||||
? html`
|
||||
<ha-alert
|
||||
alert-type="info"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.re_order_mode.title"
|
||||
)}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.re_order_mode.description"
|
||||
)}
|
||||
<mwc-button slot="action" @click=${this._exitReOrderMode}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.re_order_mode.exit"
|
||||
)}
|
||||
</mwc-button>
|
||||
</ha-alert>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<div class="header">
|
||||
<h2 id="sequence-heading" class="name">
|
||||
${this.hass.localize("ui.panel.config.script.editor.sequence")}
|
||||
</h2>
|
||||
<a
|
||||
href=${documentationUrl(this.hass, "/docs/scripts/")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.script.editor.link_available_actions"
|
||||
)}
|
||||
></ha-icon-button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ha-automation-action
|
||||
role="region"
|
||||
aria-labelledby="sequence-heading"
|
||||
.actions=${this.config.sequence}
|
||||
@value-changed=${this._sequenceChanged}
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.reOrderMode=${this.reOrderMode}
|
||||
></ha-automation-action>
|
||||
`;
|
||||
}
|
||||
|
||||
private _sequenceChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.config!, sequence: ev.detail.value as Action[] },
|
||||
});
|
||||
}
|
||||
|
||||
private _exitReOrderMode() {
|
||||
this.reOrderMode = !this.reOrderMode;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.header:first-child {
|
||||
margin-top: -16px;
|
||||
}
|
||||
.header .name {
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
flex: 1;
|
||||
}
|
||||
.header a {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"manual-script-editor": HaManualScriptEditor;
|
||||
}
|
||||
}
|
@ -1828,6 +1828,8 @@
|
||||
"default_name": "New Automation",
|
||||
"missing_name": "Cannot save automation without a name",
|
||||
"load_error_not_editable": "Only automations in automations.yaml are editable.",
|
||||
"load_error_not_duplicable": "Only automations in automations.yaml can be duplicated.",
|
||||
"load_error_not_deletable": "Only automations in automations.yaml can be deleted.",
|
||||
"load_error_unknown": "Error loading automation ({err_no}).",
|
||||
"save": "Save",
|
||||
"unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?",
|
||||
@ -2270,7 +2272,7 @@
|
||||
"no_scripts": "We couldn't find any scripts",
|
||||
"add_script": "Add script",
|
||||
"run_script": "Run script",
|
||||
"run": "[%key:ui::panel::config::automation::editor::run%]",
|
||||
"run": "[%key:ui::panel::config::automation::editor::actions::run%]",
|
||||
"show_trace": "[%key:ui::panel::config::automation::editor::show_trace%]",
|
||||
"show_info": "[%key:ui::panel::config::automation::editor::show_info%]",
|
||||
"headers": {
|
||||
@ -2288,6 +2290,7 @@
|
||||
"id_already_exists": "This ID already exists",
|
||||
"introduction": "Use scripts to run a sequence of actions.",
|
||||
"show_trace": "[%key:ui::panel::config::automation::editor::show_trace%]",
|
||||
"show_info": "[%key:ui::panel::config::automation::editor::show_info%]",
|
||||
"header": "Script: {name}",
|
||||
"default_name": "New Script",
|
||||
"modes": {
|
||||
@ -2303,13 +2306,16 @@
|
||||
"parallel": "Max number of parallel runs"
|
||||
},
|
||||
"load_error_not_editable": "Only scripts inside scripts.yaml are editable.",
|
||||
"load_error_not_duplicable": "Only scripts in scripts.yaml can be duplicated.",
|
||||
"load_error_not_deletable": "Only scripts in scripts.yaml can be deleted.",
|
||||
"load_error_unknown": "Error loading script ({err_no}).",
|
||||
"delete_confirm": "Are you sure you want to delete this script?",
|
||||
"save_script": "Save script",
|
||||
"sequence": "Sequence",
|
||||
"sequence_sentence": "The sequence of actions of this script.",
|
||||
"link_available_actions": "Learn more about available actions."
|
||||
}
|
||||
},
|
||||
"trace": { "edit_script": "Edit script" }
|
||||
},
|
||||
"scene": {
|
||||
"caption": "Scenes",
|
||||
|
Loading…
x
Reference in New Issue
Block a user