Move Zones Edit to General config + add general config page (#12452)

* Move Zones Edit to General config + add general

* Update src/translations/en.json

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* add paper tooltip back for yaml

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Zack Barett 2022-04-26 23:53:29 -05:00 committed by GitHub
parent b03abc249b
commit 7877dd8e6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 343 additions and 316 deletions

View File

@ -0,0 +1,313 @@
import timezones from "google-timezones-json";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { UNIT_C } from "../../../common/const";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { navigate } from "../../../common/navigate";
import { HaProgressButton } from "../../../components/buttons/ha-progress-button";
import { currencies } from "../../../components/currency-datalist";
import "../../../components/ha-formfield";
import "../../../components/ha-radio";
import type { HaRadio } from "../../../components/ha-radio";
import "../../../components/ha-settings-row";
import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core";
import { SYMBOL_TO_ISO } from "../../../data/currency";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
@customElement("ha-config-section-general")
class HaConfigSectionGeneral extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow!: boolean;
@state() private _submitting = false;
@state() private _unitSystem?: ConfigUpdateValues["unit_system"];
@state() private _currency?: string;
@state() private _name?: string;
@state() private _elevation?: number;
@state() private _timeZone?: string;
protected render(): TemplateResult {
const canEdit = ["storage", "default"].includes(
this.hass.config.config_source
);
const disabled = this._submitting || !canEdit;
return html`
<hass-subpage
back-path="/config/system"
.hass=${this.hass}
.narrow=${this.narrow}
.header=${this.hass.localize("ui.panel.config.core.caption")}
>
<div class="content">
<ha-card>
<div class="card-content">
${!canEdit
? html`
<p>
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.edit_requires_storage"
)}
</p>
`
: ""}
<ha-textfield
name="name"
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.location_name"
)}
.disabled=${disabled}
.value=${this._name}
@change=${this._handleChange}
></ha-textfield>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.time_zone"
)}
name="timeZone"
fixedMenuPosition
naturalMenuWidth
.disabled=${disabled}
.value=${this._timeZone}
@closed=${stopPropagation}
@change=${this._handleChange}
>
${Object.keys(timezones).map(
(tz) =>
html`<mwc-list-item value=${tz}
>${timezones[tz]}</mwc-list-item
>`
)}
</ha-select>
<ha-textfield
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.elevation"
)}
name="elevation"
type="number"
.disabled=${disabled}
.value=${this._elevation}
@change=${this._handleChange}
>
<span slot="suffix">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.elevation_meters"
)}
</span>
</ha-textfield>
<div>
<div>
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.unit_system"
)}
</div>
<ha-formfield
.label=${html`
<span style="font-size: 14px">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.metric_example"
)}
</span>
<div style="color: var(--secondary-text-color)">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.unit_system_metric"
)}
</div>
`}
>
<ha-radio
name="unit_system"
value="metric"
.checked=${this._unitSystem === "metric"}
@change=${this._unitSystemChanged}
.disabled=${this._submitting}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${html`
<span style="font-size: 14px">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.imperial_example"
)}
</span>
<div style="color: var(--secondary-text-color)">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.unit_system_imperial"
)}
</div>
`}
>
<ha-radio
name="unit_system"
value="imperial"
.checked=${this._unitSystem === "imperial"}
@change=${this._unitSystemChanged}
.disabled=${this._submitting}
></ha-radio>
</ha-formfield>
</div>
<div>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.currency"
)}
name="currency"
fixedMenuPosition
naturalMenuWidth
.disabled=${disabled}
.value=${this._currency}
@closed=${stopPropagation}
@change=${this._handleChange}
>
${currencies.map(
(currency) =>
html`<mwc-list-item .value=${currency}
>${currency}</mwc-list-item
>`
)}</ha-select
>
<a
href="https://en.wikipedia.org/wiki/ISO_4217#Active_codes"
target="_blank"
rel="noopener noreferrer"
>${this.hass.localize(
"ui.panel.config.core.section.core.core_config.find_currency_value"
)}</a
>
</div>
</div>
<ha-settings-row>
<div slot="heading">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.edit_location"
)}
</div>
<div slot="description" class="secondary">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.edit_location_description"
)}
</div>
<mwc-button @click=${this._editLocation}
>${this.hass.localize("ui.common.edit")}</mwc-button
>
</ha-settings-row>
<div class="card-actions">
<ha-progress-button @click=${this._updateEntry}>
${this.hass!.localize("ui.panel.config.zone.detail.update")}
</ha-progress-button>
</div>
</ha-card>
</div>
</hass-subpage>
`;
}
protected firstUpdated(): void {
this._unitSystem =
this.hass.config.unit_system.temperature === UNIT_C
? "metric"
: "imperial";
this._currency = this.hass.config.currency;
this._elevation = this.hass.config.elevation;
this._timeZone = this.hass.config.time_zone;
this._name = this.hass.config.location_name;
}
private _handleChange(ev) {
const target = ev.currentTarget;
let value = target.value;
if (target.name === "currency" && value) {
if (value in SYMBOL_TO_ISO) {
value = SYMBOL_TO_ISO[value];
}
}
this[`_${target.name}`] = value;
}
private _unitSystemChanged(ev: CustomEvent) {
this._unitSystem = (ev.target as HaRadio).value as "metric" | "imperial";
}
private async _updateEntry(ev) {
const button = ev.target as HaProgressButton;
if (button.progress) {
return;
}
button.progress = true;
try {
await saveCoreConfig(this.hass, {
currency: this._currency,
elevation: Number(this._elevation),
unit_system: this._unitSystem,
time_zone: this._timeZone,
location_name: this._name,
});
button.actionSuccess();
} catch (err: any) {
button.actionError();
alert(`Error saving config: ${err.message}`);
} finally {
button.progress = false;
}
}
private _editLocation() {
navigate("/config/zone");
}
static styles = [
haStyle,
css`
.content {
padding: 28px 20px 0;
max-width: 1040px;
margin: 0 auto;
}
ha-card {
max-width: 500px;
margin: 0 auto;
height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
.card-content {
display: flex;
justify-content: space-between;
flex-direction: column;
padding: 16px 16px 0 16px;
}
.card-actions {
text-align: right;
height: 48px;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 16px;
}
.card-content > * {
display: block;
margin-top: 16px;
}
ha-select {
display: block;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-config-section-general": HaConfigSectionGeneral;
}
}

View File

@ -324,6 +324,13 @@ export const configSections: { [name: string]: PageNavigation[] } = {
iconColor: "#507FfE",
components: ["system_health", "hassio"],
},
{
path: "/config/general",
translationKey: "ui.panel.config.core.caption",
iconPath: mdiCog,
iconColor: "#653249",
core: true,
},
],
about: [
{
@ -462,6 +469,10 @@ class HaPanelConfig extends HassRouterPage {
tag: "ha-config-zone",
load: () => import("./zone/ha-config-zone"),
},
general: {
tag: "ha-config-section-general",
load: () => import("./core/ha-config-section-general"),
},
zha: {
tag: "zha-config-dashboard-router",
load: () =>

View File

@ -1,284 +0,0 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list";
import "@material/mwc-list/mwc-list-item";
import timezones from "google-timezones-json";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { UNIT_C } from "../../../common/const";
import { fireEvent } from "../../../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { currencies } from "../../../components/currency-datalist";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-formfield";
import "../../../components/ha-radio";
import type { HaRadio } from "../../../components/ha-radio";
import "../../../components/ha-select";
import "../../../components/ha-textfield";
import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core";
import { SYMBOL_TO_ISO } from "../../../data/currency";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
@customElement("dialog-core-zone-detail")
class DialogZoneDetail extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _submitting = false;
@state() private _open = false;
@state() private _unitSystem?: ConfigUpdateValues["unit_system"];
@state() private _currency?: string;
@state() private _name?: string;
@state() private _elevation?: number;
@state() private _timeZone?: string;
public showDialog(): void {
this._submitting = false;
this._unitSystem =
this.hass.config.unit_system.temperature === UNIT_C
? "metric"
: "imperial";
this._currency = this.hass.config.currency;
this._elevation = this.hass.config.elevation;
this._timeZone = this.hass.config.time_zone;
this._name = this.hass.config.location_name;
this._open = true;
}
public closeDialog(): void {
this._open = false;
this._currency = undefined;
this._elevation = undefined;
this._timeZone = undefined;
this._unitSystem = undefined;
this._name = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
const canEdit = ["storage", "default"].includes(
this.hass.config.config_source
);
const disabled = this._submitting || !canEdit;
if (!this._open) {
return html``;
}
return html`
<ha-dialog
open
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.zone.core_location_dialog")
)}
>
${!canEdit
? html`
<p>
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.edit_requires_storage"
)}
</p>
`
: ""}
<ha-textfield
name="name"
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.location_name"
)}
.disabled=${disabled}
.value=${this._name}
@change=${this._handleChange}
></ha-textfield>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.time_zone"
)}
name="timeZone"
fixedMenuPosition
naturalMenuWidth
.disabled=${disabled}
.value=${this._timeZone}
@closed=${stopPropagation}
@change=${this._handleChange}
>
${Object.keys(timezones).map(
(tz) =>
html`<mwc-list-item value=${tz}>${timezones[tz]}</mwc-list-item>`
)}
</ha-select>
<ha-textfield
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.elevation"
)}
name="elevation"
type="number"
.disabled=${disabled}
.value=${this._elevation}
@change=${this._handleChange}
>
<span slot="suffix">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.elevation_meters"
)}
</span>
</ha-textfield>
<div>
<div>
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.unit_system"
)}
</div>
<ha-formfield
.label=${html`${this.hass.localize(
"ui.panel.config.core.section.core.core_config.unit_system_metric"
)}
<div class="secondary">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.metric_example"
)}
</div>`}
>
<ha-radio
name="unit_system"
value="metric"
.checked=${this._unitSystem === "metric"}
@change=${this._unitSystemChanged}
.disabled=${this._submitting}
></ha-radio>
</ha-formfield>
<ha-formfield
.label=${html`${this.hass.localize(
"ui.panel.config.core.section.core.core_config.unit_system_imperial"
)}
<div class="secondary">
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.imperial_example"
)}
</div>`}
>
<ha-radio
name="unit_system"
value="imperial"
.checked=${this._unitSystem === "imperial"}
@change=${this._unitSystemChanged}
.disabled=${this._submitting}
></ha-radio>
</ha-formfield>
</div>
<div>
<ha-select
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.currency"
)}
name="currency"
fixedMenuPosition
naturalMenuWidth
.disabled=${disabled}
.value=${this._currency}
@closed=${stopPropagation}
@change=${this._handleChange}
>
${currencies.map(
(currency) =>
html`<mwc-list-item .value=${currency}
>${currency}</mwc-list-item
>`
)}</ha-select
>
<a
href="https://en.wikipedia.org/wiki/ISO_4217#Active_codes"
target="_blank"
rel="noopener noreferrer"
>${this.hass.localize(
"ui.panel.config.core.section.core.core_config.find_currency_value"
)}</a
>
</div>
<mwc-button slot="primaryAction" @click=${this._updateEntry}>
${this.hass!.localize("ui.panel.config.zone.detail.update")}
</mwc-button>
</ha-dialog>
`;
}
private _handleChange(ev) {
const target = ev.currentTarget;
let value = target.value;
if (target.name === "currency" && value) {
if (value in SYMBOL_TO_ISO) {
value = SYMBOL_TO_ISO[value];
}
}
this[`_${target.name}`] = value;
}
private _unitSystemChanged(ev: CustomEvent) {
this._unitSystem = (ev.target as HaRadio).value as "metric" | "imperial";
}
private async _updateEntry() {
this._submitting = true;
try {
await saveCoreConfig(this.hass, {
currency: this._currency,
elevation: Number(this._elevation),
unit_system: this._unitSystem,
time_zone: this._timeZone,
location_name: this._name,
});
} catch (err: any) {
alert(`Error saving config: ${err.message}`);
} finally {
this._submitting = false;
}
this.closeDialog();
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-dialog {
--mdc-dialog-min-width: 600px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-dialog {
--mdc-dialog-min-width: calc(
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
);
}
}
.card-actions {
text-align: right;
}
ha-dialog > * {
display: block;
margin-top: 16px;
}
ha-select {
display: block;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-core-zone-detail": DialogZoneDetail;
}
}

View File

@ -46,7 +46,6 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant, Route } from "../../../types";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
import { showCoreZoneDetailDialog } from "./show-dialog-core-zone-detail";
import { showZoneDetailDialog } from "./show-dialog-zone-detail";
@customElement("ha-config-zone")
@ -188,30 +187,26 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
<div style="display:inline-block">
<ha-icon-button
.entityId=${stateObject.entity_id}
@click=${this._openCoreConfig}
.noEdit=${stateObject.entity_id !== "zone.home" ||
!this._canEditCore}
.path=${stateObject.entity_id === "zone.home" &&
this._canEditCore
? mdiPencil
: mdiPencilOff}
.label=${hass.localize(
"ui.panel.config.zone.edit_zone"
)}
.label=${stateObject.entity_id === "zone.home"
? hass.localize("ui.panel.config.zone.edit_home")
: hass.localize("ui.panel.config.zone.edit_zone")}
@click=${this._openCoreConfig}
></ha-icon-button>
<paper-tooltip animation-delay="0" position="left">
${stateObject.entity_id === "zone.home"
? hass.localize(
`ui.panel.config.zone.${
this.narrow
? "edit_home_zone_narrow"
: "edit_home_zone"
}`
)
: hass.localize(
"ui.panel.config.zone.configured_in_yaml"
)}
</paper-tooltip>
${stateObject.entity_id !== "zone.home"
? html`
<paper-tooltip animation-delay="0" position="left">
${hass.localize(
"ui.panel.config.zone.configured_in_yaml"
)}
</paper-tooltip>
`
: ""}
</div>
</paper-icon-item>
`
@ -397,7 +392,7 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
});
return;
}
showCoreZoneDetailDialog(this);
navigate("/config/general");
}
private async _createEntry(values: ZoneMutableParams) {

View File

@ -1,12 +0,0 @@
import { fireEvent } from "../../../common/dom/fire_event";
export const loadCoreZoneDetailDialog = () =>
import("./dialog-core-zone-detail");
export const showCoreZoneDetailDialog = (element: HTMLElement): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-core-zone-detail",
dialogImport: loadCoreZoneDetailDialog,
dialogParams: {},
});
};

View File

@ -314,6 +314,7 @@
"undo": "Undo",
"move": "Move",
"save": "Save",
"edit": "Edit",
"submit": "Submit",
"rename": "Rename",
"yes": "Yes",
@ -1494,7 +1495,9 @@
"metric_example": "Celsius, kilograms",
"find_currency_value": "Find your value",
"save_button": "Save",
"currency": "Currency"
"currency": "Currency",
"edit_location": "Edit location",
"edit_location_description": "Location can be changed in zone settings"
}
}
}
@ -2687,6 +2690,7 @@
"create_zone": "Add Zone",
"add_zone": "Add Zone",
"edit_zone": "Edit Zone",
"edit_home": "Edit Home",
"confirm_delete": "Are you sure you want to delete this zone?",
"can_not_edit": "Unable to edit zone",
"configured_in_yaml": "Zones configured via configuration.yaml cannot be edited via the UI.",