diff --git a/pyproject.toml b/pyproject.toml index 9ecb9ce073..17f53754f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/src/common/entity/get_states.ts b/src/common/entity/get_states.ts index 7adddf87e1..f79cc1a4d0 100644 --- a/src/common/entity/get_states.ts +++ b/src/common/entity/get_states.ts @@ -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": diff --git a/src/components/entity/ha-entity-state-picker.ts b/src/components/entity/ha-entity-state-picker.ts index c9c8965e94..21f83ae948 100644 --- a/src/components/entity/ha-entity-state-picker.ts +++ b/src/components/entity/ha-entity-state-picker.ts @@ -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` any; warning?: boolean; + divider?: boolean; } @customElement("ha-icon-overflow-menu") @@ -46,23 +47,23 @@ export class HaIconOverflowMenu extends LitElement { slot="trigger" > - ${this.items.map( - (item) => html` - -
- -
- ${item.label} -
- ` + ${this.items.map((item) => + item.divider + ? html`
  • ` + : html` +
    + +
    + ${item.label} +
    ` )} ` : html` @@ -70,6 +71,8 @@ export class HaIconOverflowMenu extends LitElement { ${this.items.map((item) => item.narrowOnly ? "" + : item.divider + ? html`
    ` : html`
    ${item.tooltip ? html` @@ -80,7 +83,7 @@ export class HaIconOverflowMenu extends LitElement { @click=${item.action} .label=${item.label} .path=${item.path} - .disabled=${item.disabled} + ?disabled=${item.disabled} >
    ` )} @@ -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; + } `, ]; } diff --git a/src/data/automation.ts b/src/data/automation.ts index 4f7d31d1eb..d8ee247032 100644 --- a/src/data/automation.ts +++ b/src/data/automation.ts @@ -314,11 +314,25 @@ let inititialAutomationEditorData: Partial | undefined; export const getAutomationConfig = (hass: HomeAssistant, id: string) => hass.callApi("GET", `config/automation/config/${id}`); +export const saveAutomationConfig = ( + hass: HomeAssistant, + id: string, + config: AutomationConfig +) => hass.callApi("POST", `config/automation/config/${id}`, config); + export const showAutomationEditor = (data?: Partial) => { 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; diff --git a/src/data/zwave_js.ts b/src/data/zwave_js.ts index d6898c5f98..7863c3de84 100644 --- a/src/data/zwave_js.ts +++ b/src/data/zwave_js.ts @@ -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; diff --git a/src/dialogs/more-info/controls/more-info-climate.ts b/src/dialogs/more-info/controls/more-info-climate.ts index 2a66080055..1715a94703 100644 --- a/src/dialogs/more-info/controls/more-info-climate.ts +++ b/src/dialogs/more-info/controls/more-info-climate.ts @@ -98,7 +98,9 @@ class MoreInfoClimate extends LitElement { ` : ""} - ${stateObj.attributes.temperature !== undefined && + ${supportTargetTemperature && + !supportTargetTemperatureRange && + stateObj.attributes.temperature !== undefined && stateObj.attributes.temperature !== null ? html` ` : ""} - ${(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` - + ${this.hass.localize("ui.panel.config.automation.editor.show_info")} ${this.hass.localize( "ui.panel.config.automation.editor.change_mode" @@ -210,15 +216,18 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { ` : ""} - - - ${this.hass.localize("ui.panel.config.automation.editor.re_order")} - - + ${this._config && !("use_blueprint" in this._config) + ? html` + ${this.hass.localize( + "ui.panel.config.automation.editor.re_order" + )} + + ` + : ""} 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) { diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 7ee6e86dda..dc395aefaf 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -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}`); } } diff --git a/src/panels/config/automation/ha-automation-trace.ts b/src/panels/config/automation/ha-automation-trace.ts index 1f5f4569b8..b2884f064f 100644 --- a/src/panels/config/automation/ha-automation-trace.ts +++ b/src/panels/config/automation/ha-automation-trace.ts @@ -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 { `; } - const actionButtons = html` - - - `; - return html` ${devButtons} - - ${this.narrow - ? html`${title} -
    ${actionButtons}
    ` + + ${!this.narrow && stateObj?.attributes.id + ? html` + + + ${this.hass.localize( + "ui.panel.config.automation.trace.edit_automation" + )} + + + ` : ""} + +
    - ${!this.narrow - ? html`
    - ${title} - - - -
    ` - : ""} ${this._traces && this._traces.length > 0 ? html` -
    - - - -
    + + + ` : ""} - ${!this.narrow ? html`
    ${actionButtons}
    ` : ""}
    ${this._traces === undefined @@ -276,7 +262,7 @@ export class HaAutomationTrace extends LitElement { `} -
    + `; } @@ -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; + } `, ]; } diff --git a/src/panels/config/automation/manual-automation-editor.ts b/src/panels/config/automation/manual-automation-editor.ts index ebe14f3e1d..d13d54ae34 100644 --- a/src/panels/config/automation/manual-automation-editor.ts +++ b/src/panels/config/automation/manual-automation-editor.ts @@ -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; diff --git a/src/panels/config/ha-config-section.ts b/src/panels/config/ha-config-section.ts index 16dc7bc389..37ab1808a0 100644 --- a/src/panels/config/ha-config-section.ts +++ b/src/panels/config/ha-config-section.ts @@ -126,3 +126,9 @@ export class HaConfigSection extends LitElement { `; } } + +declare global { + interface HTMLElementTagNameMap { + "ha-config-section": HaConfigSection; + } +} diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 1c6bf75e40..cd31e69268 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -157,6 +157,9 @@ class HaSceneDashboard extends LitElement { ), action: () => this._activateScene(scene), }, + { + divider: true, + }, { path: mdiContentDuplicate, label: this.hass.localize( diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index d693bf2417..0fb724c635 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -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` -
    +
    * { + 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; diff --git a/src/panels/config/script/blueprint-script-editor.ts b/src/panels/config/script/blueprint-script-editor.ts index fe99110f00..fac3a7873e 100644 --- a/src/panels/config/script/blueprint-script-editor.ts +++ b/src/panels/config/script/blueprint-script-editor.ts @@ -45,13 +45,14 @@ export class HaBlueprintScriptEditor extends LitElement { protected render() { const blueprint = this._blueprint; - return html` - ${this.hass.localize( + return html` + -
    ${this._blueprints ? Object.keys(this._blueprints).length @@ -118,7 +119,7 @@ export class HaBlueprintScriptEditor extends LitElement {

    `}` : ""} - `; + `; } 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; } `, ]; diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index f7803ef429..2eea1afc6f 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -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} > - + ${this.scriptEntityId && !this.narrow + ? html` + + + ${this.hass.localize( + "ui.panel.config.script.editor.show_trace" + )} + + + ` + : ""} + + + ${this.hass.localize("ui.panel.config.script.editor.show_info")} + + + + + ${this.hass.localize("ui.panel.config.script.picker.run_script")} + + + + ${this.scriptEntityId && this.narrow + ? html` + + + ${this.hass.localize( + "ui.panel.config.script.editor.show_trace" + )} + + + + ` + : ""} + ${this._config && !("use_blueprint" in this._config) + ? html` + + ${this.hass.localize( + "ui.panel.config.automation.editor.re_order" + )} + + + ` + : ""} + +
  • + ${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")} ${this.hass.localize("ui.panel.config.script.picker.delete")} ${this._config ? html` - -
    - -
    - ${this.scriptEntityId - ? html` -
    - - - ${this.hass.localize( - "ui.panel.config.script.editor.show_trace" - )} - - - - ${this.hass.localize( - "ui.panel.config.script.picker.run_script" - )} - -
    - ` - : ``} -
    +
    + +
    + +
    +
    +
    ${"use_blueprint" in this._config ? html` @@ -323,36 +373,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { > ` : html` -
    -

    - ${this.hass.localize( - "ui.panel.config.script.editor.sequence" - )} -

    - - - -
    - - + .narrow=${this.narrow} + .isWide=${this.isWide} + .config=${this._config} + @value-changed=${this._configChanged} + > `} ` : ""} @@ -360,28 +387,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) { ` : this._mode === "yaml" ? html` - ${!this.narrow - ? html` - -
    ${this._config?.alias}
    -
    - - ${this.hass.localize( - "ui.panel.config.script.picker.run_script" - )} - -
    -
    - ` - : ``} { - 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) { - 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); + } `, ]; } diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 217a86359e..da705e7ef4 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -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 { diff --git a/src/panels/config/script/ha-script-trace.ts b/src/panels/config/script/ha-script-trace.ts index e726838c8f..e0ba6c6d33 100644 --- a/src/panels/config/script/ha-script-trace.ts +++ b/src/panels/config/script/ha-script-trace.ts @@ -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 {
    `; } - const actionButtons = html` - - - `; - return html` ${devButtons} - - ${this.narrow - ? html` ${title} -
    ${actionButtons}
    ` + + ${!this.narrow && this.scriptEntityId + ? html` + + + ${this.hass.localize( + "ui.panel.config.script.trace.edit_script" + )} + + + ` : ""} + +
    - ${!this.narrow - ? html`
    - ${title} - - - -
    ` - : ""} ${this._traces && this._traces.length > 0 ? html` -
    - - - -
    + + + ` : ""} - ${!this.narrow ? html`
    ${actionButtons}
    ` : ""}
    ${this._traces === undefined @@ -266,7 +256,7 @@ export class HaScriptTrace extends LitElement {
    `} - + `; } @@ -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; + } `, ]; } diff --git a/src/panels/config/script/manual-script-editor.ts b/src/panels/config/script/manual-script-editor.ts new file mode 100644 index 0000000000..413abf862e --- /dev/null +++ b/src/panels/config/script/manual-script-editor.ts @@ -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` + + ${this.hass.localize( + "ui.panel.config.automation.editor.re_order_mode.description" + )} + + ${this.hass.localize( + "ui.panel.config.automation.editor.re_order_mode.exit" + )} + + + ` + : ""} + +
    +

    + ${this.hass.localize("ui.panel.config.script.editor.sequence")} +

    + + + +
    + + + `; + } + + 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; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 5fd677f39b..4ca9e25056 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -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",