From 9706c56c5c8c6425f0b013a4692f19d8deb49927 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Sun, 24 Apr 2022 17:26:01 -0500 Subject: [PATCH] Configuration Menu Updates 3 (#12377) --- src/components/currency-datalist.ts | 320 ++++++++-------- src/components/ha-metric.ts | 77 ++++ src/components/ha-navigation-list.ts | 4 +- .../config/blueprint/ha-blueprint-overview.ts | 2 +- src/panels/config/core/ha-config-core-form.ts | 342 ------------------ src/panels/config/core/ha-config-core.js | 57 --- src/panels/config/core/ha-config-name-form.ts | 97 ----- src/panels/config/core/ha-config-network.ts | 2 +- .../config/core/ha-config-section-storage.ts | 94 ++++- .../config/core/ha-config-section-updates.ts | 137 +++++++ .../core/ha-config-system-navigation.ts | 6 +- .../config/dashboard/ha-config-navigation.ts | 12 +- .../config/dashboard/ha-config-updates.ts | 10 +- src/panels/config/ha-panel-config.ts | 41 +-- .../ha-config-lovelace-dashboards.ts | 34 +- .../config/lovelace/ha-config-lovelace.ts | 7 - src/panels/config/users/ha-config-users.ts | 2 +- .../config/zone/dialog-core-zone-detail.ts | 278 ++++++++++++++ src/panels/config/zone/ha-config-zone.ts | 30 +- .../zone/show-dialog-core-zone-detail.ts | 12 + src/translations/en.json | 55 +-- 21 files changed, 860 insertions(+), 759 deletions(-) create mode 100644 src/components/ha-metric.ts delete mode 100644 src/panels/config/core/ha-config-core-form.ts delete mode 100644 src/panels/config/core/ha-config-core.js delete mode 100644 src/panels/config/core/ha-config-name-form.ts create mode 100644 src/panels/config/core/ha-config-section-updates.ts create mode 100644 src/panels/config/zone/dialog-core-zone-detail.ts create mode 100644 src/panels/config/zone/show-dialog-core-zone-detail.ts diff --git a/src/components/currency-datalist.ts b/src/components/currency-datalist.ts index a5ec24d762..69a69e7222 100644 --- a/src/components/currency-datalist.ts +++ b/src/components/currency-datalist.ts @@ -1,165 +1,167 @@ +export const currencies = [ + "AED", + "AFN", + "ALL", + "AMD", + "ANG", + "AOA", + "ARS", + "AUD", + "AWG", + "AZN", + "BAM", + "BBD", + "BDT", + "BGN", + "BHD", + "BIF", + "BMD", + "BND", + "BOB", + "BRL", + "BSD", + "BTN", + "BWP", + "BYN", + "BYR", + "BZD", + "CAD", + "CDF", + "CHF", + "CLP", + "CNY", + "COP", + "CRC", + "CUP", + "CVE", + "CZK", + "DJF", + "DKK", + "DOP", + "DZD", + "EGP", + "ERN", + "ETB", + "EUR", + "FJD", + "FKP", + "GBP", + "GEL", + "GHS", + "GIP", + "GMD", + "GNF", + "GTQ", + "GYD", + "HKD", + "HNL", + "HRK", + "HTG", + "HUF", + "IDR", + "ILS", + "INR", + "IQD", + "IRR", + "ISK", + "JMD", + "JOD", + "JPY", + "KES", + "KGS", + "KHR", + "KMF", + "KPW", + "KRW", + "KWD", + "KYD", + "KZT", + "LAK", + "LBP", + "LKR", + "LRD", + "LSL", + "LTL", + "LYD", + "MAD", + "MDL", + "MGA", + "MKD", + "MMK", + "MNT", + "MOP", + "MRO", + "MUR", + "MVR", + "MWK", + "MXN", + "MYR", + "MZN", + "NAD", + "NGN", + "NIO", + "NOK", + "NPR", + "NZD", + "OMR", + "PAB", + "PEN", + "PGK", + "PHP", + "PKR", + "PLN", + "PYG", + "QAR", + "RON", + "RSD", + "RUB", + "RWF", + "SAR", + "SBD", + "SCR", + "SDG", + "SEK", + "SGD", + "SHP", + "SLL", + "SOS", + "SRD", + "SSP", + "STD", + "SYP", + "SZL", + "THB", + "TJS", + "TMT", + "TND", + "TOP", + "TRY", + "TTD", + "TWD", + "TZS", + "UAH", + "UGX", + "USD", + "UYU", + "UZS", + "VEF", + "VND", + "VUV", + "WST", + "XAF", + "XCD", + "XOF", + "XPF", + "YER", + "ZAR", + "ZMK", + "ZWL", +]; + export const createCurrencyListEl = () => { const list = document.createElement("datalist"); list.id = "currencies"; - for (const currency of [ - "AED", - "AFN", - "ALL", - "AMD", - "ANG", - "AOA", - "ARS", - "AUD", - "AWG", - "AZN", - "BAM", - "BBD", - "BDT", - "BGN", - "BHD", - "BIF", - "BMD", - "BND", - "BOB", - "BRL", - "BSD", - "BTN", - "BWP", - "BYN", - "BYR", - "BZD", - "CAD", - "CDF", - "CHF", - "CLP", - "CNY", - "COP", - "CRC", - "CUP", - "CVE", - "CZK", - "DJF", - "DKK", - "DOP", - "DZD", - "EGP", - "ERN", - "ETB", - "EUR", - "FJD", - "FKP", - "GBP", - "GEL", - "GHS", - "GIP", - "GMD", - "GNF", - "GTQ", - "GYD", - "HKD", - "HNL", - "HRK", - "HTG", - "HUF", - "IDR", - "ILS", - "INR", - "IQD", - "IRR", - "ISK", - "JMD", - "JOD", - "JPY", - "KES", - "KGS", - "KHR", - "KMF", - "KPW", - "KRW", - "KWD", - "KYD", - "KZT", - "LAK", - "LBP", - "LKR", - "LRD", - "LSL", - "LTL", - "LYD", - "MAD", - "MDL", - "MGA", - "MKD", - "MMK", - "MNT", - "MOP", - "MRO", - "MUR", - "MVR", - "MWK", - "MXN", - "MYR", - "MZN", - "NAD", - "NGN", - "NIO", - "NOK", - "NPR", - "NZD", - "OMR", - "PAB", - "PEN", - "PGK", - "PHP", - "PKR", - "PLN", - "PYG", - "QAR", - "RON", - "RSD", - "RUB", - "RWF", - "SAR", - "SBD", - "SCR", - "SDG", - "SEK", - "SGD", - "SHP", - "SLL", - "SOS", - "SRD", - "SSP", - "STD", - "SYP", - "SZL", - "THB", - "TJS", - "TMT", - "TND", - "TOP", - "TRY", - "TTD", - "TWD", - "TZS", - "UAH", - "UGX", - "USD", - "UYU", - "UZS", - "VEF", - "VND", - "VUV", - "WST", - "XAF", - "XCD", - "XOF", - "XPF", - "YER", - "ZAR", - "ZMK", - "ZWL", - ]) { + for (const currency of currencies) { const option = document.createElement("option"); option.value = currency; option.innerHTML = currency; diff --git a/src/components/ha-metric.ts b/src/components/ha-metric.ts new file mode 100644 index 0000000000..2c576b9fde --- /dev/null +++ b/src/components/ha-metric.ts @@ -0,0 +1,77 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { roundWithOneDecimal } from "../util/calculate"; +import "./ha-bar"; +import "./ha-settings-row"; + +@customElement("ha-metric") +class HaMetric extends LitElement { + @property({ type: Number }) public value!: number; + + @property({ type: String }) public description!: string; + + @property({ type: String }) public tooltip?: string; + + protected render(): TemplateResult { + const roundedValue = roundWithOneDecimal(this.value); + return html` + ${this.description} +
+ ${roundedValue} % + 50, + "target-critical": roundedValue > 85, + })} + .value=${this.value} + > +
+
`; + } + + static get styles(): CSSResultGroup { + return css` + ha-settings-row { + padding: 0; + height: 54px; + width: 100%; + } + ha-settings-row > div[slot="description"] { + white-space: normal; + color: var(--secondary-text-color); + display: flex; + justify-content: space-between; + } + ha-bar { + --ha-bar-primary-color: var( + --metric-bar-ok-color, + var(--success-color) + ); + } + .target-warning { + --ha-bar-primary-color: var( + --metric-bar-warning-color, + var(--warning-color) + ); + } + .target-critical { + --ha-bar-primary-color: var( + --metric-bar-critical-color, + var(--error-color) + ); + } + .value { + width: 48px; + padding-right: 4px; + flex-shrink: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-metric": HaMetric; + } +} diff --git a/src/components/ha-navigation-list.ts b/src/components/ha-navigation-list.ts index 734e4b8cf8..4336bb2b1e 100644 --- a/src/components/ha-navigation-list.ts +++ b/src/components/ha-navigation-list.ts @@ -4,9 +4,9 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import type { PageNavigation } from "../layouts/hass-tabs-subpage"; import type { HomeAssistant } from "../types"; +import "./ha-clickable-list-item"; import "./ha-icon-next"; import "./ha-svg-icon"; -import "./ha-clickable-list-item"; @customElement("ha-navigation-list") class HaNavigationList extends LitElement { @@ -78,7 +78,7 @@ class HaNavigationList extends LitElement { .icon-background ha-svg-icon { color: #fff; } - mwc-list-item { + ha-clickable-list-item { cursor: pointer; font-size: var(--navigation-list-item-title-font-size); } diff --git a/src/panels/config/blueprint/ha-blueprint-overview.ts b/src/panels/config/blueprint/ha-blueprint-overview.ts index 35fd53a523..0e65094222 100644 --- a/src/panels/config/blueprint/ha-blueprint-overview.ts +++ b/src/panels/config/blueprint/ha-blueprint-overview.ts @@ -224,7 +224,7 @@ class HaBlueprintOverview extends LitElement { .narrow=${this.narrow} back-path="/config" .route=${this.route} - .tabs=${configSections.blueprints} + .tabs=${configSections.automations} .columns=${this._columns(this.narrow, this.hass.language)} .data=${this._processedBlueprints(this.blueprints)} id="entity_id" diff --git a/src/panels/config/core/ha-config-core-form.ts b/src/panels/config/core/ha-config-core-form.ts deleted file mode 100644 index 495b963121..0000000000 --- a/src/panels/config/core/ha-config-core-form.ts +++ /dev/null @@ -1,342 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import "@polymer/paper-input/paper-input"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import memoizeOne from "memoize-one"; -import { UNIT_C } from "../../../common/const"; -import { createCurrencyListEl } from "../../../components/currency-datalist"; -import "../../../components/ha-card"; -import "../../../components/map/ha-locations-editor"; -import type { MarkerLocation } from "../../../components/map/ha-locations-editor"; -import { createTimezoneListEl } from "../../../components/timezone-datalist"; -import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core"; -import { SYMBOL_TO_ISO } from "../../../data/currency"; -import type { PolymerChangedEvent } from "../../../polymer-types"; -import type { HomeAssistant } from "../../../types"; -import "../../../components/ha-formfield"; -import "../../../components/ha-radio"; -import type { HaRadio } from "../../../components/ha-radio"; - -@customElement("ha-config-core-form") -class ConfigCoreForm extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @state() private _working = false; - - @state() private _location?: [number, number]; - - @state() private _currency?: string; - - @state() private _elevation?: string; - - @state() private _unitSystem?: ConfigUpdateValues["unit_system"]; - - @state() private _timeZone?: string; - - protected render(): TemplateResult { - const canEdit = ["storage", "default"].includes( - this.hass.config.config_source - ); - const disabled = this._working || !canEdit; - - return html` - -
- ${!canEdit - ? html` -

- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.edit_requires_storage" - )} -

- ` - : ""} - -
- -
- -
-
- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.time_zone" - )} -
- - -
-
-
- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.elevation" - )} -
- - - - ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.elevation_meters" - )} - - -
- -
-
- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.unit_system" - )} -
-
- - ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.metric_example" - )} -
`} - > - - - - ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.imperial_example" - )} -
`} - > - - -
- -
-
- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.currency" - )}
- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.find_currency_value" - )} -
- - -
- -
- - ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.save_button" - )} - -
-
- `; - } - - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - - const tzInput = this.shadowRoot!.querySelector( - "[name=timeZone]" - ) as PaperInputElement; - tzInput.inputElement.appendChild(createTimezoneListEl()); - - const cInput = this.shadowRoot!.querySelector( - "[name=currency]" - ) as PaperInputElement; - cInput.inputElement.appendChild(createCurrencyListEl()); - } - - private _markerLocation = memoizeOne( - ( - lat: number, - lng: number, - location?: [number, number] - ): MarkerLocation[] => [ - { - id: "location", - latitude: location ? location[0] : lat, - longitude: location ? location[1] : lng, - location_editable: true, - }, - ] - ); - - private get _currencyValue() { - return this._currency !== undefined - ? this._currency - : this.hass.config.currency; - } - - private get _elevationValue() { - return this._elevation !== undefined - ? this._elevation - : this.hass.config.elevation; - } - - private get _timeZoneValue() { - return this._timeZone !== undefined - ? this._timeZone - : this.hass.config.time_zone; - } - - private get _unitSystemValue() { - return this._unitSystem !== undefined - ? this._unitSystem - : this.hass.config.unit_system.temperature === UNIT_C - ? "metric" - : "imperial"; - } - - private _handleChange(ev: PolymerChangedEvent) { - const target = ev.currentTarget as PaperInputElement; - 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 _locationChanged(ev) { - this._location = ev.detail.location; - } - - private _unitSystemChanged(ev: CustomEvent) { - this._unitSystem = (ev.target as HaRadio).value as "metric" | "imperial"; - } - - private async _save() { - this._working = true; - try { - const location = this._location || [ - this.hass.config.latitude, - this.hass.config.longitude, - ]; - await saveCoreConfig(this.hass, { - latitude: location[0], - longitude: location[1], - currency: this._currencyValue, - elevation: Number(this._elevationValue), - unit_system: this._unitSystemValue, - time_zone: this._timeZoneValue, - }); - } catch (err: any) { - alert(`Error saving config: ${err.message}`); - } finally { - this._working = false; - } - } - - static get styles(): CSSResultGroup { - return css` - .row { - display: flex; - flex-direction: row; - margin: 0 -8px; - align-items: center; - } - - .secondary { - color: var(--secondary-text-color); - } - - .flex { - flex: 1; - } - - .row > * { - margin: 0 8px; - } - - .radio-group { - display: flex; - flex-direction: column; - flex: 1; - } - - .card-actions { - text-align: right; - } - - a { - color: var(--primary-color); - } - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ha-config-core-form": ConfigCoreForm; - } -} diff --git a/src/panels/config/core/ha-config-core.js b/src/panels/config/core/ha-config-core.js deleted file mode 100644 index b76dd9b678..0000000000 --- a/src/panels/config/core/ha-config-core.js +++ /dev/null @@ -1,57 +0,0 @@ -import "@polymer/app-layout/app-header/app-header"; -import "@polymer/app-layout/app-toolbar/app-toolbar"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../../../layouts/hass-subpage"; -import LocalizeMixin from "../../../mixins/localize-mixin"; -import "../../../styles/polymer-ha-style"; -import "./ha-config-core-form"; -import "./ha-config-name-form"; - -/* - * @appliesMixin LocalizeMixin - */ -class HaConfigCore extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - -
- - -
-
- `; - } - - static get properties() { - return { - hass: Object, - isWide: Boolean, - narrow: Boolean, - showAdvanced: Boolean, - route: Object, - }; - } -} - -customElements.define("ha-config-core", HaConfigCore); diff --git a/src/panels/config/core/ha-config-name-form.ts b/src/panels/config/core/ha-config-name-form.ts deleted file mode 100644 index c380c37901..0000000000 --- a/src/panels/config/core/ha-config-name-form.ts +++ /dev/null @@ -1,97 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { css, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import "../../../components/ha-card"; -import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core"; -import type { HomeAssistant } from "../../../types"; -import "../../../components/ha-textfield"; -import type { HaTextField } from "../../../components/ha-textfield"; - -@customElement("ha-config-name-form") -class ConfigNameForm extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @state() private _working = false; - - @state() private _name!: ConfigUpdateValues["location_name"]; - - protected render(): TemplateResult { - const canEdit = ["storage", "default"].includes( - this.hass.config.config_source - ); - const disabled = this._working || !canEdit; - - return html` - -
- ${!canEdit - ? html` -

- ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.edit_requires_storage" - )} -

- ` - : ""} - -
-
- - ${this.hass.localize( - "ui.panel.config.core.section.core.core_config.save_button" - )} - -
-
- `; - } - - private get _nameValue() { - return this._name !== undefined - ? this._name - : this.hass.config.location_name; - } - - private _handleChange(ev) { - const target = ev.currentTarget as HaTextField; - this._name = target.value; - } - - private async _save() { - this._working = true; - try { - await saveCoreConfig(this.hass, { - location_name: this._nameValue, - }); - } catch (err: any) { - alert("FAIL"); - } finally { - this._working = false; - } - } - - static get styles() { - return css` - .card-actions { - text-align: right; - } - ha-textfield { - display: block; - } - `; - } -} - -declare global { - interface HTMLElementTagNameMap { - "ha-config-name-form": ConfigNameForm; - } -} diff --git a/src/panels/config/core/ha-config-network.ts b/src/panels/config/core/ha-config-network.ts index 42cecfe606..5ac9ff5cf6 100644 --- a/src/panels/config/core/ha-config-network.ts +++ b/src/panels/config/core/ha-config-network.ts @@ -40,7 +40,7 @@ class ConfigNetwork extends LitElement { } return html` - +
${this._error ? html` diff --git a/src/panels/config/core/ha-config-section-storage.ts b/src/panels/config/core/ha-config-section-storage.ts index 6e3a96f140..b40eaa4d44 100644 --- a/src/panels/config/core/ha-config-section-storage.ts +++ b/src/panels/config/core/ha-config-section-storage.ts @@ -1,7 +1,17 @@ -import { css, html, LitElement, TemplateResult } from "lit"; -import { customElement, property } from "lit/decorators"; +import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +import "../../../components/ha-alert"; +import "../../../components/ha-bar"; +import "../../../components/ha-metric"; +import { fetchHassioHostInfo, HassioHostInfo } from "../../../data/hassio/host"; import "../../../layouts/hass-subpage"; import type { HomeAssistant, Route } from "../../../types"; +import { + getValueInPercentage, + roundWithOneDecimal, +} from "../../../util/calculate"; import "./ha-config-analytics"; @customElement("ha-config-section-storage") @@ -12,6 +22,17 @@ class HaConfigSectionStorage extends LitElement { @property({ type: Boolean }) public narrow!: boolean; + @state() private _error?: { code: string; message: string }; + + @state() private _storageData?: HassioHostInfo; + + protected firstUpdated(changedProps: PropertyValues) { + super.firstUpdated(changedProps); + if (isComponentLoaded(this.hass, "hassio")) { + this._load(); + } + } + protected render(): TemplateResult { return html` -
+
+ ${this._error + ? html` + ${this._error.message || this._error.code} + ` + : ""} + ${this._storageData + ? html` + + + ${this._storageData.disk_life_time !== "" && + this._storageData.disk_life_time >= 10 + ? html` + + ` + : ""} + + ` + : ""} +
`; } + private async _load() { + this._error = undefined; + try { + if (isComponentLoaded(this.hass, "hassio")) { + this._storageData = await fetchHassioHostInfo(this.hass); + } + } catch (err: any) { + this._error = err.message || err; + } + } + + private _getUsedSpace = memoizeOne((used: number, total: number) => + roundWithOneDecimal(getValueInPercentage(used, 0, total)) + ); + static styles = css` .content { padding: 28px 20px 0; max-width: 1040px; margin: 0 auto; } + ha-card { + padding: 16px; + max-width: 500px; + margin: 0 auto; + height: 100%; + justify-content: space-between; + flex-direction: column; + display: flex; + } + .emmc { + --metric-bar-ok-color: #000; + } `; } diff --git a/src/panels/config/core/ha-config-section-updates.ts b/src/panels/config/core/ha-config-section-updates.ts new file mode 100644 index 0000000000..0853566da3 --- /dev/null +++ b/src/panels/config/core/ha-config-section-updates.ts @@ -0,0 +1,137 @@ +import { HassEntities } from "home-assistant-js-websocket"; +import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { computeStateDomain } from "../../../common/entity/compute_state_domain"; +import { caseInsensitiveStringCompare } from "../../../common/string/compare"; +import "../../../components/ha-alert"; +import "../../../components/ha-bar"; +import "../../../components/ha-metric"; +import { updateCanInstall, UpdateEntity } from "../../../data/update"; +import "../../../layouts/hass-subpage"; +import type { HomeAssistant } from "../../../types"; +import { showToast } from "../../../util/toast"; +import "../dashboard/ha-config-updates"; +import "./ha-config-analytics"; + +@customElement("ha-config-section-updates") +class HaConfigSectionUpdates extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow!: boolean; + + private _notifyUpdates = false; + + protected render(): TemplateResult { + const canInstallUpdates = this._filterUpdateEntitiesWithInstall( + this.hass.states + ); + + return html` + +
+ + ${canInstallUpdates.length + ? html` + + ` + : html` + ${this.hass.localize("ui.panel.config.updates.no_updates")} + `} + +
+
+ `; + } + + protected override updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + if (!changedProps.has("hass") || !this._notifyUpdates) { + return; + } + this._notifyUpdates = false; + if (this._filterUpdateEntitiesWithInstall(this.hass.states).length) { + showToast(this, { + message: this.hass.localize( + "ui.panel.config.updates.updates_refreshed" + ), + }); + } else { + showToast(this, { + message: this.hass.localize("ui.panel.config.updates.no_new_updates"), + }); + } + } + + private _filterUpdateEntities = memoizeOne((entities: HassEntities) => + ( + Object.values(entities).filter( + (entity) => computeStateDomain(entity) === "update" + ) as UpdateEntity[] + ).sort((a, b) => { + if (a.attributes.title === "Home Assistant Core") { + return -3; + } + if (b.attributes.title === "Home Assistant Core") { + return 3; + } + if (a.attributes.title === "Home Assistant Operating System") { + return -2; + } + if (b.attributes.title === "Home Assistant Operating System") { + return 2; + } + if (a.attributes.title === "Home Assistant Supervisor") { + return -1; + } + if (b.attributes.title === "Home Assistant Supervisor") { + return 1; + } + return caseInsensitiveStringCompare( + a.attributes.title || a.attributes.friendly_name || "", + b.attributes.title || b.attributes.friendly_name || "" + ); + }) + ); + + private _filterUpdateEntitiesWithInstall = memoizeOne( + (entities: HassEntities) => + this._filterUpdateEntities(entities).filter((entity) => + updateCanInstall(entity) + ) + ); + + static styles = css` + .content { + padding: 28px 20px 0; + max-width: 1040px; + margin: 0 auto; + } + ha-card { + padding: 16px; + max-width: 500px; + margin: 0 auto; + height: 100%; + justify-content: space-between; + flex-direction: column; + display: flex; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-section-updates": HaConfigSectionUpdates; + } +} diff --git a/src/panels/config/core/ha-config-system-navigation.ts b/src/panels/config/core/ha-config-system-navigation.ts index 0db85573b0..8792ef009a 100644 --- a/src/panels/config/core/ha-config-system-navigation.ts +++ b/src/panels/config/core/ha-config-system-navigation.ts @@ -33,7 +33,7 @@ class HaConfigSystemNavigation extends LitElement { return html` ${this.narrow ? html`
- ${this.hass.localize( - "ui.panel.config.dashboard.system.title" - )} + ${this.hass.localize("ui.panel.config.dashboard.system.main")}
` : ""} ` )} - ${!this._showAll && this.updateEntities.length >= 4 + ${!this.showAll && this.updateEntities.length >= 4 ? html`
`} + > + + + +
+ + ${currencies.map( + (currency) => + html`${currency}` + )} + ${this.hass.localize( + "ui.panel.config.core.section.core.core_config.find_currency_value" + )} +
+ + ${this.hass!.localize("ui.panel.config.zone.detail.update")} + + + `; + } + + 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; + } +} diff --git a/src/panels/config/zone/ha-config-zone.ts b/src/panels/config/zone/ha-config-zone.ts index 23db9af09f..e5802d069e 100644 --- a/src/panels/config/zone/ha-config-zone.ts +++ b/src/panels/config/zone/ha-config-zone.ts @@ -13,7 +13,6 @@ import { TemplateResult, } from "lit"; import { customElement, property, query, state } from "lit/decorators"; -import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { navigate } from "../../../common/navigate"; @@ -44,6 +43,7 @@ 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") @@ -186,15 +186,9 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { + import("./dialog-core-zone-detail"); + +export const showCoreZoneDetailDialog = (element: HTMLElement): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-core-zone-detail", + dialogImport: loadCoreZoneDetailDialog, + dialogParams: {}, + }); +}; diff --git a/src/translations/en.json b/src/translations/en.json index a71198f227..da380f5dfc 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1066,52 +1066,52 @@ "header": "Configure Home Assistant", "dashboard": { "devices": { - "title": "Devices & Services", - "description": "Integrations, devices, entities and helpers" + "main": "Devices & Services", + "secondary": "Integrations, devices, entities and helpers" }, "automations": { - "title": "Automations & Scenes", - "description": "Manage automations, scenes, scripts and blueprints" + "main": "Automations & Scenes", + "secondary": "Manage automations, scenes, scripts and blueprints" }, "backup": { - "title": "Backup", - "description": "Generate backups of your Home Assistant configuration" + "main": "Backup", + "secondary": "Generate backups of your Home Assistant configuration" }, "supervisor": { - "title": "Add-ons", - "description": "Extend the function around Home Assistant" + "main": "Add-ons", + "secondary": "Extend the function around Home Assistant" }, "dashboards": { - "title": "Dashboards", - "description": "Create customized sets of cards to control your home" + "main": "Dashboards", + "secondary": "Create customized sets of cards to control your home" }, "energy": { - "title": "Energy", - "description": "Monitor your energy production and consumption" + "main": "Energy", + "secondary": "Monitor your energy production and consumption" }, "tags": { - "title": "Tags", - "description": "Trigger automations when an NFC tag, QR code, etc. is scanned" + "main": "Tags", + "secondary": "Trigger automations when an NFC tag, QR code, etc. is scanned" }, "people": { - "title": "People", - "description": "Manage the people that Home Assistant tracks" + "main": "People", + "secondary": "Manage the people that Home Assistant tracks" }, "areas": { - "title": "Areas & Zones", - "description": "Manage areas & zones that Home Assistant tracks" + "main": "Areas & Zones", + "secondary": "Manage areas and zones that Home Assistant tracks" }, "companion": { - "title": "Companion App", - "description": "Location and notifications" + "main": "Companion App", + "secondary": "Location and notifications" }, "system": { - "title": "System", - "description": "Create backups, check logs or reboot your system" + "main": "System", + "secondary": "Create backups, check logs or reboot your system" }, "about": { - "title": "About", - "description": "Version, system health and links to documentation" + "main": "About", + "secondary": "Version, system health and links to documentation" } }, "common": { @@ -1122,6 +1122,7 @@ }, "updates": { "caption": "Updates", + "no_updates": "No updates available", "no_update_entities": { "title": "Unable to check for updates", "description": "You do not have any integrations that provide updates." @@ -1778,7 +1779,7 @@ "geo_location": { "label": "Geolocation", "source": "Source", - "zone": "Location", + "zone": "Zone", "event": "Event", "enter": "Enter", "leave": "Leave" @@ -3109,7 +3110,9 @@ "caption": "Network" }, "storage": { - "caption": "Storage" + "caption": "Storage", + "used_space": "Used Space", + "emmc_lifetime_used": "eMMC Lifetime Used" } }, "lovelace": {