mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 01:06:35 +00:00
Configuration Menu Updates 3 (#12377)
This commit is contained in:
parent
3677c5be2c
commit
9706c56c5c
@ -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;
|
||||
|
77
src/components/ha-metric.ts
Normal file
77
src/components/ha-metric.ts
Normal file
@ -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`<ha-settings-row>
|
||||
<span slot="heading"> ${this.description} </span>
|
||||
<div slot="description" .title=${this.tooltip ?? ""}>
|
||||
<span class="value"> ${roundedValue} % </span>
|
||||
<ha-bar
|
||||
class=${classMap({
|
||||
"target-warning": roundedValue > 50,
|
||||
"target-critical": roundedValue > 85,
|
||||
})}
|
||||
.value=${this.value}
|
||||
></ha-bar>
|
||||
</div>
|
||||
</ha-settings-row>`;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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`
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.form.heading"
|
||||
)}
|
||||
>
|
||||
<div class="card-content">
|
||||
${!canEdit
|
||||
? html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.edit_requires_storage"
|
||||
)}
|
||||
</p>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<div class="row">
|
||||
<ha-locations-editor
|
||||
class="flex"
|
||||
.hass=${this.hass}
|
||||
.locations=${this._markerLocation(
|
||||
this.hass.config.latitude,
|
||||
this.hass.config.longitude,
|
||||
this._location
|
||||
)}
|
||||
@location-updated=${this._locationChanged}
|
||||
></ha-locations-editor>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="flex">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.time_zone"
|
||||
)}
|
||||
</div>
|
||||
|
||||
<paper-input
|
||||
class="flex"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.time_zone"
|
||||
)}
|
||||
name="timeZone"
|
||||
list="timezones"
|
||||
.disabled=${disabled}
|
||||
.value=${this._timeZoneValue}
|
||||
@value-changed=${this._handleChange}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.elevation"
|
||||
)}
|
||||
</div>
|
||||
|
||||
<paper-input
|
||||
class="flex"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.elevation"
|
||||
)}
|
||||
name="elevation"
|
||||
type="number"
|
||||
.disabled=${disabled}
|
||||
.value=${this._elevationValue}
|
||||
@value-changed=${this._handleChange}
|
||||
>
|
||||
<span slot="suffix">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.elevation_meters"
|
||||
)}
|
||||
</span>
|
||||
</paper-input>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="flex">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.unit_system"
|
||||
)}
|
||||
</div>
|
||||
<div class="radio-group">
|
||||
<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._unitSystemValue === "metric"}
|
||||
@change=${this._unitSystemChanged}
|
||||
.disabled=${this._working}
|
||||
></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._unitSystemValue === "imperial"}
|
||||
@change=${this._unitSystemChanged}
|
||||
.disabled=${this._working}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="flex">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.currency"
|
||||
)}<br />
|
||||
<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>
|
||||
|
||||
<paper-input
|
||||
class="flex"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.currency"
|
||||
)}
|
||||
name="currency"
|
||||
list="currencies"
|
||||
.disabled=${disabled}
|
||||
.value=${this._currencyValue}
|
||||
@value-changed=${this._handleChange}
|
||||
></paper-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._save} .disabled=${disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.save_button"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
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<string>) {
|
||||
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;
|
||||
}
|
||||
}
|
@ -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`
|
||||
<style include="iron-flex ha-style">
|
||||
.content {
|
||||
padding: 28px 20px 0;
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
ha-config-name-form,
|
||||
ha-config-core-form {
|
||||
display: block;
|
||||
margin-top: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<hass-subpage
|
||||
hass="[[hass]]"
|
||||
narrow="[[narrow]]"
|
||||
header="[[localize('ui.panel.config.core.caption')]]"
|
||||
back-path="/config/system"
|
||||
>
|
||||
<div class="content">
|
||||
<ha-config-name-form hass="[[hass]]"></ha-config-name-form>
|
||||
<ha-config-core-form hass="[[hass]]"></ha-config-core-form>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
isWide: Boolean,
|
||||
narrow: Boolean,
|
||||
showAdvanced: Boolean,
|
||||
route: Object,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-config-core", HaConfigCore);
|
@ -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`
|
||||
<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
|
||||
class="flex"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.location_name"
|
||||
)}
|
||||
.disabled=${disabled}
|
||||
.value=${this._nameValue}
|
||||
@change=${this._handleChange}
|
||||
></ha-textfield>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._save} .disabled=${disabled}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.core.section.core.core_config.save_button"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ class ConfigNetwork extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card outlined header="Network">
|
||||
<ha-card outlined header="Network Adapter">
|
||||
<div class="card-content">
|
||||
${this._error
|
||||
? html`
|
||||
|
@ -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`
|
||||
<hass-subpage
|
||||
@ -19,17 +40,84 @@ class HaConfigSectionStorage extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
>
|
||||
<div class="content"></div>
|
||||
<div class="content">
|
||||
${this._error
|
||||
? html`
|
||||
<ha-alert alert-type="error"
|
||||
>${this._error.message || this._error.code}</ha-alert
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
${this._storageData
|
||||
? html`
|
||||
<ha-card outlined>
|
||||
<ha-metric
|
||||
.description=${this.hass.localize(
|
||||
"ui.panel.config.storage.used_space"
|
||||
)}
|
||||
.value=${this._getUsedSpace(
|
||||
this._storageData?.disk_used,
|
||||
this._storageData?.disk_total
|
||||
)}
|
||||
.tooltip=${`${this._storageData.disk_used} GB/${this._storageData.disk_total} GB`}
|
||||
></ha-metric>
|
||||
${this._storageData.disk_life_time !== "" &&
|
||||
this._storageData.disk_life_time >= 10
|
||||
? html`
|
||||
<ha-metric
|
||||
.description=${this.hass.localize(
|
||||
"ui.panel.config.storage.emmc_lifetime_used"
|
||||
)}
|
||||
.value=${this._storageData.disk_life_time}
|
||||
.tooltip=${`${
|
||||
this._storageData.disk_life_time - 10
|
||||
} % -
|
||||
${this._storageData.disk_life_time} %`}
|
||||
class="emmc"
|
||||
></ha-metric>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
137
src/panels/config/core/ha-config-section-updates.ts
Normal file
137
src/panels/config/core/ha-config-section-updates.ts
Normal file
@ -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`
|
||||
<hass-subpage
|
||||
back-path="/config/system"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.header=${this.hass.localize("ui.panel.config.updates.caption")}
|
||||
>
|
||||
<div class="content">
|
||||
<ha-card outlined>
|
||||
${canInstallUpdates.length
|
||||
? html`
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updateEntities=${canInstallUpdates}
|
||||
showAll
|
||||
></ha-config-updates>
|
||||
`
|
||||
: html`
|
||||
${this.hass.localize("ui.panel.config.updates.no_updates")}
|
||||
`}
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ class HaConfigSystemNavigation extends LitElement {
|
||||
return html`
|
||||
<hass-subpage
|
||||
back-path="/config"
|
||||
.header=${this.hass.localize("ui.panel.config.dashboard.system.title")}
|
||||
.header=${this.hass.localize("ui.panel.config.dashboard.system.main")}
|
||||
>
|
||||
<ha-config-section
|
||||
.narrow=${this.narrow}
|
||||
@ -43,9 +43,7 @@ class HaConfigSystemNavigation extends LitElement {
|
||||
<ha-card>
|
||||
${this.narrow
|
||||
? html`<div class="title">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.dashboard.system.title"
|
||||
)}
|
||||
${this.hass.localize("ui.panel.config.dashboard.system.main")}
|
||||
</div>`
|
||||
: ""}
|
||||
<ha-navigation-list
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "@material/mwc-list/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { canShowPage } from "../../../common/config/can_show_page";
|
||||
import "../../../components/ha-card";
|
||||
@ -30,7 +30,7 @@ class HaConfigNavigation extends LitElement {
|
||||
name:
|
||||
page.name ||
|
||||
this.hass.localize(
|
||||
`ui.panel.config.dashboard.${page.translationKey}.title`
|
||||
`ui.panel.config.dashboard.${page.translationKey}.main`
|
||||
),
|
||||
description:
|
||||
page.component === "cloud" && (page.info as CloudStatus)
|
||||
@ -51,7 +51,7 @@ class HaConfigNavigation extends LitElement {
|
||||
${
|
||||
page.description ||
|
||||
this.hass.localize(
|
||||
`ui.panel.config.dashboard.${page.translationKey}.description`
|
||||
`ui.panel.config.dashboard.${page.translationKey}.secondary`
|
||||
)
|
||||
}
|
||||
`,
|
||||
@ -81,6 +81,12 @@ class HaConfigNavigation extends LitElement {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static styles: CSSResultGroup = css`
|
||||
ha-navigation-list {
|
||||
--navigation-list-item-title-font-size: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -2,7 +2,7 @@ import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/state-badge";
|
||||
import "../../../components/ha-alert";
|
||||
@ -19,7 +19,7 @@ class HaConfigUpdates extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public updateEntities?: UpdateEntity[];
|
||||
|
||||
@state() private _showAll = false;
|
||||
@property({ type: Boolean, reflect: true }) showAll = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.updateEntities?.length) {
|
||||
@ -27,7 +27,7 @@ class HaConfigUpdates extends LitElement {
|
||||
}
|
||||
|
||||
const updates =
|
||||
this._showAll || this.updateEntities.length <= 3
|
||||
this.showAll || this.updateEntities.length <= 3
|
||||
? this.updateEntities
|
||||
: this.updateEntities.slice(0, 2);
|
||||
|
||||
@ -66,7 +66,7 @@ class HaConfigUpdates extends LitElement {
|
||||
</paper-icon-item>
|
||||
`
|
||||
)}
|
||||
${!this._showAll && this.updateEntities.length >= 4
|
||||
${!this.showAll && this.updateEntities.length >= 4
|
||||
? html`
|
||||
<button class="show-more" @click=${this._showAllClicked}>
|
||||
${this.hass.localize("ui.panel.config.updates.more_updates", {
|
||||
@ -85,7 +85,7 @@ class HaConfigUpdates extends LitElement {
|
||||
}
|
||||
|
||||
private _showAllClicked() {
|
||||
this._showAll = true;
|
||||
this.showAll = true;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup[] {
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
mdiCog,
|
||||
mdiCpu32Bit,
|
||||
mdiDevices,
|
||||
mdiHomeAssistant,
|
||||
mdiInformation,
|
||||
mdiInformationOutline,
|
||||
mdiLightningBolt,
|
||||
@ -261,14 +260,6 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
},
|
||||
],
|
||||
general: [
|
||||
{
|
||||
component: "core",
|
||||
path: "/config/core",
|
||||
translationKey: "ui.panel.config.core.caption",
|
||||
iconPath: mdiHomeAssistant,
|
||||
iconColor: "#4A5963",
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "server_control",
|
||||
path: "/config/server_control",
|
||||
@ -277,19 +268,25 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
iconColor: "#4A5963",
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
path: "/config/updates",
|
||||
translationKey: "ui.panel.config.updates.caption",
|
||||
iconPath: mdiUpdate,
|
||||
iconColor: "#3B808E",
|
||||
},
|
||||
{
|
||||
component: "logs",
|
||||
path: "/config/logs",
|
||||
translationKey: "ui.panel.config.logs.caption",
|
||||
iconPath: mdiMathLog,
|
||||
iconColor: "#4A5963",
|
||||
iconColor: "#C65326",
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
path: "/config/backup",
|
||||
translationKey: "ui.panel.config.backup.caption",
|
||||
iconPath: mdiBackupRestore,
|
||||
iconColor: "#4084CD",
|
||||
iconColor: "#0D47A1",
|
||||
component: "backup",
|
||||
},
|
||||
{
|
||||
@ -298,12 +295,6 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
iconPath: mdiShape,
|
||||
iconColor: "#f1c447",
|
||||
},
|
||||
{
|
||||
path: "/config/hardware",
|
||||
translationKey: "ui.panel.config.hardware.caption",
|
||||
iconPath: mdiCpu32Bit,
|
||||
iconColor: "#4A5963",
|
||||
},
|
||||
{
|
||||
path: "/config/network",
|
||||
translationKey: "ui.panel.config.network.caption",
|
||||
@ -317,10 +308,10 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
iconColor: "#518C43",
|
||||
},
|
||||
{
|
||||
path: "/config/update",
|
||||
translationKey: "ui.panel.config.updates.caption",
|
||||
iconPath: mdiUpdate,
|
||||
iconColor: "#4A5963",
|
||||
path: "/config/hardware",
|
||||
translationKey: "ui.panel.config.hardware.caption",
|
||||
iconPath: mdiCpu32Bit,
|
||||
iconColor: "#301A8E",
|
||||
},
|
||||
],
|
||||
about: [
|
||||
@ -374,10 +365,6 @@ class HaPanelConfig extends HassRouterPage {
|
||||
tag: "ha-config-cloud",
|
||||
load: () => import("./cloud/ha-config-cloud"),
|
||||
},
|
||||
core: {
|
||||
tag: "ha-config-core",
|
||||
load: () => import("./core/ha-config-core"),
|
||||
},
|
||||
devices: {
|
||||
tag: "ha-config-devices",
|
||||
load: () => import("./devices/ha-config-devices"),
|
||||
@ -444,6 +431,10 @@ class HaPanelConfig extends HassRouterPage {
|
||||
tag: "ha-config-section-storage",
|
||||
load: () => import("./core/ha-config-section-storage"),
|
||||
},
|
||||
updates: {
|
||||
tag: "ha-config-section-updates",
|
||||
load: () => import("./core/ha-config-section-updates"),
|
||||
},
|
||||
users: {
|
||||
tag: "ha-config-users",
|
||||
load: () => import("./users/ha-config-users"),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
mdiCheck,
|
||||
mdiCheckCircleOutline,
|
||||
mdiDotsVertical,
|
||||
mdiOpenInNew,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
@ -16,6 +17,7 @@ import {
|
||||
DataTableColumnContainer,
|
||||
RowClickedEvent,
|
||||
} from "../../../../components/data-table/ha-data-table";
|
||||
import "../../../../components/ha-clickable-list-item";
|
||||
import "../../../../components/ha-fab";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@ -216,7 +218,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
if (isComponentLoaded(this.hass, "energy")) {
|
||||
result.push({
|
||||
icon: "hass:lightning-bolt",
|
||||
title: this.hass.localize(`ui.panel.config.dashboard.energy.title`),
|
||||
title: this.hass.localize(`ui.panel.config.dashboard.energy.main`),
|
||||
show_in_sidebar: true,
|
||||
mode: "storage",
|
||||
url_path: "energy",
|
||||
@ -260,6 +262,32 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
hasFab
|
||||
clickable
|
||||
>
|
||||
${this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
slot="toolbar-icon"
|
||||
activatable
|
||||
>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<ha-clickable-list-item
|
||||
@click=${this._entryClicked}
|
||||
href="/config/lovelace/resources"
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.panel.config.lovelace.resources.caption"
|
||||
)}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.lovelace.resources.caption"
|
||||
)}
|
||||
</ha-clickable-list-item>
|
||||
</ha-button-menu>
|
||||
`
|
||||
: ""}
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
.label=${this.hass.localize(
|
||||
@ -354,4 +382,8 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _entryClicked(ev) {
|
||||
ev.currentTarget.blur();
|
||||
}
|
||||
}
|
||||
|
@ -12,13 +12,6 @@ export const lovelaceTabs = [
|
||||
translationKey: "ui.panel.config.lovelace.dashboards.caption",
|
||||
icon: "hass:view-dashboard",
|
||||
},
|
||||
{
|
||||
component: "lovelace",
|
||||
path: "/config/lovelace/resources",
|
||||
translationKey: "ui.panel.config.lovelace.resources.caption",
|
||||
icon: "hass:file-multiple",
|
||||
advancedOnly: true,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("ha-config-lovelace")
|
||||
|
@ -157,7 +157,7 @@ export class HaConfigUsers extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
backPath="/config"
|
||||
.tabs=${configSections.areas}
|
||||
.tabs=${configSections.persons}
|
||||
.columns=${this._columns(this.narrow, this.hass.localize)}
|
||||
.data=${this._users}
|
||||
@row-click=${this._editUser}
|
||||
|
278
src/panels/config/zone/dialog-core-zone-detail.ts
Normal file
278
src/panels/config/zone/dialog-core-zone-detail.ts
Normal file
@ -0,0 +1,278 @@
|
||||
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 { HaRadio } from "../../../components/ha-radio";
|
||||
import "../../../components/ha-select";
|
||||
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, "Core Zone Configuration")}
|
||||
>
|
||||
${!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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
<ha-icon-button
|
||||
.entityId=${stateObject.entity_id}
|
||||
@click=${this._openCoreConfig}
|
||||
disabled=${ifDefined(
|
||||
stateObject.entity_id === "zone.home" &&
|
||||
this.narrow &&
|
||||
this._canEditCore
|
||||
? undefined
|
||||
: true
|
||||
)}
|
||||
.disabled=${stateObject.entity_id === "zone.home" &&
|
||||
!this._canEditCore}
|
||||
.path=${stateObject.entity_id === "zone.home" &&
|
||||
this.narrow &&
|
||||
this._canEditCore
|
||||
? mdiPencil
|
||||
: mdiPencilOff}
|
||||
@ -391,22 +385,8 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
|
||||
this._openDialog(entry);
|
||||
}
|
||||
|
||||
private async _openCoreConfig(ev: Event) {
|
||||
const entityId: string = (ev.currentTarget! as any).entityId;
|
||||
if (entityId !== "zone.home" || !this.narrow || !this._canEditCore) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.zone.go_to_core_config"),
|
||||
text: this.hass.localize("ui.panel.config.zone.home_zone_core_config"),
|
||||
confirmText: this.hass!.localize("ui.common.yes"),
|
||||
dismissText: this.hass!.localize("ui.common.no"),
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
navigate("/config/core");
|
||||
private async _openCoreConfig() {
|
||||
showCoreZoneDetailDialog(this);
|
||||
}
|
||||
|
||||
private async _createEntry(values: ZoneMutableParams) {
|
||||
|
12
src/panels/config/zone/show-dialog-core-zone-detail.ts
Normal file
12
src/panels/config/zone/show-dialog-core-zone-detail.ts
Normal file
@ -0,0 +1,12 @@
|
||||
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: {},
|
||||
});
|
||||
};
|
@ -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": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user