From 1431e75f8b8c0929610923b4b0e1b6d8674fbd1a Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Thu, 3 Sep 2020 18:28:10 -0500 Subject: [PATCH 001/146] More Info: Add History Tab (#6758) Co-authored-by: J. Nick Koston --- src/data/logbook.ts | 12 +- src/dialogs/more-info/ha-more-info-dialog.ts | 276 ++++++++++-------- .../more-info/ha-more-info-tab-history.ts | 164 +++++++++++ src/panels/logbook/ha-logbook.ts | 157 ++++++---- src/translations/en.json | 2 + 5 files changed, 428 insertions(+), 183 deletions(-) create mode 100644 src/dialogs/more-info/ha-more-info-tab-history.ts diff --git a/src/data/logbook.ts b/src/data/logbook.ts index c1ff44b6fe..00fd8e937d 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -23,7 +23,8 @@ export const getLogbookData = ( hass: HomeAssistant, startDate: string, endDate: string, - entityId?: string + entityId?: string, + entity_matches_only?: boolean ) => { const ALL_ENTITIES = "*"; @@ -51,7 +52,8 @@ export const getLogbookData = ( hass, startDate, endDate, - entityId !== ALL_ENTITIES ? entityId : undefined + entityId !== ALL_ENTITIES ? entityId : undefined, + entity_matches_only ).then((entries) => entries.reverse()); return DATA_CACHE[cacheKey][entityId]; }; @@ -60,11 +62,13 @@ const getLogbookDataFromServer = async ( hass: HomeAssistant, startDate: string, endDate: string, - entityId?: string + entityId?: string, + entity_matches_only?: boolean ) => { const url = `logbook/${startDate}?end_time=${endDate}${ entityId ? `&entity=${entityId}` : "" - }`; + }${entity_matches_only ? `&entity_matches_only` : ""}`; + return hass.callApi("GET", url); }; diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index b1953f678d..56d429836d 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -1,33 +1,34 @@ import "@material/mwc-button"; import "@material/mwc-icon-button"; -import "../../components/ha-header-bar"; -import "../../components/ha-dialog"; -import "../../components/ha-svg-icon"; +import "@material/mwc-tab"; +import "@material/mwc-tab-bar"; +import { mdiClose, mdiCog, mdiPencil } from "@mdi/js"; +import { + css, + customElement, + html, + internalProperty, + LitElement, + property, +} from "lit-element"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const"; +import { fireEvent } from "../../common/dom/fire_event"; +import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; import { navigate } from "../../common/navigate"; -import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-dialog"; +import "../../components/ha-header-bar"; +import "../../components/ha-svg-icon"; import "../../components/state-history-charts"; import { removeEntityRegistryEntry } from "../../data/entity_registry"; import { showEntityEditorDialog } from "../../panels/config/entities/show-dialog-entity-editor"; +import "../../panels/logbook/ha-logbook"; +import { haStyleDialog } from "../../resources/styles"; import "../../state-summary/state-card-content"; +import { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; import "./more-info-content"; -import { - customElement, - LitElement, - property, - internalProperty, - css, - html, -} from "lit-element"; -import { haStyleDialog } from "../../resources/styles"; -import { HomeAssistant } from "../../types"; -import { getRecentWithCache } from "../../data/cached-history"; -import { computeDomain } from "../../common/entity/compute_domain"; -import { mdiClose, mdiCog, mdiPencil } from "@mdi/js"; -import { HistoryResult } from "../../data/history"; const DOMAINS_NO_INFO = ["camera", "configurator"]; const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"]; @@ -43,11 +44,9 @@ export class MoreInfoDialog extends LitElement { @property({ type: Boolean, reflect: true }) public large = false; - @internalProperty() private _stateHistory?: HistoryResult; - @internalProperty() private _entityId?: string | null; - private _historyRefreshInterval?: number; + @internalProperty() private _currTabIndex = 0; public showDialog(params: MoreInfoDialogParams) { this._entityId = params.entityId; @@ -55,21 +54,11 @@ export class MoreInfoDialog extends LitElement { this.closeDialog(); } this.large = false; - this._stateHistory = undefined; - if (this._computeShowHistoryComponent(this._entityId)) { - this._getStateHistory(); - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = window.setInterval(() => { - this._getStateHistory(); - }, 60 * 1000); - } } public closeDialog() { this._entityId = undefined; - this._stateHistory = undefined; - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = undefined; + this._currTabIndex = 0; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -93,109 +82,123 @@ export class MoreInfoDialog extends LitElement { hideActions data-domain=${domain} > - - - - -
- ${computeStateName(stateObj)} -
- ${this.hass.user!.is_admin - ? html` - - ` - : ""} - ${this.hass.user!.is_admin && - ((EDITABLE_DOMAINS_WITH_ID.includes(domain) && - stateObj.attributes.id) || - EDITABLE_DOMAINS.includes(domain)) - ? html` - - ` - : ""} -
-
- ${DOMAINS_NO_INFO.includes(domain) - ? "" - : html` - - `} +
+ + + + +
+ ${computeStateName(stateObj)} +
+ ${this.hass.user!.is_admin + ? html` + + + + ` + : ""} + ${this.hass.user!.is_admin && + ((EDITABLE_DOMAINS_WITH_ID.includes(domain) && + stateObj.attributes.id) || + EDITABLE_DOMAINS.includes(domain)) + ? html` + + + + ` + : ""} +
${this._computeShowHistoryComponent(entityId) ? html` - + + + + ` : ""} - - - ${stateObj.attributes.restored - ? html`

- ${this.hass.localize( - "ui.dialogs.more_info_control.restored.not_provided" - )} -

-

- ${this.hass.localize( - "ui.dialogs.more_info_control.restored.remove_intro" - )} -

- - ${this.hass.localize( - "ui.dialogs.more_info_control.restored.remove_action" - )} - ` - : ""} +
+
+ ${this._currTabIndex === 0 + ? html` + ${DOMAINS_NO_INFO.includes(domain) + ? "" + : html` + + `} + + ${stateObj.attributes.restored + ? html` +

+ ${this.hass.localize( + "ui.dialogs.more_info_control.restored.not_provided" + )} +

+

+ ${this.hass.localize( + "ui.dialogs.more_info_control.restored.remove_intro" + )} +

+ + ${this.hass.localize( + "ui.dialogs.more_info_control.restored.remove_action" + )} + + ` + : ""} + ` + : html` + + `}
`; } - private _enlarge() { - this.large = !this.large; + protected firstUpdated(): void { + import("./ha-more-info-tab-history"); } - private async _getStateHistory(): Promise { - if (!this._entityId) { - return; - } - this._stateHistory = await getRecentWithCache( - this.hass!, - this._entityId, - { - refresh: 60, - cacheKey: `more_info.${this._entityId}`, - hoursToShow: 24, - }, - this.hass!.localize, - this.hass!.language - ); + private _enlarge() { + this.large = !this.large; } private _computeShowHistoryComponent(entityId) { @@ -243,6 +246,15 @@ export class MoreInfoDialog extends LitElement { this.closeDialog(); } + private _handleTabChanged(ev: CustomEvent): void { + const newTab = ev.detail.index; + if (newTab === this._currTabIndex) { + return; + } + + this._currTabIndex = ev.detail.index; + } + static get styles() { return [ haStyleDialog, @@ -256,8 +268,6 @@ export class MoreInfoDialog extends LitElement { --mdc-theme-on-primary: var(--primary-text-color); --mdc-theme-primary: var(--mdc-theme-surface); flex-shrink: 0; - border-bottom: 1px solid - var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); } @media all and (max-width: 450px), all and (max-height: 500px) { @@ -268,6 +278,11 @@ export class MoreInfoDialog extends LitElement { } } + .heading { + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + @media all and (min-width: 451px) and (min-height: 501px) { ha-dialog { --mdc-dialog-max-width: 90vw; @@ -306,8 +321,7 @@ export class MoreInfoDialog extends LitElement { --dialog-content-padding: 0; } - state-card-content, - state-history-charts { + state-card-content { display: block; margin-bottom: 16px; } @@ -315,3 +329,9 @@ export class MoreInfoDialog extends LitElement { ]; } } + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-dialog": MoreInfoDialog; + } +} diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts new file mode 100644 index 0000000000..036d66bdb6 --- /dev/null +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -0,0 +1,164 @@ +import { + css, + customElement, + html, + internalProperty, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; +import { computeStateDomain } from "../../common/entity/compute_state_domain"; +import "../../components/ha-circular-progress"; +import "../../components/state-history-charts"; +import { getRecentWithCache } from "../../data/cached-history"; +import { HistoryResult } from "../../data/history"; +import { getLogbookData, LogbookEntry } from "../../data/logbook"; +import "../../panels/logbook/ha-logbook"; +import { haStyleDialog } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-more-info-tab-history") +export class MoreInfoTabHistoryDialog extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public entityId!: string; + + @internalProperty() private _stateHistory?: HistoryResult; + + @internalProperty() private _entries?: LogbookEntry[]; + + @internalProperty() private _persons = {}; + + private _historyRefreshInterval?: number; + + protected render(): TemplateResult { + if (!this.entityId) { + return html``; + } + const stateObj = this.hass.states[this.entityId]; + + if (!stateObj) { + return html``; + } + + return html` + + ${!this._entries + ? html` + + ` + : html` + + `} + `; + } + + protected firstUpdated(): void { + this._fetchPersonNames(); + } + + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + if (!this.entityId) { + clearInterval(this._historyRefreshInterval); + } + + if (changedProps.has("entityId")) { + this._stateHistory = undefined; + this._entries = undefined; + + this._getStateHistory(); + this._getLogBookData(); + + clearInterval(this._historyRefreshInterval); + this._historyRefreshInterval = window.setInterval(() => { + this._getStateHistory(); + }, 60 * 1000); + } + } + + private async _getStateHistory(): Promise { + this._stateHistory = await getRecentWithCache( + this.hass!, + this.entityId, + { + refresh: 60, + cacheKey: `more_info.${this.entityId}`, + hoursToShow: 24, + }, + this.hass!.localize, + this.hass!.language + ); + } + + private async _getLogBookData() { + const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); + const now = new Date(); + this._entries = await getLogbookData( + this.hass, + yesterday.toISOString(), + now.toISOString(), + this.entityId, + true + ); + } + + private _fetchPersonNames() { + Object.values(this.hass.states).forEach((entity) => { + if ( + entity.attributes.user_id && + computeStateDomain(entity) === "person" + ) { + this._persons[entity.attributes.user_id] = + entity.attributes.friendly_name; + } + }); + } + + static get styles() { + return [ + haStyleDialog, + css` + state-history-charts { + display: block; + margin-bottom: 16px; + } + + ha-logbook.has-entries { + height: 360px; + } + + ha-circular-progress { + display: flex; + justify-content: center; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-tab-history": MoreInfoTabHistoryDialog; + } +} diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index af886126d3..173dc59231 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -1,35 +1,48 @@ import { css, CSSResult, + customElement, + eventOptions, html, LitElement, property, PropertyValues, TemplateResult, - eventOptions, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import { scroll } from "lit-virtualizer"; import { formatDate } from "../../common/datetime/format_date"; import { formatTimeWithSeconds } from "../../common/datetime/format_time"; +import { restoreScroll } from "../../common/decorators/restore-scroll"; import { fireEvent } from "../../common/dom/fire_event"; import { domainIcon } from "../../common/entity/domain_icon"; import { stateIcon } from "../../common/entity/state_icon"; import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl"; +import "../../components/ha-circular-progress"; import "../../components/ha-icon"; import { LogbookEntry } from "../../data/logbook"; import { HomeAssistant } from "../../types"; -import { restoreScroll } from "../../common/decorators/restore-scroll"; +@customElement("ha-logbook") class HaLogbook extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public userIdToName = {}; + @property({ attribute: false }) public userIdToName = {}; - @property() public entries: LogbookEntry[] = []; + @property({ attribute: false }) public entries: LogbookEntry[] = []; - @property({ attribute: "rtl", type: Boolean, reflect: true }) + @property({ type: Boolean, attribute: "narrow" }) + public narrow = false; + + @property({ attribute: "rtl", type: Boolean }) private _rtl = false; + @property({ type: Boolean, attribute: "no-icon" }) + public noIcon = false; + + @property({ type: Boolean, attribute: "no-name" }) + public noName = false; + // @ts-ignore @restoreScroll(".container") private _savedScrollPos?: number; @@ -52,14 +65,22 @@ class HaLogbook extends LitElement { protected render(): TemplateResult { if (!this.entries?.length) { return html` -
+
${this.hass.localize("ui.panel.logbook.entries_not_found")}
`; } return html` -
+
${scroll({ items: this.entries, renderItem: (item: LogbookEntry, index?: number) => @@ -76,6 +97,7 @@ class HaLogbook extends LitElement { if (index === undefined) { return html``; } + const previous = this.entries[index - 1]; const state = item.entity_id ? this.hass.states[item.entity_id] : undefined; const item_username = @@ -98,46 +120,52 @@ class HaLogbook extends LitElement {
${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
- -
- ${!item.entity_id - ? html` ${item.name} ` - : html` - ${item.name} - `} - ${item.message}${item_username - ? ` (${item_username})` - : ``} - ${!item.context_event_type - ? "" - : item.context_event_type === "call_service" - ? // Service Call - html` by service ${item.context_domain}.${item.context_service}` - : item.context_entity_id === item.entity_id - ? // HomeKit or something that self references - html` by - ${item.context_name - ? item.context_name - : item.context_event_type}` - : // Another entity such as an automation or script - html` by - ${item.context_entity_id_name}`} +
+ ${!this.noIcon + ? html` + + ` + : ""} +
+ ${!this.noName + ? !item.entity_id + ? html`${item.name}` + : html` + ${item.name} + ` + : ""} + ${item.message} + ${item_username ? ` (${item_username})` : ``} + ${!item.context_event_type + ? "" + : item.context_event_type === "call_service" + ? // Service Call + html` by service + ${item.context_domain}.${item.context_service}` + : item.context_entity_id === item.entity_id + ? // HomeKit or something that self references + html` by + ${item.context_name + ? item.context_name + : item.context_event_type}` + : // Another entity such as an automation or script + html` by + ${item.context_entity_id_name}`} +
@@ -163,26 +191,36 @@ class HaLogbook extends LitElement { height: 100%; } - :host([rtl]) { + .rtl { direction: ltr; } .entry { display: flex; line-height: 2em; + padding-bottom: 8px; } .time { width: 65px; flex-shrink: 0; - font-size: 0.8em; + font-size: 12px; color: var(--secondary-text-color); } - :host([rtl]) .date { + .rtl .date { direction: rtl; } + .icon-message { + display: flex; + align-items: center; + } + + .no-entries { + text-align: center; + } + ha-icon { margin: 0 8px 0 16px; flex-shrink: 0; @@ -193,6 +231,10 @@ class HaLogbook extends LitElement { color: var(--primary-text-color); } + .no-name .item-message { + text-transform: capitalize; + } + a { color: var(--primary-color); } @@ -212,8 +254,21 @@ class HaLogbook extends LitElement { .uni-virtualizer-host > * { box-sizing: border-box; } + + .narrow .entry { + flex-direction: column-reverse; + line-height: 1.5; + } + + .narrow .icon-message ha-icon { + margin-left: 0; + } `; } } -customElements.define("ha-logbook", HaLogbook); +declare global { + interface HTMLElementTagNameMap { + "ha-logbook": HaLogbook; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index df63a21322..0c0a64fd09 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -389,6 +389,8 @@ "dismiss": "Dismiss dialog", "settings": "Entity settings", "edit": "Edit entity", + "controls": "Controls", + "history": "History", "script": { "last_action": "Last Action", "last_triggered": "Last Triggered" From f3639c2663b70e76d1ccd4c084ab4d66f299cfa4 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Thu, 3 Sep 2020 18:28:53 -0500 Subject: [PATCH 002/146] Card Picker: Entity Picker (#6693) --- .../editor/card-editor/hui-card-picker.ts | 190 ++++----- .../card-editor/hui-dialog-create-card.ts | 278 +++++++++++++ .../card-editor/hui-dialog-edit-card.ts | 380 +++++++++--------- .../card-editor/hui-dialog-suggest-card.ts | 7 +- .../card-editor/hui-entity-picker-table.ts | 151 +++++++ .../card-editor/show-create-card-dialog.ts | 25 ++ .../unused-entities/hui-unused-entities.ts | 152 ++----- src/panels/lovelace/views/hui-view.ts | 4 +- src/translations/en.json | 7 +- 9 files changed, 780 insertions(+), 414 deletions(-) create mode 100644 src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts create mode 100644 src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts create mode 100644 src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index dce01a77c4..54e108a46f 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -1,4 +1,7 @@ +import "@material/mwc-tab-bar/mwc-tab-bar"; +import "@material/mwc-tab/mwc-tab"; import Fuse from "fuse.js"; +import memoizeOne from "memoize-one"; import { css, CSSResult, @@ -11,30 +14,35 @@ import { TemplateResult, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; +import { styleMap } from "lit-html/directives/style-map"; import { until } from "lit-html/directives/until"; -import memoizeOne from "memoize-one"; + import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../common/search/search-input"; import { UNAVAILABLE_STATES } from "../../../../data/entity"; -import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace"; import { CustomCardEntry, customCards, CUSTOM_TYPE_PREFIX, getCustomCardEntry, } from "../../../../data/lovelace_custom_cards"; -import { HomeAssistant } from "../../../../types"; import { - calcUnusedEntities, computeUsedEntities, + calcUnusedEntities, } from "../../common/compute-unused-entities"; import { tryCreateCardElement } from "../../create-element/create-card-element"; -import { LovelaceCard } from "../../types"; import { getCardStubConfig } from "../get-card-stub-config"; -import { CardPickTarget, Card } from "../types"; import { coreCards } from "../lovelace-cards"; -import { styleMap } from "lit-html/directives/style-map"; + +import type { CardPickTarget, Card } from "../types"; +import type { LovelaceCard } from "../../types"; +import type { HomeAssistant } from "../../../../types"; +import type { + LovelaceCardConfig, + LovelaceConfig, +} from "../../../../data/lovelace"; + import "../../../../components/ha-circular-progress"; +import "../../../../common/search/search-input"; interface CardElement { card: Card; @@ -53,14 +61,14 @@ export class HuiCardPicker extends LitElement { @internalProperty() private _filter = ""; - private _unusedEntities?: string[]; - - private _usedEntities?: string[]; - @internalProperty() private _width?: number; @internalProperty() private _height?: number; + private _unusedEntities?: string[]; + + private _usedEntities?: string[]; + private _filterCards = memoizeOne( (cardElements: CardElement[], filter?: string): CardElement[] => { if (!filter) { @@ -232,85 +240,6 @@ export class HuiCardPicker extends LitElement { this._filter = value; } - static get styles(): CSSResult[] { - return [ - css` - .cards-container { - display: grid; - grid-gap: 8px 8px; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - margin-top: 20px; - } - - .card { - height: 100%; - max-width: 500px; - display: flex; - flex-direction: column; - border-radius: 4px; - border: 1px solid var(--divider-color); - background: var(--primary-background-color, #fafafa); - cursor: pointer; - box-sizing: border-box; - position: relative; - } - - .card-header { - color: var(--ha-card-header-color, --primary-text-color); - font-family: var(--ha-card-header-font-family, inherit); - font-size: 16px; - font-weight: bold; - letter-spacing: -0.012em; - line-height: 20px; - padding: 12px 16px; - display: block; - text-align: center; - background: var( - --ha-card-background, - var(--card-background-color, white) - ); - border-radius: 0 0 4px 4px; - border-bottom: 1px solid var(--divider-color); - } - - .preview { - pointer-events: none; - margin: 20px; - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - } - - .preview > :first-child { - zoom: 0.6; - display: block; - width: 100%; - } - - .description { - text-align: center; - } - - .spinner { - align-items: center; - justify-content: center; - } - - .overlay { - position: absolute; - width: 100%; - height: 100%; - z-index: 1; - } - - .manual { - max-width: none; - } - `, - ]; - } - private _cardPicked(ev: Event): void { const config: LovelaceCardConfig = (ev.currentTarget! as CardPickTarget) .config; @@ -406,6 +335,85 @@ export class HuiCardPicker extends LitElement {
`; } + + static get styles(): CSSResult[] { + return [ + css` + .cards-container { + display: grid; + grid-gap: 8px 8px; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + margin-top: 20px; + } + + .card { + height: 100%; + max-width: 500px; + display: flex; + flex-direction: column; + border-radius: 4px; + border: 1px solid var(--divider-color); + background: var(--primary-background-color, #fafafa); + cursor: pointer; + box-sizing: border-box; + position: relative; + } + + .card-header { + color: var(--ha-card-header-color, --primary-text-color); + font-family: var(--ha-card-header-font-family, inherit); + font-size: 16px; + font-weight: bold; + letter-spacing: -0.012em; + line-height: 20px; + padding: 12px 16px; + display: block; + text-align: center; + background: var( + --ha-card-background, + var(--card-background-color, white) + ); + border-radius: 0 0 4px 4px; + border-bottom: 1px solid var(--divider-color); + } + + .preview { + pointer-events: none; + margin: 20px; + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + } + + .preview > :first-child { + zoom: 0.6; + display: block; + width: 100%; + } + + .description { + text-align: center; + } + + .spinner { + align-items: center; + justify-content: center; + } + + .overlay { + position: absolute; + width: 100%; + height: 100%; + z-index: 1; + } + + .manual { + max-width: none; + } + `, + ]; + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts new file mode 100644 index 0000000000..c0fb354da9 --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -0,0 +1,278 @@ +import "@material/mwc-tab-bar/mwc-tab-bar"; +import "@material/mwc-tab/mwc-tab"; +import { + css, + CSSResultArray, + customElement, + html, + internalProperty, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoize from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { computeDomain } from "../../../../common/entity/compute_domain"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { DataTableRowData } from "../../../../components/data-table/ha-data-table"; +import "../../../../components/ha-dialog"; +import "../../../../components/ha-header-bar"; +import type { LovelaceViewConfig } from "../../../../data/lovelace"; +import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; +import { haStyleDialog } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import "./hui-card-picker"; +import "./hui-entity-picker-table"; +import { + EditCardDialogParams, + showEditCardDialog, +} from "./show-edit-card-dialog"; +import { showSuggestCardDialog } from "./show-suggest-card-dialog"; + +declare global { + interface HASSDomEvents { + "selected-changed": SelectedChangedEvent; + } +} + +interface SelectedChangedEvent { + selectedEntities: string[]; +} + +@customElement("hui-dialog-create-card") +export class HuiCreateDialogCard extends LitElement implements HassDialog { + @property({ attribute: false }) protected hass!: HomeAssistant; + + @internalProperty() private _params?: EditCardDialogParams; + + @internalProperty() private _viewConfig!: LovelaceViewConfig; + + @internalProperty() private _selectedEntities: string[] = []; + + @internalProperty() private _currTabIndex = 0; + + public async showDialog(params: EditCardDialogParams): Promise { + this._params = params; + const [view] = params.path; + this._viewConfig = params.lovelaceConfig.views[view]; + } + + public closeDialog(): boolean { + this._params = undefined; + this._currTabIndex = 0; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + return true; + } + + protected render(): TemplateResult { + if (!this._params) { + return html``; + } + + return html` + +
+ +
+ ${this._viewConfig.title + ? this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.pick_card_view_title", + "name", + `"${this._viewConfig.title}"` + ) + : this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.pick_card" + )} +
+
+ + this._handleTabChanged(ev)} + > + + + +
+ ${this._currTabIndex === 0 + ? html` + + ` + : html` +
+ +
+ `} + +
+ + ${this.hass!.localize("ui.common.cancel")} + + ${this._selectedEntities.length + ? html` + + ${this.hass!.localize("ui.common.continue")} + + ` + : ""} +
+
+ `; + } + + private _ignoreKeydown(ev: KeyboardEvent) { + ev.stopPropagation(); + } + + static get styles(): CSSResultArray { + return [ + haStyleDialog, + css` + @media all and (max-width: 450px), all and (max-height: 500px) { + /* overrule the ha-style-dialog max-height on small screens */ + ha-dialog { + --mdc-dialog-max-height: 100%; + height: 100%; + } + } + + @media all and (min-width: 850px) { + ha-dialog { + --mdc-dialog-min-width: 845px; + } + } + + ha-dialog { + --mdc-dialog-max-width: 845px; + } + + ha-header-bar { + --mdc-theme-on-primary: var(--primary-text-color); + --mdc-theme-primary: var(--mdc-theme-surface); + flex-shrink: 0; + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + + @media (min-width: 1200px) { + ha-dialog { + --mdc-dialog-max-width: calc(100% - 32px); + --mdc-dialog-min-width: 1000px; + } + } + + .header_button { + color: inherit; + text-decoration: none; + } + + mwc-tab-bar { + padding-top: 8px; + } + + .entity-picker-container { + display: flex; + flex-direction: column; + height: 100%; + min-height: calc(100vh - 112px); + margin-top: -20px; + } + `, + ]; + } + + private _handleCardPicked(ev) { + const config = ev.detail.config; + if (this._params!.entities && this._params!.entities.length) { + if (Object.keys(config).includes("entities")) { + config.entities = this._params!.entities; + } else if (Object.keys(config).includes("entity")) { + config.entity = this._params!.entities[0]; + } + } + + showEditCardDialog(this, { + lovelaceConfig: this._params!.lovelaceConfig, + saveConfig: this._params!.saveConfig, + path: this._params!.path, + cardConfig: config, + }); + + this.closeDialog(); + } + + private _handleTabChanged(ev: CustomEvent): void { + const newTab = ev.detail.index; + if (newTab === this._currTabIndex) { + return; + } + + this._currTabIndex = ev.detail.index; + this._selectedEntities = []; + } + + private _handleSelectedChanged(ev: CustomEvent): void { + this._selectedEntities = ev.detail.selectedEntities; + } + + private _cancel(ev?: Event) { + if (ev) { + ev.stopPropagation(); + } + this.closeDialog(); + } + + private _suggestCards(): void { + showSuggestCardDialog(this, { + lovelaceConfig: this._params!.lovelaceConfig, + saveConfig: this._params!.saveConfig, + path: this._params!.path as [number], + entities: this._selectedEntities, + }); + + this.closeDialog(); + } + + private _allEntities = memoize((entities) => + Object.keys(entities).map((entity) => { + const stateObj = this.hass.states[entity]; + return { + icon: "", + entity_id: entity, + stateObj, + name: computeStateName(stateObj), + domain: computeDomain(entity), + last_changed: stateObj!.last_changed, + } as DataTableRowData; + }) + ); +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dialog-create-card": HuiCreateDialogCard; + } +} diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index effc794665..c8caba2a48 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -11,28 +11,32 @@ import { TemplateResult, PropertyValues, } from "lit-element"; -import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-dialog"; +import { mdiHelpCircle } from "@mdi/js"; + +import { fireEvent } from "../../../../common/dom/fire_event"; +import { haStyleDialog } from "../../../../resources/styles"; +import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; +import { addCard, replaceCard } from "../config-util"; +import { getCardDocumentationURL } from "../get-card-documentation-url"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; +import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; + +import type { HomeAssistant } from "../../../../types"; +import type { GUIModeChangedEvent } from "../types"; +import type { ConfigChangedEvent, HuiCardEditor } from "./hui-card-editor"; +import type { EditCardDialogParams } from "./show-edit-card-dialog"; +import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; +import type { HASSDomEvent } from "../../../../common/dom/fire_event"; import type { LovelaceCardConfig, LovelaceViewConfig, } from "../../../../data/lovelace"; -import { haStyleDialog } from "../../../../resources/styles"; -import "../../../../components/ha-circular-progress"; -import type { HomeAssistant } from "../../../../types"; -import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; -import { addCard, replaceCard } from "../config-util"; -import type { GUIModeChangedEvent } from "../types"; + import "./hui-card-editor"; -import type { ConfigChangedEvent, HuiCardEditor } from "./hui-card-editor"; -import "./hui-card-picker"; import "./hui-card-preview"; -import type { EditCardDialogParams } from "./show-edit-card-dialog"; -import { getCardDocumentationURL } from "../get-card-documentation-url"; -import { mdiHelpCircle } from "@mdi/js"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; -import { HassDialog } from "../../../../dialogs/make-dialog-manager"; -import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; +import "../../../../components/ha-dialog"; +import "../../../../components/ha-header-bar"; +import "../../../../components/ha-circular-progress"; declare global { // for fire event @@ -47,7 +51,7 @@ declare global { @customElement("hui-dialog-edit-card") export class HuiDialogEditCard extends LitElement implements HassDialog { - @property() protected hass!: HomeAssistant; + @property({ attribute: false }) public hass!: HomeAssistant; @internalProperty() private _params?: EditCardDialogParams; @@ -150,62 +154,56 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { @keydown=${this._ignoreKeydown} @closed=${this._cancel} @opened=${this._opened} - .heading=${html`${heading} - ${this._documentationURL !== undefined - ? html` - - - - - - ` - : ""}`} + .heading=${true} > -
- ${this._cardConfig === undefined - ? html` - - ` - : html` -
-
- -
-
- - ${this._error - ? html` - - ` - : ``} -
-
- `} +
+ +
${heading}
+ ${this._documentationURL !== undefined + ? html` + + + + + + ` + : ""} +
+
+
+
+ +
+
+ + ${this._error + ? html` + + ` + : ``} +
${this._cardConfig !== undefined ? html` @@ -256,126 +254,6 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { ev.stopPropagation(); } - static get styles(): CSSResultArray { - return [ - haStyleDialog, - css` - :host { - --code-mirror-max-height: calc(100vh - 176px); - } - - @media all and (max-width: 450px), all and (max-height: 500px) { - /* overrule the ha-style-dialog max-height on small screens */ - ha-dialog { - --mdc-dialog-max-height: 100%; - height: 100%; - } - } - - @media all and (min-width: 850px) { - ha-dialog { - --mdc-dialog-min-width: 845px; - } - } - - ha-dialog { - --mdc-dialog-max-width: 845px; - } - - .center { - margin-left: auto; - margin-right: auto; - } - - .content { - display: flex; - flex-direction: column; - margin: 0 -10px; - } - .content hui-card-preview { - margin: 4px auto; - max-width: 390px; - } - .content .element-editor { - margin: 0 10px; - } - - @media (min-width: 1200px) { - ha-dialog { - --mdc-dialog-max-width: calc(100% - 32px); - --mdc-dialog-min-width: 1000px; - } - - .content { - flex-direction: row; - } - .content > * { - flex-basis: 0; - flex-grow: 1; - flex-shrink: 1; - min-width: 0; - } - .content hui-card-preview { - padding: 8px 10px; - margin: auto 0px; - max-width: 500px; - } - } - - mwc-button ha-circular-progress { - margin-right: 20px; - } - .hidden { - display: none; - } - .element-editor { - margin-bottom: 8px; - } - .blur { - filter: blur(2px) grayscale(100%); - } - .element-preview { - position: relative; - } - .element-preview ha-circular-progress { - top: 50%; - left: 50%; - position: absolute; - z-index: 10; - } - hui-card-preview { - padding-top: 8px; - margin-bottom: 4px; - display: block; - width: 100%; - box-sizing: border-box; - } - .gui-mode-button { - margin-right: auto; - } - .header { - display: flex; - align-items: center; - justify-content: space-between; - } - `, - ]; - } - - private _handleCardPicked(ev) { - const config = ev.detail.config; - if (this._params!.entities && this._params!.entities.length) { - if (Object.keys(config).includes("entities")) { - config.entities = this._params!.entities; - } else if (Object.keys(config).includes("entity")) { - config.entity = this._params!.entities[0]; - } - } - this._cardConfig = deepFreeze(config); - this._error = ev.detail.error; - this._dirty = true; - } - private _handleConfigChanged(ev: HASSDomEvent) { this._cardConfig = deepFreeze(ev.detail.config); this._error = ev.detail.error; @@ -463,6 +341,124 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { showSaveSuccessToast(this, this.hass); this.closeDialog(); } + + static get styles(): CSSResultArray { + return [ + haStyleDialog, + css` + :host { + --code-mirror-max-height: calc(100vh - 176px); + } + + @media all and (max-width: 450px), all and (max-height: 500px) { + /* overrule the ha-style-dialog max-height on small screens */ + ha-dialog { + --mdc-dialog-max-height: 100%; + height: 100%; + } + } + + @media all and (min-width: 850px) { + ha-dialog { + --mdc-dialog-min-width: 845px; + } + } + + ha-dialog { + --mdc-dialog-max-width: 845px; + } + + ha-header-bar { + --mdc-theme-on-primary: var(--primary-text-color); + --mdc-theme-primary: var(--mdc-theme-surface); + flex-shrink: 0; + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + + .center { + margin-left: auto; + margin-right: auto; + } + + .content { + display: flex; + flex-direction: column; + margin: 0 -10px; + } + .content hui-card-preview { + margin: 4px auto; + max-width: 390px; + } + .content .element-editor { + margin: 0 10px; + } + + @media (min-width: 1200px) { + ha-dialog { + --mdc-dialog-max-width: calc(100% - 32px); + --mdc-dialog-min-width: 1000px; + } + + .content { + flex-direction: row; + } + .content > * { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + min-width: 0; + } + .content hui-card-preview { + padding: 8px 10px; + margin: auto 0px; + max-width: 500px; + } + } + + mwc-button ha-circular-progress { + margin-right: 20px; + } + .hidden { + display: none; + } + .element-editor { + margin-bottom: 8px; + } + .blur { + filter: blur(2px) grayscale(100%); + } + .element-preview { + position: relative; + } + .element-preview ha-circular-progress { + top: 50%; + left: 50%; + position: absolute; + z-index: 10; + } + hui-card-preview { + padding-top: 8px; + margin-bottom: 4px; + display: block; + width: 100%; + box-sizing: border-box; + } + .gui-mode-button { + margin-right: auto; + } + .header { + display: flex; + align-items: center; + justify-content: space-between; + } + .header_button { + color: inherit; + text-decoration: none; + } + `, + ]; + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts index a4f222200c..00e7d8b5a0 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts @@ -5,9 +5,9 @@ import { CSSResultArray, customElement, html, + internalProperty, LitElement, property, - internalProperty, query, TemplateResult, } from "lit-element"; @@ -21,7 +21,7 @@ import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; import { computeCards } from "../../common/generate-lovelace-config"; import { addCards } from "../config-util"; import "./hui-card-preview"; -import { showEditCardDialog } from "./show-edit-card-dialog"; +import { showCreateCardDialog } from "./show-create-card-dialog"; import { SuggestCardDialogParams } from "./show-suggest-card-dialog"; @customElement("hui-dialog-suggest-card") @@ -179,7 +179,8 @@ export class HuiDialogSuggestCard extends LitElement { ) { return; } - showEditCardDialog(this, { + + showCreateCardDialog(this, { lovelaceConfig: this._params!.lovelaceConfig, saveConfig: this._params!.saveConfig, path: this._params!.path, diff --git a/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts b/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts new file mode 100644 index 0000000000..3a724fc6e2 --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts @@ -0,0 +1,151 @@ +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import type { HASSDomEvent } from "../../../../common/dom/fire_event"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; +import "../../../../components/data-table/ha-data-table"; +import type { + DataTableColumnContainer, + DataTableRowData, + SelectionChangedEvent, +} from "../../../../components/data-table/ha-data-table"; +import "../../../../components/entity/state-badge"; +import "../../../../components/ha-relative-time"; +import type { HomeAssistant } from "../../../../types"; + +@customElement("hui-entity-picker-table") +export class HuiEntityPickerTable extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow?: boolean; + + @property({ type: Array }) public entities!: DataTableRowData[]; + + protected render(): TemplateResult { + return html` + + `; + } + + private _columns = memoizeOne((narrow: boolean) => { + const columns: DataTableColumnContainer = { + icon: { + title: "", + type: "icon", + template: (_icon, entity: any) => html` + + `, + }, + name: { + title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"), + sortable: true, + filterable: true, + grows: true, + direction: "asc", + template: (name, entity: any) => html` +
+ ${name} + ${narrow + ? html` +
+ ${entity.stateObj.entity_id} +
+ ` + : ""} +
+ `, + }, + }; + + columns.entity_id = { + title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity_id"), + sortable: true, + filterable: true, + width: "30%", + hidden: narrow, + }; + + columns.domain = { + title: this.hass!.localize("ui.panel.lovelace.unused_entities.domain"), + sortable: true, + filterable: true, + width: "15%", + hidden: narrow, + }; + + columns.last_changed = { + title: this.hass!.localize( + "ui.panel.lovelace.unused_entities.last_changed" + ), + type: "numeric", + sortable: true, + width: "15%", + hidden: narrow, + template: (lastChanged: string) => html` + + `, + }; + + return columns; + }); + + private _handleSelectionChanged( + ev: HASSDomEvent + ): void { + const selectedEntities = ev.detail.value; + + fireEvent(this, "selected-changed", { selectedEntities }); + } + + private _handleEntityClicked(ev: Event) { + const entityId = ((ev.target as HTMLElement).closest( + ".mdc-data-table__row" + ) as any).rowId; + fireEvent(this, "hass-more-info", { + entityId, + }); + } + + static get styles(): CSSResult { + return css` + ha-data-table { + --data-table-border-width: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-entity-picker-table": HuiEntityPickerTable; + } +} diff --git a/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts new file mode 100644 index 0000000000..42192a467e --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts @@ -0,0 +1,25 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; +import { LovelaceConfig } from "../../../../data/lovelace"; + +export interface CreateCardDialogParams { + lovelaceConfig: LovelaceConfig; + saveConfig: (config: LovelaceConfig) => void; + path: [number] | [number, number]; + entities?: string[]; // We can pass entity id's that will be added to the config when a card is picked +} + +const importCreateCardDialog = () => + import( + /* webpackChunkName: "hui-dialog-create-card" */ "./hui-dialog-create-card" + ); + +export const showCreateCardDialog = ( + element: HTMLElement, + createCardDialogParams: CreateCardDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "hui-dialog-create-card", + dialogImport: importCreateCardDialog, + dialogParams: createCardDialogParams, + }); +}; diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts index fdc35e7d84..1e81f187a5 100644 --- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts +++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts @@ -10,39 +10,31 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { mdiPlus } from "@mdi/js"; import { classMap } from "lit-html/directives/class-map"; -import memoizeOne from "memoize-one"; -import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; + import { computeDomain } from "../../../../common/entity/compute_domain"; import { computeStateName } from "../../../../common/entity/compute_state_name"; -import { - computeRTL, - computeRTLDirection, -} from "../../../../common/util/compute_rtl"; -import "../../../../components/data-table/ha-data-table"; -import type { - DataTableColumnContainer, - SelectionChangedEvent, -} from "../../../../components/data-table/ha-data-table"; -import "../../../../components/entity/state-badge"; -import "../../../../components/ha-icon"; -import "../../../../components/ha-relative-time"; -import type { LovelaceConfig } from "../../../../data/lovelace"; -import type { HomeAssistant } from "../../../../types"; +import { computeRTL } from "../../../../common/util/compute_rtl"; import { computeUnusedEntities } from "../../common/compute-unused-entities"; -import type { Lovelace } from "../../types"; -import "../../../../components/ha-svg-icon"; -import { mdiPlus } from "@mdi/js"; import { showSuggestCardDialog } from "../card-editor/show-suggest-card-dialog"; import { showSelectViewDialog } from "../select-view/show-select-view-dialog"; +import type { DataTableRowData } from "../../../../components/data-table/ha-data-table"; +import type { LovelaceConfig } from "../../../../data/lovelace"; +import type { HomeAssistant } from "../../../../types"; +import type { Lovelace } from "../../types"; + +import "../card-editor/hui-entity-picker-table"; +import "../../../../components/ha-svg-icon"; + @customElement("hui-unused-entities") export class HuiUnusedEntities extends LitElement { @property({ attribute: false }) public lovelace!: Lovelace; @property({ attribute: false }) public hass!: HomeAssistant; - @property() public narrow?: boolean; + @property({ type: Boolean }) public narrow?: boolean; @internalProperty() private _unusedEntities: string[] = []; @@ -52,74 +44,6 @@ export class HuiUnusedEntities extends LitElement { return this.lovelace.config; } - private _columns = memoizeOne((narrow: boolean) => { - const columns: DataTableColumnContainer = { - icon: { - title: "", - type: "icon", - template: (_icon, entity: any) => html` - - `, - }, - name: { - title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"), - sortable: true, - filterable: true, - grows: true, - direction: "asc", - template: (name, entity: any) => html` -
- ${name} - ${narrow - ? html` -
- ${entity.stateObj.entity_id} -
- ` - : ""} -
- `, - }, - }; - - if (narrow) { - return columns; - } - - columns.entity_id = { - title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity_id"), - sortable: true, - filterable: true, - width: "30%", - }; - columns.domain = { - title: this.hass!.localize("ui.panel.lovelace.unused_entities.domain"), - sortable: true, - filterable: true, - width: "15%", - }; - columns.last_changed = { - title: this.hass!.localize( - "ui.panel.lovelace.unused_entities.last_changed" - ), - type: "numeric", - sortable: true, - width: "15%", - template: (lastChanged: string) => html` - - `, - }; - - return columns; - }); - protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); @@ -161,9 +85,10 @@ export class HuiUnusedEntities extends LitElement { ` : ""} - { + { const stateObj = this.hass!.states[entity]; return { icon: "", @@ -173,18 +98,9 @@ export class HuiUnusedEntities extends LitElement { domain: computeDomain(entity), last_changed: stateObj!.last_changed, }; - })} - .id=${"entity_id"} - selectable - @selection-changed=${this._handleSelectionChanged} - .dir=${computeRTLDirection(this.hass)} - .searchLabel=${this.hass.localize( - "ui.panel.lovelace.unused_entities.search" - )} - .noDataText=${this.hass.localize( - "ui.panel.lovelace.unused_entities.no_data" - )} - > + }) as DataTableRowData[]} + @selected-changed=${this._handleSelectedChanged} + >
Date: Fri, 4 Sep 2020 00:32:27 +0000 Subject: [PATCH 003/146] [ci skip] Translation update --- translations/frontend/en.json | 16 ++++++++++++++-- translations/frontend/pl.json | 17 +++++++++++++++-- translations/frontend/zh-Hans.json | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 014a6c943d..482cf10f23 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -505,6 +505,7 @@ "back": "Back", "cancel": "Cancel", "close": "Close", + "continue": "Continue", "delete": "Delete", "error_required": "Required", "loading": "Loading", @@ -689,8 +690,10 @@ "crop": "Crop" }, "more_info_control": { + "controls": "Controls", "dismiss": "Dismiss dialog", "edit": "Edit entity", + "history": "History", "person": { "create_zone": "Create zone from current location" }, @@ -1912,7 +1915,7 @@ "filter": "Reload filter entities", "generic": "Reload generic IP camera entities", "generic_thermostat": "Reload generic thermostat entities", - "group": "Reload groups", + "group": "Reload groups, group entities, and notify services", "heading": "YAML configuration reloading", "history_stats": "Reload history stats entities", "homekit": "Reload HomeKit", @@ -1923,12 +1926,17 @@ "input_text": "Reload input texts", "introduction": "Some parts of Home Assistant can reload without requiring a restart. Hitting reload will unload their current YAML configuration and load the new one.", "min_max": "Reload min/max entities", + "mqtt": "Reload mqtt entities", "person": "Reload persons", "ping": "Reload ping binary sensor entities", - "rest": "Reload rest entities", + "reload": "Reload {domain}", + "rest": "Reload rest entities and notify services", + "rpi_gpio": "Reload Raspberry Pi GPIO entities", "scene": "Reload scenes", "script": "Reload scripts", + "smtp": "Reload smtp notify services", "statistics": "Reload statistics entities", + "telegram": "Reload telegram notify services", "template": "Reload template entities", "trend": "Reload trend entities", "universal": "Reload universal media player entities", @@ -2549,7 +2557,11 @@ } }, "cardpicker": { + "by_card": "By Card", + "by_entity": "By Entity", "custom_card": "Custom", + "domain": "Domain", + "entity": "Entity", "no_description": "No description available." }, "edit_card": { diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index ad4119b2f6..901952d6c0 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -379,7 +379,7 @@ "preset_mode": "Ustawienia", "swing_mode": "Tryb ruchu łopatek", "target_humidity": "Wilgotność docelowa", - "target_temperature": "Wilgotność docelowa", + "target_temperature": "Temperatura docelowa", "target_temperature_entity": "{name} temperatura docelowa", "target_temperature_mode": "{name} temperatura docelowa {mode}" }, @@ -617,6 +617,7 @@ "update": "Aktualizuj" }, "domain_toggler": { + "reset_entities": "Zresetuj encje", "title": "Włączanie domen" }, "entity_registry": { @@ -697,7 +698,7 @@ "confirm_remove_text": "Czy na pewno chcesz usunąć tę encję?", "confirm_remove_title": "Usunąć encję?", "not_provided": "Ta encja jest obecnie niedostępna i jest osierocona po usuniętej, zmienionej lub dysfunkcyjnej integracji, lub urządzeniu.", - "remove_action": "Usuń encje", + "remove_action": "Usuń encję", "remove_intro": "Jeśli encja nie jest używana możesz ją usunąć." }, "script": { @@ -1200,8 +1201,14 @@ }, "alexa": { "banner": "Edytowanie, które encje są udostępnione za pomocą interfejsu użytkownika, jest wyłączone, ponieważ skonfigurowano filtry encji w pliku configuration.yaml.", + "dont_expose_entity": "Nie udostępniaj encji", "expose": "Udostępnione do Alexy", + "expose_entity": "Udostępniaj encję", + "exposed": "{selected} udostępniona", "exposed_entities": "Udostępnione encje", + "follow_domain": "Obserwuj domenę", + "manage_domains": "Zarządzaj domenami", + "not_exposed": "{selected} nieudostępniona", "not_exposed_entities": "Nieudostępnione encje", "title": "Alexa" }, @@ -1239,8 +1246,14 @@ "google": { "banner": "Edytowanie, które encje są udostępnione za pomocą interfejsu użytkownika, jest wyłączone, ponieważ skonfigurowano filtry encji w pliku configuration.yaml.", "disable_2FA": "Wyłącz uwierzytelnianie dwuskładnikowe", + "dont_expose_entity": "Nie udostępniaj encji", "expose": "Udostępnione do Asystenta Google", + "expose_entity": "Udostępniaj encję", + "exposed": "{selected} udostępniona", "exposed_entities": "Udostępnione encje", + "follow_domain": "Obserwuj domenę", + "manage_domains": "Zarządzaj domenami", + "not_exposed": "{selected} nieudostępniona", "not_exposed_entities": "Nieudostępnione encje", "sync_to_google": "Synchronizowanie zmian z Google.", "title": "Asystent Google" diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index 96f370e5df..a318db0442 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -1776,7 +1776,7 @@ }, "select_instance": { "header": "选择一个 OpenZWave 实例", - "introduction": "您正在运行多个 OpenZWave 实例。您要管理哪个实例?" + "introduction": "有多个 OpenZWave 实例正在运行。您要管理哪个实例?" }, "services": { "add_node": "添加节点", From 426a0727c3d82fa58aa3a8c1e23f8e006b58dc5c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Sep 2020 14:52:21 +0200 Subject: [PATCH 004/146] Add person picture to user badge (#6784) * Use person picture ha-user-badge * Fix missing import * lint * User person picture in user-badge Co-authored-by: Ludeeus --- src/components/ha-sidebar.ts | 6 +- src/components/user/ha-user-badge.ts | 99 +++++++++++++++++--- src/components/user/ha-user-picker.ts | 6 +- src/panels/config/person/ha-config-person.ts | 11 ++- 4 files changed, 100 insertions(+), 22 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 575cb4cc2f..4f86c9f202 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -374,7 +374,11 @@ class HaSidebar extends LitElement { @mouseleave=${this._itemMouseLeave} > - + ${hass.user ? hass.user.name : ""} diff --git a/src/components/user/ha-user-badge.ts b/src/components/user/ha-user-badge.ts index 3e418ff596..c7fd114ae4 100644 --- a/src/components/user/ha-user-badge.ts +++ b/src/components/user/ha-user-badge.ts @@ -3,13 +3,16 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, TemplateResult, } from "lit-element"; +import { styleMap } from "lit-html/directives/style-map"; import { toggleAttribute } from "../../common/dom/toggle_attribute"; +import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { User } from "../../data/user"; -import { CurrentUser } from "../../types"; +import { CurrentUser, HomeAssistant } from "../../types"; const computeInitials = (name: string) => { if (!name) { @@ -29,26 +32,93 @@ const computeInitials = (name: string) => { @customElement("ha-user-badge") class StateBadge extends LitElement { - @property() public user?: User | CurrentUser; + @property({ attribute: false }) public hass!: HomeAssistant; - protected render(): TemplateResult { - const user = this.user; - const initials = user ? computeInitials(user.name) : "?"; - return html` ${initials} `; - } + @property({ attribute: false }) public user?: User | CurrentUser; + + @internalProperty() private _personPicture?: string; + + private _personEntityId?: string; protected updated(changedProps) { super.updated(changedProps); - toggleAttribute( - this, - "long", - (this.user ? computeInitials(this.user.name) : "?").length > 2 - ); + if (changedProps.has("user")) { + this._getPersonPicture(); + if (!this._personPicture) { + toggleAttribute( + this, + "long", + (this.hass.user ? computeInitials(this.hass.user.name) : "?").length > + 2 + ); + } + return; + } + const oldHass = changedProps.get("hass"); + if ( + this._personEntityId && + oldHass && + this.hass.states[this._personEntityId] !== + oldHass.states[this._personEntityId] + ) { + const state = this.hass.states[this._personEntityId]; + if (state) { + this._personPicture = state.attributes.entity_picture; + } else { + this._getPersonPicture(); + } + } + } + + protected render(): TemplateResult { + if (!this.hass || !this.user) { + return html``; + } + const user = this.user; + const picture = this._personPicture; + + return html` + ${picture + ? html`
` + : html`
2} + > + ${computeInitials(user?.name!)} +
`} + `; + } + + private _getPersonPicture() { + this._personEntityId = undefined; + this._personPicture = undefined; + if (!this.hass || !this.user) { + return; + } + for (const entity of Object.values(this.hass.states)) { + if ( + entity.attributes.user_id === this.user!.id && + computeStateDomain(entity) === "person" + ) { + this._personEntityId = entity.entity_id; + this._personPicture = entity.attributes.entity_picture; + break; + } + } } static get styles(): CSSResult { return css` - :host { + .picture { + width: 40px; + height: 40px; + background-size: cover; + border-radius: 50%; + } + .initials { display: inline-block; box-sizing: border-box; width: 40px; @@ -60,8 +130,7 @@ class StateBadge extends LitElement { color: var(--text-light-primary-color, var(--primary-text-color)); overflow: hidden; } - - :host([long]) { + :host([long]) .initials { font-size: 80%; } `; diff --git a/src/components/user/ha-user-picker.ts b/src/components/user/ha-user-picker.ts index d9a7fda1ec..ecd99f188f 100644 --- a/src/components/user/ha-user-picker.ts +++ b/src/components/user/ha-user-picker.ts @@ -53,7 +53,11 @@ class HaUserPicker extends LitElement { ${this._sortedUsers(this.users).map( (user) => html` - + ${user.name} ` diff --git a/src/panels/config/person/ha-config-person.ts b/src/panels/config/person/ha-config-person.ts index 764ae63b28..24f794b4e8 100644 --- a/src/panels/config/person/ha-config-person.ts +++ b/src/panels/config/person/ha-config-person.ts @@ -1,17 +1,21 @@ +import "@material/mwc-fab"; +import { mdiPlus } from "@mdi/js"; import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; import { css, CSSResult, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { styleMap } from "lit-html/directives/style-map"; import { compare } from "../../../common/string/compare"; import "../../../components/ha-card"; -import "@material/mwc-fab"; +import "../../../components/ha-svg-icon"; +import "../../../components/user/ha-user-badge"; import { createPerson, deletePerson, @@ -30,9 +34,6 @@ import { loadPersonDetailDialog, showPersonDetailDialog, } from "./show-dialog-person-detail"; -import "../../../components/ha-svg-icon"; -import { mdiPlus } from "@mdi/js"; -import { styleMap } from "lit-html/directives/style-map"; class HaConfigPerson extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; From b7845c318eef39a4e8fe2f17bf40113e5d7fad44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 4 Sep 2020 15:08:30 +0200 Subject: [PATCH 005/146] Error extraction and target cleanup (#6782) --- hassio/src/addon-store/hassio-addon-store.ts | 9 +++-- .../addon-view/config/hassio-addon-audio.ts | 2 +- .../addon-view/config/hassio-addon-config.ts | 18 ++++----- .../addon-view/config/hassio-addon-network.ts | 21 +++++----- .../hassio-addon-documentation-tab.ts | 11 +++--- .../src/addon-view/info/hassio-addon-info.ts | 4 +- .../src/addon-view/log/hassio-addon-logs.ts | 5 ++- hassio/src/dashboard/hassio-update.ts | 24 ++++++------ .../dialogs/network/dialog-hassio-network.ts | 38 +++++++++---------- .../dialog-hassio-repositories.ts | 9 +++-- .../snapshot/dialog-hassio-snapshot.ts | 3 +- hassio/src/dialogs/suggestAddonRestart.ts | 3 +- hassio/src/snapshots/hassio-snapshots.ts | 32 ++++++++-------- hassio/src/system/hassio-host-info.ts | 25 +++++------- hassio/src/system/hassio-supervisor-info.ts | 19 ++++------ hassio/src/system/hassio-supervisor-log.ts | 21 +++++----- 16 files changed, 118 insertions(+), 126 deletions(-) diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts index c02d8c1b12..baf2fef515 100644 --- a/hassio/src/addon-store/hassio-addon-store.ts +++ b/hassio/src/addon-store/hassio-addon-store.ts @@ -1,12 +1,13 @@ import "@material/mwc-icon-button/mwc-icon-button"; +import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiDotsVertical } from "@mdi/js"; import { css, CSSResult, + internalProperty, LitElement, property, - internalProperty, PropertyValues, } from "lit-element"; import { html, TemplateResult } from "lit-html"; @@ -19,13 +20,13 @@ import { HassioAddonRepository, reloadHassioAddons, } from "../../../src/data/hassio/addon"; -import "../../../src/layouts/hass-tabs-subpage"; +import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import "../../../src/layouts/hass-loading-screen"; +import "../../../src/layouts/hass-tabs-subpage"; import { HomeAssistant, Route } from "../../../src/types"; import { showRepositoriesDialog } from "../dialogs/repositories/show-dialog-repositories"; import { supervisorTabs } from "../hassio-tabs"; import "./hassio-addon-repository"; -import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => { if (a.slug === "local") { @@ -179,7 +180,7 @@ class HassioAddonStore extends LitElement { this._repos.sort(sortRepos); this._addons = addonsInfo.addons; } catch (err) { - alert("Failed to fetch add-on info"); + alert(extractApiErrorMessage(err)); } } diff --git a/hassio/src/addon-view/config/hassio-addon-audio.ts b/hassio/src/addon-view/config/hassio-addon-audio.ts index a5de831ba2..71ef1170d5 100644 --- a/hassio/src/addon-view/config/hassio-addon-audio.ts +++ b/hassio/src/addon-view/config/hassio-addon-audio.ts @@ -176,7 +176,7 @@ class HassioAddonAudio extends LitElement { } private async _saveSettings(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; this._error = undefined; diff --git a/hassio/src/addon-view/config/hassio-addon-config.ts b/hassio/src/addon-view/config/hassio-addon-config.ts index 4fa442e3a3..82755a9840 100644 --- a/hassio/src/addon-view/config/hassio-addon-config.ts +++ b/hassio/src/addon-view/config/hassio-addon-config.ts @@ -5,14 +5,15 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, query, TemplateResult, } from "lit-element"; import { fireEvent } from "../../../../src/common/dom/fire_event"; +import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/ha-card"; import "../../../../src/components/ha-yaml-editor"; import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor"; @@ -21,8 +22,7 @@ import { HassioAddonSetOptionParams, setHassioAddonOption, } from "../../../../src/data/hassio/addon"; -import "../../../../src/components/buttons/ha-progress-button"; - +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../src/resources/styles"; import type { HomeAssistant } from "../../../../src/types"; @@ -113,9 +113,9 @@ class HassioAddonConfig extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to reset addon configuration, ${ - err.body?.message || err - }`; + this._error = `Failed to reset addon configuration, ${extractApiErrorMessage( + err + )}`; } button.progress = false; } @@ -147,9 +147,9 @@ class HassioAddonConfig extends LitElement { await suggestAddonRestart(this, this.hass, this.addon); } } catch (err) { - this._error = `Failed to save addon configuration, ${ - err.body?.message || err - }`; + this._error = `Failed to save addon configuration, ${extractApiErrorMessage( + err + )}`; } button.progress = false; } diff --git a/hassio/src/addon-view/config/hassio-addon-network.ts b/hassio/src/addon-view/config/hassio-addon-network.ts index 07ce08f5a4..d90a68ffd3 100644 --- a/hassio/src/addon-view/config/hassio-addon-network.ts +++ b/hassio/src/addon-view/config/hassio-addon-network.ts @@ -4,24 +4,25 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; import { fireEvent } from "../../../../src/common/dom/fire_event"; +import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/ha-card"; import { HassioAddonDetails, HassioAddonSetOptionParams, setHassioAddonOption, } from "../../../../src/data/hassio/addon"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; import { hassioStyle } from "../../resources/hassio-style"; -import "../../../../src/components/buttons/ha-progress-button"; interface NetworkItem { description: string; @@ -130,7 +131,7 @@ class HassioAddonNetwork extends LitElement { } private async _resetTapped(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; const data: HassioAddonSetOptionParams = { @@ -149,16 +150,16 @@ class HassioAddonNetwork extends LitElement { await suggestAddonRestart(this, this.hass, this.addon); } } catch (err) { - this._error = `Failed to set addon network configuration, ${ - err.body?.message || err - }`; + this._error = `Failed to set addon network configuration, ${extractApiErrorMessage( + err + )}`; } button.progress = false; } private async _saveTapped(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; this._error = undefined; @@ -183,9 +184,9 @@ class HassioAddonNetwork extends LitElement { await suggestAddonRestart(this, this.hass, this.addon); } } catch (err) { - this._error = `Failed to set addon network configuration, ${ - err.body?.message || err - }`; + this._error = `Failed to set addon network configuration, ${extractApiErrorMessage( + err + )}`; } button.progress = false; } diff --git a/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts b/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts index 7e5b519886..3d604e03bb 100644 --- a/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts +++ b/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts @@ -3,18 +3,19 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-markdown"; import { fetchHassioAddonDocumentation, HassioAddonDetails, } from "../../../../src/data/hassio/addon"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import "../../../../src/layouts/hass-loading-screen"; -import "../../../../src/components/ha-circular-progress"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { hassioStyle } from "../../resources/hassio-style"; @@ -80,9 +81,9 @@ class HassioAddonDocumentationDashboard extends LitElement { this.addon!.slug ); } catch (err) { - this._error = `Failed to get addon documentation, ${ - err.body?.message || err - }`; + this._error = `Failed to get addon documentation, ${extractApiErrorMessage( + err + )}`; } } } diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index a17c7cc86c..1de072ee99 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -768,7 +768,7 @@ class HassioAddonInfo extends LitElement { } private async _installClicked(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; try { @@ -833,7 +833,7 @@ class HassioAddonInfo extends LitElement { } private async _uninstallClicked(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; const confirmed = await showConfirmationDialog(this, { diff --git a/hassio/src/addon-view/log/hassio-addon-logs.ts b/hassio/src/addon-view/log/hassio-addon-logs.ts index e9f6703c2c..314dc29c09 100644 --- a/hassio/src/addon-view/log/hassio-addon-logs.ts +++ b/hassio/src/addon-view/log/hassio-addon-logs.ts @@ -4,9 +4,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import "../../../../src/components/ha-card"; @@ -14,6 +14,7 @@ import { fetchHassioAddonLogs, HassioAddonDetails, } from "../../../../src/data/hassio/addon"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import "../../components/hassio-ansi-to-html"; @@ -75,7 +76,7 @@ class HassioAddonLogs extends LitElement { try { this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug); } catch (err) { - this._error = `Failed to get addon logs, ${err.body?.message || err}`; + this._error = `Failed to get addon logs, ${extractApiErrorMessage(err)}`; } } diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index 4af34bb58b..3bb15444ed 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -5,27 +5,30 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/ha-card"; import "../../../src/components/ha-svg-icon"; +import { + extractApiErrorMessage, + HassioResponse, +} from "../../../src/data/hassio/common"; import { HassioHassOSInfo } from "../../../src/data/hassio/host"; import { HassioHomeAssistantInfo, HassioSupervisorInfo, } from "../../../src/data/hassio/supervisor"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../src/dialogs/generic/show-dialog-box"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; import { hassioStyle } from "../resources/hassio-style"; -import { - showConfirmationDialog, - showAlertDialog, -} from "../../../src/dialogs/generic/show-dialog-box"; -import { HassioResponse } from "../../../src/data/hassio/common"; @customElement("hassio-update") export class HassioUpdate extends LitElement { @@ -145,7 +148,7 @@ export class HassioUpdate extends LitElement { } private async _confirmUpdate(ev): Promise { - const item = ev.target; + const item = ev.currentTarget; item.progress = true; const confirmed = await showConfirmationDialog(this, { title: `Update ${item.name}`, @@ -165,12 +168,7 @@ export class HassioUpdate extends LitElement { if (err.status_code && err.status_code !== 504) { showAlertDialog(this, { title: "Update failed", - text: - typeof err === "object" - ? typeof err.body === "object" - ? err.body.message - : err.body || "Unkown error" - : err, + text: extractApiErrorMessage(err), }); } } diff --git a/hassio/src/dialogs/network/dialog-hassio-network.ts b/hassio/src/dialogs/network/dialog-hassio-network.ts index f49d1c1a86..1ab958891b 100644 --- a/hassio/src/dialogs/network/dialog-hassio-network.ts +++ b/hassio/src/dialogs/network/dialog-hassio-network.ts @@ -1,43 +1,42 @@ import "@material/mwc-button/mwc-button"; import "@material/mwc-icon-button"; -import "@material/mwc-tab-bar"; import "@material/mwc-tab"; -import { PaperInputElement } from "@polymer/paper-input/paper-input"; +import "@material/mwc-tab-bar"; import { mdiClose } from "@mdi/js"; +import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import { cache } from "lit-html/directives/cache"; - -import { - updateNetworkInterface, - NetworkInterface, -} from "../../../../src/data/hassio/network"; import { fireEvent } from "../../../../src/common/dom/fire_event"; -import { HassioNetworkDialogParams } from "./show-dialog-network"; -import { haStyleDialog } from "../../../../src/resources/styles"; -import { - showAlertDialog, - showConfirmationDialog, -} from "../../../../src/dialogs/generic/show-dialog-box"; -import type { HomeAssistant } from "../../../../src/types"; -import type { HaRadio } from "../../../../src/components/ha-radio"; -import { HassDialog } from "../../../../src/dialogs/make-dialog-manager"; - import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-formfield"; import "../../../../src/components/ha-header-bar"; import "../../../../src/components/ha-radio"; +import type { HaRadio } from "../../../../src/components/ha-radio"; import "../../../../src/components/ha-related-items"; import "../../../../src/components/ha-svg-icon"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; +import { + NetworkInterface, + updateNetworkInterface, +} from "../../../../src/data/hassio/network"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../../src/dialogs/generic/show-dialog-box"; +import { HassDialog } from "../../../../src/dialogs/make-dialog-manager"; +import { haStyleDialog } from "../../../../src/resources/styles"; +import type { HomeAssistant } from "../../../../src/types"; +import { HassioNetworkDialogParams } from "./show-dialog-network"; @customElement("dialog-hassio-network") export class DialogHassioNetwork extends LitElement implements HassDialog { @@ -201,8 +200,7 @@ export class DialogHassioNetwork extends LitElement implements HassDialog { } catch (err) { showAlertDialog(this, { title: "Failed to change network settings", - text: - typeof err === "object" ? err.body.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); this._prosessing = false; return; diff --git a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts index 0f9f42f860..150fc2b181 100644 --- a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts +++ b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts @@ -5,25 +5,26 @@ import "@polymer/paper-input/paper-input"; import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item-body"; -import "../../../../src/components/ha-circular-progress"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, query, TemplateResult, } from "lit-element"; import memoizeOne from "memoize-one"; +import "../../../../src/components/ha-circular-progress"; import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-svg-icon"; import { fetchHassioAddonsInfo, HassioAddonRepository, } from "../../../../src/data/hassio/addon"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { setSupervisorOption } from "../../../../src/data/hassio/supervisor"; import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import type { HomeAssistant } from "../../../../src/types"; @@ -190,7 +191,7 @@ class HassioRepositoriesDialog extends LitElement { input.value = ""; } catch (err) { - this._error = err.message; + this._error = extractApiErrorMessage(err); } this._prosessing = false; } @@ -222,7 +223,7 @@ class HassioRepositoriesDialog extends LitElement { await this._dialogParams!.loadData(); } catch (err) { - this._error = err.message; + this._error = extractApiErrorMessage(err); } } } diff --git a/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts b/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts index b54e18edd4..eb70aa51c7 100755 --- a/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts +++ b/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts @@ -15,6 +15,7 @@ import { import { createCloseHeading } from "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-svg-icon"; import { getSignedPath } from "../../../../src/data/auth"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { fetchHassioSnapshotInfo, HassioSnapshotDetail, @@ -379,7 +380,7 @@ class HassioSnapshotDialog extends LitElement { `/api/hassio/snapshots/${this._snapshot!.slug}/download` ); } catch (err) { - alert(`Error: ${err.message}`); + alert(`Error: ${extractApiErrorMessage(err)}`); return; } diff --git a/hassio/src/dialogs/suggestAddonRestart.ts b/hassio/src/dialogs/suggestAddonRestart.ts index 2af4f31fa4..de4343b379 100644 --- a/hassio/src/dialogs/suggestAddonRestart.ts +++ b/hassio/src/dialogs/suggestAddonRestart.ts @@ -3,6 +3,7 @@ import { HassioAddonDetails, restartHassioAddon, } from "../../../src/data/hassio/addon"; +import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { showAlertDialog, showConfirmationDialog, @@ -26,7 +27,7 @@ export const suggestAddonRestart = async ( } catch (err) { showAlertDialog(element, { title: "Failed to restart", - text: err.body.message, + text: extractApiErrorMessage(err), }); } } diff --git a/hassio/src/snapshots/hassio-snapshots.ts b/hassio/src/snapshots/hassio-snapshots.ts index 90f338ba7b..3bc9bfcc45 100644 --- a/hassio/src/snapshots/hassio-snapshots.ts +++ b/hassio/src/snapshots/hassio-snapshots.ts @@ -1,15 +1,13 @@ import "@material/mwc-button"; import "@material/mwc-icon-button"; +import { mdiPackageVariant, mdiPackageVariantClosed, mdiReload } from "@mdi/js"; import "@polymer/paper-checkbox/paper-checkbox"; +import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox"; import "@polymer/paper-input/paper-input"; +import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import "@polymer/paper-radio-button/paper-radio-button"; import "@polymer/paper-radio-group/paper-radio-group"; - -import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import type { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group"; -import { mdiPackageVariant, mdiPackageVariantClosed, mdiReload } from "@mdi/js"; -import { fireEvent } from "../../../src/common/dom/fire_event"; import { css, CSSResultArray, @@ -21,7 +19,11 @@ import { PropertyValues, TemplateResult, } from "lit-element"; - +import { fireEvent } from "../../../src/common/dom/fire_event"; +import "../../../src/components/buttons/ha-progress-button"; +import "../../../src/components/ha-card"; +import "../../../src/components/ha-svg-icon"; +import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { createHassioFullSnapshot, createHassioPartialSnapshot, @@ -31,19 +33,15 @@ import { HassioSnapshot, reloadHassioSnapshots, } from "../../../src/data/hassio/snapshot"; -import "../../../src/components/buttons/ha-progress-button"; -import { hassioStyle } from "../resources/hassio-style"; import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor"; +import "../../../src/layouts/hass-tabs-subpage"; +import { PolymerChangedEvent } from "../../../src/polymer-types"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant, Route } from "../../../src/types"; -import { PolymerChangedEvent } from "../../../src/polymer-types"; +import "../components/hassio-card-content"; import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot"; import { supervisorTabs } from "../hassio-tabs"; - -import "../../../src/components/ha-card"; -import "../../../src/components/ha-svg-icon"; -import "../../../src/layouts/hass-tabs-subpage"; -import "../components/hassio-card-content"; +import { hassioStyle } from "../resources/hassio-style"; interface CheckboxItem { slug: string; @@ -292,12 +290,12 @@ class HassioSnapshots extends LitElement { this._snapshots = await fetchHassioSnapshots(this.hass); this._snapshots.sort((a, b) => (a.date < b.date ? 1 : -1)); } catch (err) { - this._error = err.message; + this._error = extractApiErrorMessage(err); } } private async _createSnapshot(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; this._error = ""; @@ -345,7 +343,7 @@ class HassioSnapshots extends LitElement { this._updateSnapshots(); fireEvent(this, "hass-api-called", { success: true, response: null }); } catch (err) { - this._error = err.message; + this._error = extractApiErrorMessage(err); } button.progress = false; } diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index dfe7224b4a..bdd3e801b4 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -19,6 +19,7 @@ import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-card"; import "../../../src/components/ha-settings-row"; +import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware"; import { changeHostOptions, @@ -220,14 +221,13 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to get Hardware list", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } } private async _hostReboot(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; const confirmed = await showConfirmationDialog(this, { @@ -247,15 +247,14 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to reboot", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } button.progress = false; } private async _hostShutdown(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; const confirmed = await showConfirmationDialog(this, { @@ -275,15 +274,14 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to shutdown", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } button.progress = false; } private async _osUpdate(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; const confirmed = await showConfirmationDialog(this, { @@ -303,8 +301,7 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to update", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } button.progress = false; @@ -333,8 +330,7 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Setting hostname failed", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } } @@ -347,8 +343,7 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to import from USB", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } } diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index 6d0bf96efe..fd043751de 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -26,6 +26,7 @@ import { import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; import { hassioStyle } from "../resources/hassio-style"; +import { extractApiErrorMessage } from "../../../src/data/hassio/common"; @customElement("hassio-supervisor-info") class HassioSupervisorInfo extends LitElement { @@ -143,7 +144,7 @@ class HassioSupervisorInfo extends LitElement { } private async _toggleBeta(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; if (this.supervisorInfo.channel === "stable") { @@ -182,15 +183,14 @@ class HassioSupervisorInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to set supervisor option", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } button.progress = false; } private async _supervisorReload(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; try { @@ -198,15 +198,14 @@ class HassioSupervisorInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to reload the supervisor", - text: - typeof err === "object" ? err.body?.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } button.progress = false; } private async _supervisorUpdate(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; const confirmed = await showConfirmationDialog(this, { @@ -226,8 +225,7 @@ class HassioSupervisorInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to update the supervisor", - text: - typeof err === "object" ? err.body.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } button.progress = false; @@ -257,8 +255,7 @@ class HassioSupervisorInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to set supervisor option", - text: - typeof err === "object" ? err.body.message || "Unkown error" : err, + text: extractApiErrorMessage(err), }); } } diff --git a/hassio/src/system/hassio-supervisor-log.ts b/hassio/src/system/hassio-supervisor-log.ts index fdd0ce70db..88528eb431 100644 --- a/hassio/src/system/hassio-supervisor-log.ts +++ b/hassio/src/system/hassio-supervisor-log.ts @@ -12,16 +12,15 @@ import { property, TemplateResult, } from "lit-element"; - -import { fetchHassioLogs } from "../../../src/data/hassio/supervisor"; -import { hassioStyle } from "../resources/hassio-style"; -import { haStyle } from "../../../src/resources/styles"; -import { HomeAssistant } from "../../../src/types"; - import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/ha-card"; +import { extractApiErrorMessage } from "../../../src/data/hassio/common"; +import { fetchHassioLogs } from "../../../src/data/hassio/supervisor"; import "../../../src/layouts/hass-loading-screen"; +import { haStyle } from "../../../src/resources/styles"; +import { HomeAssistant } from "../../../src/types"; import "../components/hassio-ansi-to-html"; +import { hassioStyle } from "../resources/hassio-style"; interface LogProvider { key: string; @@ -116,11 +115,11 @@ class HassioSupervisorLog extends LitElement { private async _setLogProvider(ev): Promise { const provider = ev.detail.item.getAttribute("provider"); this._selectedLogProvider = provider; - this._loadData(); + this._loadData(); } private async _refresh(ev: CustomEvent): Promise { - const button = ev.target as any; + const button = ev.currentTarget as any; button.progress = true; await this._loadData(); button.progress = false; @@ -135,9 +134,9 @@ class HassioSupervisorLog extends LitElement { this._selectedLogProvider ); } catch (err) { - this._error = `Failed to get supervisor logs, ${ - typeof err === "object" ? err.body?.message || "Unkown error" : err - }`; + this._error = `Failed to get supervisor logs, ${extractApiErrorMessage( + err + )}`; } } From faee2c3e1b16aa758a802b43d39bcb7032bcf722 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Sep 2020 15:18:01 +0200 Subject: [PATCH 006/146] Fix gauge editor (#6783) --- .../config-elements/hui-gauge-card-editor.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts index c58d65aa6b..3f0d545d23 100644 --- a/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-gauge-card-editor.ts @@ -4,14 +4,16 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, number, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-switch"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import "../../../../components/ha-formfield"; +import "../../../../components/ha-switch"; import { HomeAssistant } from "../../../../types"; import { GaugeCardConfig, SeverityConfig } from "../../cards/types"; import "../../components/hui-entity-editor"; @@ -19,8 +21,6 @@ import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; import { EditorTarget, EntitiesEditorEvent } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; -import { assert, object, string, optional, number } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -222,12 +222,16 @@ export class HuiGaugeCardEditor extends LitElement } if ((ev.target as EditorTarget).checked) { - this._config.severity = { - green: 0, - yellow: 0, - red: 0, + this._config = { + ...this._config, + severity: { + green: 0, + yellow: 0, + red: 0, + }, }; } else { + this._config = { ...this._config }; delete this._config.severity; } fireEvent(this, "config-changed", { config: this._config }); From d1a9cb488a3642caf61acacb698ae10131c075d7 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Sep 2020 15:54:42 +0200 Subject: [PATCH 007/146] Add person badge (#6785) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add person badge * Update src/components/user/ha-person-badge.ts Co-authored-by: Joakim Sørensen * Revert screwup by @ludeeus Co-authored-by: Joakim Sørensen Co-authored-by: Ludeeus --- src/components/user/ha-person-badge.ts | 71 ++++++++++++++++++++ src/components/user/ha-user-badge.ts | 50 ++++++-------- src/panels/config/person/ha-config-person.ts | 36 +++------- 3 files changed, 101 insertions(+), 56 deletions(-) create mode 100644 src/components/user/ha-person-badge.ts diff --git a/src/components/user/ha-person-badge.ts b/src/components/user/ha-person-badge.ts new file mode 100644 index 0000000000..70c47d6023 --- /dev/null +++ b/src/components/user/ha-person-badge.ts @@ -0,0 +1,71 @@ +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; +import { styleMap } from "lit-html/directives/style-map"; +import { Person } from "../../data/person"; +import { computeInitials } from "./ha-user-badge"; + +@customElement("ha-person-badge") +class PersonBadge extends LitElement { + @property({ attribute: false }) public person?: Person; + + protected render(): TemplateResult { + if (!this.person) { + return html``; + } + + const picture = this.person.picture; + + if (picture) { + return html`
`; + } + const initials = computeInitials(this.person.name); + return html`
+ ${initials} +
`; + } + + static get styles(): CSSResult { + return css` + .picture { + width: 40px; + height: 40px; + background-size: cover; + border-radius: 50%; + } + .initials { + display: inline-block; + box-sizing: border-box; + width: 40px; + line-height: 40px; + border-radius: 50%; + text-align: center; + background-color: var(--light-primary-color); + text-decoration: none; + color: var(--text-light-primary-color, var(--primary-text-color)); + overflow: hidden; + } + .initials.long { + font-size: 80%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-person-badge": PersonBadge; + } +} diff --git a/src/components/user/ha-user-badge.ts b/src/components/user/ha-user-badge.ts index c7fd114ae4..09972237fc 100644 --- a/src/components/user/ha-user-badge.ts +++ b/src/components/user/ha-user-badge.ts @@ -8,15 +8,15 @@ import { property, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import { styleMap } from "lit-html/directives/style-map"; -import { toggleAttribute } from "../../common/dom/toggle_attribute"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { User } from "../../data/user"; import { CurrentUser, HomeAssistant } from "../../types"; -const computeInitials = (name: string) => { +export const computeInitials = (name: string) => { if (!name) { - return "user"; + return "?"; } return ( name @@ -31,7 +31,7 @@ const computeInitials = (name: string) => { }; @customElement("ha-user-badge") -class StateBadge extends LitElement { +class UserBadge extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public user?: User | CurrentUser; @@ -44,14 +44,6 @@ class StateBadge extends LitElement { super.updated(changedProps); if (changedProps.has("user")) { this._getPersonPicture(); - if (!this._personPicture) { - toggleAttribute( - this, - "long", - (this.hass.user ? computeInitials(this.hass.user.name) : "?").length > - 2 - ); - } return; } const oldHass = changedProps.get("hass"); @@ -67,6 +59,8 @@ class StateBadge extends LitElement { } else { this._getPersonPicture(); } + } else if (!this._personEntityId && oldHass) { + this._getPersonPicture(); } } @@ -74,22 +68,20 @@ class StateBadge extends LitElement { if (!this.hass || !this.user) { return html``; } - const user = this.user; const picture = this._personPicture; - return html` - ${picture - ? html`
` - : html`
2} - > - ${computeInitials(user?.name!)} -
`} - `; + if (picture) { + return html`
`; + } + const initials = computeInitials(this.user.name); + return html`
+ ${initials} +
`; } private _getPersonPicture() { @@ -100,7 +92,7 @@ class StateBadge extends LitElement { } for (const entity of Object.values(this.hass.states)) { if ( - entity.attributes.user_id === this.user!.id && + entity.attributes.user_id === this.user.id && computeStateDomain(entity) === "person" ) { this._personEntityId = entity.entity_id; @@ -130,7 +122,7 @@ class StateBadge extends LitElement { color: var(--text-light-primary-color, var(--primary-text-color)); overflow: hidden; } - :host([long]) .initials { + .initials.long { font-size: 80%; } `; @@ -139,6 +131,6 @@ class StateBadge extends LitElement { declare global { interface HTMLElementTagNameMap { - "ha-user-badge": StateBadge; + "ha-user-badge": UserBadge; } } diff --git a/src/panels/config/person/ha-config-person.ts b/src/panels/config/person/ha-config-person.ts index 24f794b4e8..3d1de87bd0 100644 --- a/src/panels/config/person/ha-config-person.ts +++ b/src/panels/config/person/ha-config-person.ts @@ -1,4 +1,3 @@ -import "@material/mwc-fab"; import { mdiPlus } from "@mdi/js"; import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; @@ -11,11 +10,10 @@ import { property, TemplateResult, } from "lit-element"; -import { styleMap } from "lit-html/directives/style-map"; import { compare } from "../../../common/string/compare"; import "../../../components/ha-card"; import "../../../components/ha-svg-icon"; -import "../../../components/user/ha-user-badge"; +import "../../../components/user/ha-person-badge"; import { createPerson, deletePerson, @@ -87,15 +85,10 @@ class HaConfigPerson extends LitElement { ${this._storageItems.map((entry) => { return html` - ${entry.picture - ? html`
` - : ""} + ${entry.name} @@ -123,15 +116,10 @@ class HaConfigPerson extends LitElement { ${this._configItems.map((entry) => { return html` - ${entry.picture - ? html`
` - : ""} + ${entry.name} @@ -248,12 +236,6 @@ class HaConfigPerson extends LitElement { margin: 16px auto; overflow: hidden; } - .picture { - width: 40px; - height: 40px; - background-size: cover; - border-radius: 50%; - } .empty { text-align: center; padding: 8px; From 9c4fdaa4f3cb2d7ce9df6c5496b0819bda90b94f Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Fri, 4 Sep 2020 16:21:15 +0200 Subject: [PATCH 008/146] Minor EN text improvements / fixes (#6788) --- src/translations/en.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 3aeafd0ec1..0e2df4858a 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -445,7 +445,7 @@ "delete": "Delete", "confirm_delete": "Are you sure you want to delete this entry?", "update": "Update", - "note": "Note: this might not work yet with all integrations." + "note": "Note: This might not work yet with all integrations." } }, "helper_settings": { @@ -1671,7 +1671,7 @@ "users": { "caption": "Users", "description": "Manage users", - "users_privileges_note": "The users group is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators.", + "users_privileges_note": "The user group feature is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators.", "picker": { "headers": { "name": "Name", @@ -1849,7 +1849,7 @@ "clusters": { "header": "Clusters", "help_cluster_dropdown": "Select a cluster to view attributes and commands.", - "introduction": "Clusters are the building blocks for Zigbee functionality. They seperate functionality into logical units. There are client and server types and that are comprised of attributes and commands." + "introduction": "Clusters are the building blocks for Zigbee functionality. They separate functionality into logical units. There are client and server types and that are comprised of attributes and commands." }, "cluster_attributes": { "header": "Cluster Attributes", @@ -1940,10 +1940,10 @@ "node_group_associations": "Node group associations", "group": "Group", "node_to_control": "Node to control", - "nodes_in_group": "Other Nodes in this group:", + "nodes_in_group": "Other nodes in this group:", "max_associations": "Max Associations:", - "add_to_group": "Add To Group", - "remove_from_group": "Remove From Group", + "add_to_group": "Add to Group", + "remove_from_group": "Remove from Group", "remove_broadcast": "Remove Broadcast" }, "ozw_log": { From 793b9f238ceaf952f81e7bf59406d6ce2f30c303 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Sep 2020 18:30:04 +0200 Subject: [PATCH 009/146] Tweak card create dialog a bit (#6787) --- src/components/ha-header-bar.ts | 2 +- src/dialogs/more-info/ha-more-info-dialog.ts | 88 ++++++++++--------- .../more-info/ha-more-info-tab-history.ts | 16 ++-- src/panels/logbook/ha-logbook.ts | 31 +++++-- .../editor/card-editor/hui-card-picker.ts | 34 +++---- .../card-editor/hui-dialog-create-card.ts | 61 +++++++------ .../card-editor/show-edit-card-dialog.ts | 1 - src/translations/en.json | 3 +- 8 files changed, 136 insertions(+), 100 deletions(-) diff --git a/src/components/ha-header-bar.ts b/src/components/ha-header-bar.ts index 946c1979de..0677e96673 100644 --- a/src/components/ha-header-bar.ts +++ b/src/components/ha-header-bar.ts @@ -1,6 +1,6 @@ -import { customElement, LitElement, html, unsafeCSS, css } from "lit-element"; // @ts-ignore import topAppBarStyles from "@material/top-app-bar/dist/mdc.top-app-bar.min.css"; +import { css, customElement, html, LitElement, unsafeCSS } from "lit-element"; @customElement("ha-header-bar") export class HaHeaderBar extends LitElement { diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 56d429836d..086040ddef 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -11,6 +11,7 @@ import { LitElement, property, } from "lit-element"; +import { cache } from "lit-html/directives/cache"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const"; import { fireEvent } from "../../common/dom/fire_event"; @@ -147,47 +148,52 @@ export class MoreInfoDialog extends LitElement { : ""}
- ${this._currTabIndex === 0 - ? html` - ${DOMAINS_NO_INFO.includes(domain) - ? "" - : html` - - `} - - ${stateObj.attributes.restored - ? html` -

- ${this.hass.localize( - "ui.dialogs.more_info_control.restored.not_provided" - )} -

-

- ${this.hass.localize( - "ui.dialogs.more_info_control.restored.remove_intro" - )} -

- - ${this.hass.localize( - "ui.dialogs.more_info_control.restored.remove_action" - )} - - ` - : ""} - ` - : html` - - `} + ${cache( + this._currTabIndex === 0 + ? html` + ${DOMAINS_NO_INFO.includes(domain) + ? "" + : html` + + `} + + ${stateObj.attributes.restored + ? html` +

+ ${this.hass.localize( + "ui.dialogs.more_info_control.restored.not_provided" + )} +

+

+ ${this.hass.localize( + "ui.dialogs.more_info_control.restored.remove_intro" + )} +

+ + ${this.hass.localize( + "ui.dialogs.more_info_control.restored.remove_action" + )} + + ` + : ""} + ` + : html` + + ` + )}
`; diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts index 036d66bdb6..18624a11ef 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -8,7 +8,7 @@ import { PropertyValues, TemplateResult, } from "lit-element"; -import { classMap } from "lit-html/directives/class-map"; +import { styleMap } from "lit-html/directives/style-map"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import "../../components/ha-circular-progress"; import "../../components/state-history-charts"; @@ -57,19 +57,21 @@ export class MoreInfoTabHistoryDialog extends LitElement { alt=${this.hass.localize("ui.common.loading")} > ` - : html` + : this._entries.length + ? html` - `} + ` + : ""} `; } @@ -144,8 +146,8 @@ export class MoreInfoTabHistoryDialog extends LitElement { margin-bottom: 16px; } - ha-logbook.has-entries { - height: 360px; + ha-logbook { + max-height: 360px; } ha-circular-progress { diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 173dc59231..11880d5887 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -103,7 +103,7 @@ class HaLogbook extends LitElement { const item_username = item.context_user_id && this.userIdToName[item.context_user_id]; return html` -
+
${index === 0 || (item?.when && previous?.when && @@ -195,19 +195,39 @@ class HaLogbook extends LitElement { direction: ltr; } + .entry-container { + width: 100%; + } + .entry { display: flex; + width: 100%; line-height: 2em; - padding-bottom: 8px; + padding: 8px 16px; + box-sizing: border-box; + border-top: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); } .time { + display: flex; + justify-content: center; + flex-direction: column; width: 65px; flex-shrink: 0; font-size: 12px; color: var(--secondary-text-color); } + .date { + margin: 8px 0; + padding: 0 16px; + } + + .narrow .date { + padding: 0 8px; + } + .rtl .date { direction: rtl; } @@ -239,10 +259,6 @@ class HaLogbook extends LitElement { color: var(--primary-color); } - .container { - padding: 0 16px; - } - .uni-virtualizer-host { display: block; position: relative; @@ -256,8 +272,9 @@ class HaLogbook extends LitElement { } .narrow .entry { - flex-direction: column-reverse; + flex-direction: column; line-height: 1.5; + padding: 8px; } .narrow .icon-message ha-icon { diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index 54e108a46f..f68b99e1f5 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -1,48 +1,45 @@ import "@material/mwc-tab-bar/mwc-tab-bar"; import "@material/mwc-tab/mwc-tab"; import Fuse from "fuse.js"; -import memoizeOne from "memoize-one"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { styleMap } from "lit-html/directives/style-map"; import { until } from "lit-html/directives/until"; - +import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../common/search/search-input"; +import "../../../../components/ha-circular-progress"; import { UNAVAILABLE_STATES } from "../../../../data/entity"; +import type { + LovelaceCardConfig, + LovelaceConfig, +} from "../../../../data/lovelace"; import { CustomCardEntry, customCards, CUSTOM_TYPE_PREFIX, getCustomCardEntry, } from "../../../../data/lovelace_custom_cards"; +import type { HomeAssistant } from "../../../../types"; import { - computeUsedEntities, calcUnusedEntities, + computeUsedEntities, } from "../../common/compute-unused-entities"; import { tryCreateCardElement } from "../../create-element/create-card-element"; +import type { LovelaceCard } from "../../types"; import { getCardStubConfig } from "../get-card-stub-config"; import { coreCards } from "../lovelace-cards"; - -import type { CardPickTarget, Card } from "../types"; -import type { LovelaceCard } from "../../types"; -import type { HomeAssistant } from "../../../../types"; -import type { - LovelaceCardConfig, - LovelaceConfig, -} from "../../../../data/lovelace"; - -import "../../../../components/ha-circular-progress"; -import "../../../../common/search/search-input"; +import type { Card, CardPickTarget } from "../types"; interface CardElement { card: Card; @@ -107,7 +104,7 @@ export class HuiCardPicker extends LitElement { no-label-float @value-changed=${this._handleSearchChange} .label=${this.hass.localize( - "ui.panel.lovelace.editor.card.generic.search" + "ui.panel.lovelace.editor.edit_card.search_cards" )} >
{ + public async showDialog(params: CreateCardDialogParams): Promise { this._params = params; const [view] = params.path; this._viewConfig = params.lovelaceConfig.views[view]; @@ -76,10 +76,11 @@ export class HuiCreateDialogCard extends LitElement implements HassDialog { @keydown=${this._ignoreKeydown} @closed=${this._cancel} .heading=${true} + class=${classMap({ table: this._currTabIndex === 1 })} >
-
+ ${this._viewConfig.title ? this.hass!.localize( "ui.panel.lovelace.editor.edit_card.pick_card_view_title", @@ -89,7 +90,7 @@ export class HuiCreateDialogCard extends LitElement implements HassDialog { : this.hass!.localize( "ui.panel.lovelace.editor.edit_card.pick_card" )} -
+
- ${this._currTabIndex === 0 - ? html` - - ` - : html` -
- -
- `} + @config-changed=${this._handleCardPicked} + > + ` + : html` +
+ +
+ ` + )}
@@ -167,6 +170,11 @@ export class HuiCreateDialogCard extends LitElement implements HassDialog { ha-dialog { --mdc-dialog-max-width: 845px; + --dialog-content-padding: 2px 24px 20px 24px; + } + + ha-dialog.table { + --dialog-content-padding: 0; } ha-header-bar { @@ -190,7 +198,8 @@ export class HuiCreateDialogCard extends LitElement implements HassDialog { } mwc-tab-bar { - padding-top: 8px; + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); } .entity-picker-container { diff --git a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts index 18b0d820e3..46525f1c43 100644 --- a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts @@ -5,7 +5,6 @@ export interface EditCardDialogParams { lovelaceConfig: LovelaceConfig; saveConfig: (config: LovelaceConfig) => void; path: [number] | [number, number]; - entities?: string[]; // We can pass entity id's that will be added to the config when a card is picked cardConfig?: LovelaceCardConfig; } diff --git a/src/translations/en.json b/src/translations/en.json index 0e2df4858a..efec3b01be 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2137,7 +2137,8 @@ "delete": "Delete Card", "duplicate": "Duplicate Card", "move": "Move to View", - "options": "More options" + "options": "More options", + "search_cards": "Search cards" }, "move_card": { "header": "Choose a view to move the card to" From aa5e20df05304a161d0be8d2b8688a88c7628171 Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Fri, 4 Sep 2020 14:55:40 -0400 Subject: [PATCH 010/146] Add basic nodes list & node metadata to OZW config panel (#6719) Co-authored-by: Bram Kragten --- src/data/ozw.ts | 11 + .../ozw/ozw-config-dashboard.ts | 55 ++--- .../ozw/ozw-config-router.ts | 37 +-- ...ig-network.ts => ozw-network-dashboard.ts} | 121 +++++---- .../ozw/ozw-network-nodes.ts | 144 +++++++++++ .../ozw/ozw-network-router.ts | 83 +++++++ .../ozw/ozw-node-dashboard.ts | 231 ++++++++++++++++++ .../integration-panels/ozw/ozw-node-router.ts | 66 +++++ src/translations/en.json | 11 + 9 files changed, 652 insertions(+), 107 deletions(-) rename src/panels/config/integrations/integration-panels/ozw/{ozw-config-network.ts => ozw-network-dashboard.ts} (83%) create mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts create mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts create mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts create mode 100644 src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts diff --git a/src/data/ozw.ts b/src/data/ozw.ts index b24692c01f..80ad9882b7 100644 --- a/src/data/ozw.ts +++ b/src/data/ozw.ts @@ -14,6 +14,8 @@ export interface OZWDevice { is_zwave_plus: boolean; ozw_instance: number; event: string; + node_manufacturer_name: string; + node_product_name: string; } export interface OZWDeviceMetaDataResponse { @@ -147,6 +149,15 @@ export const fetchOZWNetworkStatistics = ( ozw_instance: ozw_instance, }); +export const fetchOZWNodes = ( + hass: HomeAssistant, + ozw_instance: number +): Promise => + hass.callWS({ + type: "ozw/get_nodes", + ozw_instance: ozw_instance, + }); + export const fetchOZWNodeStatus = ( hass: HomeAssistant, ozw_instance: number, diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts index f23d7a7114..478ede0f8e 100644 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-config-dashboard.ts @@ -1,6 +1,8 @@ +import "@material/mwc-button/mwc-button"; +import "@material/mwc-fab"; +import { mdiCheckCircle, mdiCircle, mdiCloseCircle, mdiZWave } from "@mdi/js"; import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; -import "@material/mwc-fab"; import { css, CSSResultArray, @@ -14,20 +16,18 @@ import { import { navigate } from "../../../../../common/navigate"; import "../../../../../components/ha-card"; import "../../../../../components/ha-icon-next"; +import { + fetchOZWInstances, + networkOfflineStatuses, + networkOnlineStatuses, + networkStartingStatuses, + OZWInstance, +} from "../../../../../data/ozw"; +import "../../../../../layouts/hass-tabs-subpage"; +import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant, Route } from "../../../../../types"; import "../../../ha-config-section"; -import { mdiCircle, mdiCheckCircle, mdiCloseCircle, mdiZWave } from "@mdi/js"; -import "../../../../../layouts/hass-tabs-subpage"; -import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; -import "@material/mwc-button/mwc-button"; -import { - OZWInstance, - fetchOZWInstances, - networkOnlineStatuses, - networkOfflineStatuses, - networkStartingStatuses, -} from "../../../../../data/ozw"; export const ozwTabs: PageNavigation[] = []; @@ -45,22 +45,8 @@ class OZWConfigDashboard extends LitElement { @internalProperty() private _instances: OZWInstance[] = []; - public connectedCallback(): void { - super.connectedCallback(); - if (this.hass) { - this._fetchData(); - } - } - - private async _fetchData() { - this._instances = await fetchOZWInstances(this.hass!); - if (this._instances.length === 1) { - navigate( - this, - `/config/ozw/network/${this._instances[0].ozw_instance}`, - true - ); - } + protected firstUpdated() { + this._fetchData(); } protected render(): TemplateResult { @@ -142,12 +128,23 @@ class OZWConfigDashboard extends LitElement { `; })} ` - : ``} + : ""} `; } + private async _fetchData() { + this._instances = await fetchOZWInstances(this.hass!); + if (this._instances.length === 1) { + navigate( + this, + `/config/ozw/network/${this._instances[0].ozw_instance}`, + true + ); + } + } + static get styles(): CSSResultArray { return [ haStyle, diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts index 28402cbe4c..804e2073af 100644 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-config-router.ts @@ -1,10 +1,23 @@ import { customElement, property } from "lit-element"; +import memoizeOne from "memoize-one"; import { HassRouterPage, RouterOptions, } from "../../../../../layouts/hass-router-page"; -import { HomeAssistant } from "../../../../../types"; -import { navigate } from "../../../../../common/navigate"; +import { HomeAssistant, Route } from "../../../../../types"; + +export const computeTail = memoizeOne((route: Route) => { + const dividerPos = route.path.indexOf("/", 1); + return dividerPos === -1 + ? { + prefix: route.prefix + route.path, + path: "", + } + : { + prefix: route.prefix + route.path.substr(0, dividerPos), + path: route.path.substr(dividerPos), + }; +}); @customElement("ozw-config-router") class OZWConfigRouter extends HassRouterPage { @@ -30,10 +43,10 @@ class OZWConfigRouter extends HassRouterPage { ), }, network: { - tag: "ozw-config-network", + tag: "ozw-network-router", load: () => import( - /* webpackChunkName: "ozw-config-network" */ "./ozw-config-network" + /* webpackChunkName: "ozw-network-router" */ "./ozw-network-router" ), }, }, @@ -46,19 +59,9 @@ class OZWConfigRouter extends HassRouterPage { el.narrow = this.narrow; el.configEntryId = this._configEntry; if (this._currentPage === "network") { - el.ozw_instance = this.routeTail.path.substr(1); - } - - const searchParams = new URLSearchParams(window.location.search); - if (this._configEntry && !searchParams.has("config_entry")) { - searchParams.append("config_entry", this._configEntry); - navigate( - this, - `${this.routeTail.prefix}${ - this.routeTail.path - }?${searchParams.toString()}`, - true - ); + const path = this.routeTail.path.split("/"); + el.ozwInstance = path[1]; + el.route = computeTail(this.routeTail); } } } diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-config-network.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts similarity index 83% rename from src/panels/config/integrations/integration-panels/ozw/ozw-config-network.ts rename to src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts index 6d67ed779e..98ae8a28a6 100644 --- a/src/panels/config/integrations/integration-panels/ozw/ozw-config-network.ts +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-network-dashboard.ts @@ -1,4 +1,6 @@ +import "@material/mwc-button/mwc-button"; import "@material/mwc-fab"; +import { mdiCheckCircle, mdiCircle, mdiCloseCircle } from "@mdi/js"; import { css, CSSResultArray, @@ -9,31 +11,28 @@ import { property, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import { navigate } from "../../../../../common/navigate"; +import "../../../../../components/buttons/ha-call-service-button"; import "../../../../../components/ha-card"; import "../../../../../components/ha-icon-next"; -import "../../../../../components/buttons/ha-call-service-button"; +import { + fetchOZWNetworkStatistics, + fetchOZWNetworkStatus, + networkOfflineStatuses, + networkOnlineStatuses, + networkStartingStatuses, + OZWInstance, + OZWNetworkStatistics, +} from "../../../../../data/ozw"; +import "../../../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant, Route } from "../../../../../types"; import "../../../ha-config-section"; -import { mdiCircle, mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; -import "../../../../../layouts/hass-tabs-subpage"; -import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; -import "@material/mwc-button/mwc-button"; -import { - OZWInstance, - fetchOZWNetworkStatus, - fetchOZWNetworkStatistics, - networkOnlineStatuses, - networkOfflineStatuses, - networkStartingStatuses, - OZWNetworkStatistics, -} from "../../../../../data/ozw"; +import { ozwNetworkTabs } from "./ozw-network-router"; -export const ozwTabs: PageNavigation[] = []; - -@customElement("ozw-config-network") -class OZWConfigNetwork extends LitElement { +@customElement("ozw-network-dashboard") +class OZWNetworkDashboard extends LitElement { @property({ type: Object }) public hass!: HomeAssistant; @property({ type: Object }) public route!: Route; @@ -44,7 +43,7 @@ class OZWConfigNetwork extends LitElement { @property() public configEntryId?: string; - @property() public ozw_instance = 0; + @property() public ozwInstance?: number; @internalProperty() private _network?: OZWInstance; @@ -54,54 +53,21 @@ class OZWConfigNetwork extends LitElement { @internalProperty() private _icon = mdiCircle; - public connectedCallback(): void { - super.connectedCallback(); - if (this.ozw_instance <= 0) { + protected firstUpdated() { + if (!this.ozwInstance) { navigate(this, "/config/ozw/dashboard", true); - } - if (this.hass) { + } else if (this.hass) { this._fetchData(); } } - private async _fetchData() { - this._network = await fetchOZWNetworkStatus(this.hass!, this.ozw_instance); - this._statistics = await fetchOZWNetworkStatistics( - this.hass!, - this.ozw_instance - ); - if (networkOnlineStatuses.includes(this._network.Status)) { - this._status = "online"; - this._icon = mdiCheckCircle; - } - if (networkStartingStatuses.includes(this._network.Status)) { - this._status = "starting"; - } - if (networkOfflineStatuses.includes(this._network.Status)) { - this._status = "offline"; - this._icon = mdiCloseCircle; - } - } - - private _generateServiceButton(service: string) { - return html` - - ${this.hass!.localize("ui.panel.config.ozw.services." + service)} - - `; - } - protected render(): TemplateResult { return html`
@@ -118,20 +84,21 @@ class OZWConfigNetwork extends LitElement {
${this.hass.localize( "ui.panel.config.ozw.common.network" )} ${this.hass.localize( - "ui.panel.config.ozw.network_status." + this._status + `ui.panel.config.ozw.network_status.${this._status}` )}
${this.hass.localize( - "ui.panel.config.ozw.network_status.details." + - this._network.Status.toLowerCase() + `ui.panel.config.ozw.network_status.details.${this._network.Status.toLowerCase()}` )}
@@ -171,6 +138,38 @@ class OZWConfigNetwork extends LitElement { `; } + private async _fetchData() { + if (!this.ozwInstance) return; + this._network = await fetchOZWNetworkStatus(this.hass!, this.ozwInstance); + this._statistics = await fetchOZWNetworkStatistics( + this.hass!, + this.ozwInstance + ); + if (networkOnlineStatuses.includes(this._network!.Status)) { + this._status = "online"; + this._icon = mdiCheckCircle; + } + if (networkStartingStatuses.includes(this._network!.Status)) { + this._status = "starting"; + } + if (networkOfflineStatuses.includes(this._network!.Status)) { + this._status = "offline"; + this._icon = mdiCloseCircle; + } + } + + private _generateServiceButton(service: string) { + return html` + + ${this.hass!.localize(`ui.panel.config.ozw.services.${service}`)} + + `; + } + static get styles(): CSSResultArray { return [ haStyle, @@ -248,6 +247,6 @@ class OZWConfigNetwork extends LitElement { declare global { interface HTMLElementTagNameMap { - "ozw-config-network": OZWConfigNetwork; + "ozw-network-dashboard": OZWNetworkDashboard; } } diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts new file mode 100644 index 0000000000..11d6a3c2a3 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-network-nodes.ts @@ -0,0 +1,144 @@ +import "@material/mwc-button/mwc-button"; +import "@material/mwc-fab"; +import { mdiAlert, mdiCheck } from "@mdi/js"; +import { + CSSResult, + customElement, + html, + internalProperty, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import { navigate } from "../../../../../common/navigate"; +import "../../../../../components/buttons/ha-call-service-button"; +import { HASSDomEvent } from "../../../../../common/dom/fire_event"; +import { + DataTableColumnContainer, + RowClickedEvent, +} from "../../../../../components/data-table/ha-data-table"; +import "../../../../../components/ha-card"; +import "../../../../../components/ha-icon-next"; +import { fetchOZWNodes, OZWDevice } from "../../../../../data/ozw"; +import "../../../../../layouts/hass-tabs-subpage"; +import "../../../../../layouts/hass-tabs-subpage-data-table"; +import { haStyle } from "../../../../../resources/styles"; +import type { HomeAssistant, Route } from "../../../../../types"; +import "../../../ha-config-section"; +import { ozwNetworkTabs } from "./ozw-network-router"; + +export interface NodeRowData extends OZWDevice { + node?: NodeRowData; + id?: number; +} + +@customElement("ozw-network-nodes") +class OZWNetworkNodes extends LitElement { + @property({ type: Object }) public hass!: HomeAssistant; + + @property({ type: Object }) public route!: Route; + + @property({ type: Boolean }) public narrow!: boolean; + + @property({ type: Boolean }) public isWide!: boolean; + + @property() public configEntryId?: string; + + @property() public ozwInstance = 0; + + @internalProperty() private _nodes: OZWDevice[] = []; + + private _columns = memoizeOne( + (narrow: boolean): DataTableColumnContainer => { + return { + node_id: { + title: this.hass.localize("ui.panel.config.ozw.nodes_table.id"), + sortable: true, + type: "numeric", + width: "72px", + filterable: true, + direction: "asc", + }, + node_product_name: { + title: this.hass.localize("ui.panel.config.ozw.nodes_table.model"), + sortable: true, + width: narrow ? "75%" : "25%", + }, + node_manufacturer_name: { + title: this.hass.localize( + "ui.panel.config.ozw.nodes_table.manufacturer" + ), + sortable: true, + hidden: narrow, + width: "25%", + }, + node_query_stage: { + title: this.hass.localize( + "ui.panel.config.ozw.nodes_table.query_stage" + ), + sortable: true, + width: narrow ? "25%" : "15%", + }, + is_zwave_plus: { + title: this.hass.localize( + "ui.panel.config.ozw.nodes_table.zwave_plus" + ), + hidden: narrow, + template: (value: boolean) => + value ? html` ` : "", + }, + is_failed: { + title: this.hass.localize("ui.panel.config.ozw.nodes_table.failed"), + hidden: narrow, + template: (value: boolean) => + value ? html` ` : "", + }, + }; + } + ); + + protected firstUpdated() { + if (!this.ozwInstance) { + navigate(this, "/config/ozw/dashboard", true); + } else if (this.hass) { + this._fetchData(); + } + } + + protected render(): TemplateResult { + return html` + + + `; + } + + private async _fetchData() { + this._nodes = await fetchOZWNodes(this.hass!, this.ozwInstance!); + } + + private _handleRowClicked(ev: HASSDomEvent) { + const nodeId = ev.detail.id; + navigate(this, `/config/ozw/network/${this.ozwInstance}/node/${nodeId}`); + } + + static get styles(): CSSResult { + return haStyle; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ozw-network-nodes": OZWNetworkNodes; + } +} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts new file mode 100644 index 0000000000..f98660bed0 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-network-router.ts @@ -0,0 +1,83 @@ +import { customElement, property } from "lit-element"; +import { + HassRouterPage, + RouterOptions, +} from "../../../../../layouts/hass-router-page"; +import { HomeAssistant } from "../../../../../types"; +import { computeTail } from "./ozw-config-router"; +import { PageNavigation } from "../../../../../layouts/hass-tabs-subpage"; +import { mdiServerNetwork, mdiNetwork } from "@mdi/js"; + +export const ozwNetworkTabs = (instance: number): PageNavigation[] => { + return [ + { + translationKey: "ui.panel.config.ozw.navigation.network", + path: `/config/ozw/network/${instance}/dashboard`, + iconPath: mdiServerNetwork, + }, + { + translationKey: "ui.panel.config.ozw.navigation.nodes", + path: `/config/ozw/network/${instance}/nodes`, + iconPath: mdiNetwork, + }, + ]; +}; + +@customElement("ozw-network-router") +class OZWNetworkRouter extends HassRouterPage { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public isWide!: boolean; + + @property() public narrow!: boolean; + + @property() public ozwInstance!: number; + + private _configEntry = new URLSearchParams(window.location.search).get( + "config_entry" + ); + + protected routerOptions: RouterOptions = { + defaultPage: "dashboard", + showLoading: true, + routes: { + dashboard: { + tag: "ozw-network-dashboard", + load: () => + import( + /* webpackChunkName: "ozw-network-dashboard" */ "./ozw-network-dashboard" + ), + }, + nodes: { + tag: "ozw-network-nodes", + load: () => + import( + /* webpackChunkName: "ozw-network-nodes" */ "./ozw-network-nodes" + ), + }, + node: { + tag: "ozw-node-router", + load: () => + import(/* webpackChunkName: "ozw-node-router" */ "./ozw-node-router"), + }, + }, + }; + + protected updatePageEl(el): void { + el.route = computeTail(this.routeTail); + el.hass = this.hass; + el.isWide = this.isWide; + el.narrow = this.narrow; + el.configEntryId = this._configEntry; + el.ozwInstance = this.ozwInstance; + if (this._currentPage === "node") { + el.nodeId = this.routeTail.path.split("/")[1]; + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ozw-network-router": OZWNetworkRouter; + } +} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts new file mode 100644 index 0000000000..bd2fa6af3e --- /dev/null +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-node-dashboard.ts @@ -0,0 +1,231 @@ +import "@material/mwc-button/mwc-button"; +import "@material/mwc-fab"; +import { + css, + CSSResultArray, + customElement, + html, + LitElement, + internalProperty, + property, + TemplateResult, +} from "lit-element"; +import { navigate } from "../../../../../common/navigate"; +import "../../../../../components/buttons/ha-call-service-button"; +import "../../../../../components/ha-card"; +import "../../../../../components/ha-icon-next"; +import "../../../../../layouts/hass-tabs-subpage"; +import { haStyle } from "../../../../../resources/styles"; +import type { HomeAssistant, Route } from "../../../../../types"; +import "../../../ha-config-section"; +import { + fetchOZWNodeStatus, + fetchOZWNodeMetadata, + OZWDevice, + OZWDeviceMetaDataResponse, +} from "../../../../../data/ozw"; +import { ERR_NOT_FOUND } from "../../../../../data/websocket_api"; +import { showOZWRefreshNodeDialog } from "./show-dialog-ozw-refresh-node"; +import { ozwNetworkTabs } from "./ozw-network-router"; + +@customElement("ozw-node-dashboard") +class OZWNodeDashboard extends LitElement { + @property({ type: Object }) public hass!: HomeAssistant; + + @property({ type: Object }) public route!: Route; + + @property({ type: Boolean }) public narrow!: boolean; + + @property({ type: Boolean }) public isWide!: boolean; + + @property() public configEntryId?: string; + + @property() public ozwInstance?; + + @property() public nodeId?; + + @internalProperty() private _node?: OZWDevice; + + @internalProperty() private _metadata?: OZWDeviceMetaDataResponse; + + @internalProperty() private _not_found = false; + + protected firstUpdated() { + if (!this.ozwInstance) { + navigate(this, "/config/ozw/dashboard", true); + } else if (!this.nodeId) { + navigate(this, `/config/ozw/network/${this.ozwInstance}/nodes`, true); + } else if (this.hass) { + this._fetchData(); + } + } + + protected render(): TemplateResult { + if (this._not_found) { + return html` + + `; + } + + return html` + + +
+ Node Management +
+ +
+ View the status of a node and manage its configuration. +
+ ${this._node + ? html` + +
+ ${this._node.node_manufacturer_name} + ${this._node.node_product_name}
+ Node ID: ${this._node.node_id}
+ Query Stage: ${this._node.node_query_stage} + ${this._metadata?.metadata.ProductManualURL + ? html` +

Product Manual

+
` + : ``} +
+
+ + Refresh Node + +
+
+ + ${this._metadata + ? html` + +
+ ${this._metadata.metadata.Description} +
+
+ +
+ ${this._metadata.metadata.InclusionHelp} +
+
+ +
+ ${this._metadata.metadata.ExclusionHelp} +
+
+ +
+ ${this._metadata.metadata.ResetHelp} +
+
+ +
+ ${this._metadata.metadata.WakeupHelp} +
+
+ ` + : ``} + ` + : ``} +
+
+ `; + } + + private async _fetchData() { + if (!this.ozwInstance || !this.nodeId) { + return; + } + + try { + this._node = await fetchOZWNodeStatus( + this.hass!, + this.ozwInstance, + this.nodeId + ); + this._metadata = await fetchOZWNodeMetadata( + this.hass!, + this.ozwInstance, + this.nodeId + ); + } catch (err) { + if (err.code === ERR_NOT_FOUND) { + this._not_found = true; + return; + } + throw err; + } + } + + private async _refreshNodeClicked() { + showOZWRefreshNodeDialog(this, { + node_id: this.nodeId, + ozw_instance: this.ozwInstance, + }); + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + .secondary { + color: var(--secondary-text-color); + } + + .content { + margin-top: 24px; + } + + .sectionHeader { + position: relative; + padding-right: 40px; + } + + ha-card { + margin: 0 auto; + max-width: 600px; + } + + .card-actions.warning ha-call-service-button { + color: var(--error-color); + } + + .toggle-help-icon { + position: absolute; + top: -6px; + right: 0; + color: var(--primary-color); + } + + ha-service-description { + display: block; + color: grey; + padding: 0 8px 12px; + } + + [hidden] { + display: none; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ozw-node-dashboard": OZWNodeDashboard; + } +} diff --git a/src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts b/src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts new file mode 100644 index 0000000000..baf68ddc96 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/ozw/ozw-node-router.ts @@ -0,0 +1,66 @@ +import { customElement, property } from "lit-element"; +import { navigate } from "../../../../../common/navigate"; +import { + HassRouterPage, + RouterOptions, +} from "../../../../../layouts/hass-router-page"; +import { HomeAssistant } from "../../../../../types"; + +@customElement("ozw-node-router") +class OZWNodeRouter extends HassRouterPage { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public isWide!: boolean; + + @property() public narrow!: boolean; + + @property() public ozwInstance!: number; + + @property() public nodeId!: number; + + private _configEntry = new URLSearchParams(window.location.search).get( + "config_entry" + ); + + protected routerOptions: RouterOptions = { + defaultPage: "dashboard", + showLoading: true, + routes: { + dashboard: { + tag: "ozw-node-dashboard", + load: () => + import( + /* webpackChunkName: "ozw-node-dashboard" */ "./ozw-node-dashboard" + ), + }, + }, + }; + + protected updatePageEl(el): void { + el.route = this.routeTail; + el.hass = this.hass; + el.isWide = this.isWide; + el.narrow = this.narrow; + el.configEntryId = this._configEntry; + el.ozwInstance = this.ozwInstance; + el.nodeId = this.nodeId; + + const searchParams = new URLSearchParams(window.location.search); + if (this._configEntry && !searchParams.has("config_entry")) { + searchParams.append("config_entry", this._configEntry); + navigate( + this, + `${this.routeTail.prefix}${ + this.routeTail.path + }?${searchParams.toString()}`, + true + ); + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ozw-node-router": OZWNodeRouter; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index efec3b01be..9f0c211c75 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1804,6 +1804,17 @@ "introduction": "Manage network-wide functions.", "node_count": "{count} nodes" }, + "nodes_table": { + "id": "ID", + "manufacturer": "Manufacturer", + "model": "Model", + "query_stage": "Query Stage", + "zwave_plus": "Z-Wave Plus", + "failed": "Failed" + }, + "node": { + "not_found": "Node not found" + }, "services": { "add_node": "Add Node", "remove_node": "Remove Node" From 349a5f52b10691416962ac707deaf5cf3ce030af Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Fri, 4 Sep 2020 13:56:48 -0500 Subject: [PATCH 011/146] More Info History: Scrollbar Style (#6790) --- src/components/ha-sidebar.ts | 527 +++++++++++++++---------------- src/panels/logbook/ha-logbook.ts | 166 +++++----- src/resources/styles.ts | 19 ++ 3 files changed, 363 insertions(+), 349 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 4f86c9f202..9cfffb822c 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -43,6 +43,7 @@ import { getExternalConfig, } from "../external_app/external_config"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; +import { haStyleScrollbar } from "../resources/styles"; import type { HomeAssistant, PanelInfo } from "../types"; import "./ha-icon"; import "./ha-menu-button"; @@ -249,6 +250,7 @@ class HaSidebar extends LitElement {
* { - box-sizing: border-box; - } + .uni-virtualizer-host > * { + box-sizing: border-box; + } - .narrow .entry { - flex-direction: column; - line-height: 1.5; - padding: 8px; - } + .narrow .entry { + flex-direction: column; + line-height: 1.5; + padding: 8px; + } - .narrow .icon-message ha-icon { - margin-left: 0; - } - `; + .narrow .icon-message ha-icon { + margin-left: 0; + } + `, + ]; } } diff --git a/src/resources/styles.ts b/src/resources/styles.ts index 8054abe475..ef04c15548 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -320,3 +320,22 @@ export const haStyleDialog = css` color: var(--error-color); } `; + +export const haStyleScrollbar = css` + .ha-scrollbar::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; + } + + .ha-scrollbar::-webkit-scrollbar-thumb { + -webkit-border-radius: 4px; + border-radius: 4px; + background: var(--scrollbar-thumb-color); + } + + .ha-scrollbar { + overflow-y: auto; + scrollbar-color: var(--scrollbar-thumb-color) transparent; + scrollbar-width: thin; + } +`; From b065f002a4a12088587e9a515c239c4b40c6998c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Sep 2020 22:22:55 +0200 Subject: [PATCH 012/146] Allow local storage decorator to register as property (#6776) --- src/common/decorators/local-storage.ts | 113 ++++++++++++++++++++--- src/components/ha-form/ha-form-select.ts | 10 +- src/components/ha-sidebar.ts | 12 ++- 3 files changed, 114 insertions(+), 21 deletions(-) diff --git a/src/common/decorators/local-storage.ts b/src/common/decorators/local-storage.ts index 99cdebdf3d..d4034ae25f 100644 --- a/src/common/decorators/local-storage.ts +++ b/src/common/decorators/local-storage.ts @@ -1,7 +1,33 @@ +import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { PropertyDeclaration, UpdatingElement } from "lit-element"; import type { ClassElement } from "../../types"; +type Callback = (oldValue: any, newValue: any) => void; + class Storage { - private _storage: any = {}; + constructor() { + window.addEventListener("storage", (ev: StorageEvent) => { + if (ev.key && this.hasKey(ev.key)) { + this._storage[ev.key] = ev.newValue + ? JSON.parse(ev.newValue) + : ev.newValue; + if (this._listeners[ev.key]) { + this._listeners[ev.key].forEach((listener) => + listener( + ev.oldValue ? JSON.parse(ev.oldValue) : ev.oldValue, + this._storage[ev.key!] + ) + ); + } + } + }); + } + + private _storage: { [storageKey: string]: any } = {}; + + private _listeners: { + [storageKey: string]: Callback[]; + } = {}; public addFromStorage(storageKey: any): void { if (!this._storage[storageKey]) { @@ -12,6 +38,30 @@ class Storage { } } + public subscribeChanges( + storageKey: string, + callback: Callback + ): UnsubscribeFunc { + if (this._listeners[storageKey]) { + this._listeners[storageKey].push(callback); + } else { + this._listeners[storageKey] = [callback]; + } + return () => { + this.unsubscribeChanges(storageKey, callback); + }; + } + + public unsubscribeChanges(storageKey: string, callback: Callback) { + if (!(storageKey in this._listeners)) { + return; + } + const index = this._listeners[storageKey].indexOf(callback); + if (index !== -1) { + this._listeners[storageKey].splice(index, 1); + } + } + public hasKey(storageKey: string): any { return storageKey in this._storage; } @@ -32,30 +82,49 @@ class Storage { const storage = new Storage(); -export const LocalStorage = (key?: string) => { - return (element: ClassElement, propName: string) => { - const storageKey = key || propName; - const initVal = element.initializer ? element.initializer() : undefined; +export const LocalStorage = ( + storageKey?: string, + property?: boolean, + propertyOptions?: PropertyDeclaration +): any => { + return (clsElement: ClassElement) => { + const key = String(clsElement.key); + storageKey = storageKey || String(clsElement.key); + const initVal = clsElement.initializer + ? clsElement.initializer() + : undefined; storage.addFromStorage(storageKey); + const subscribe = (el: UpdatingElement): UnsubscribeFunc => + storage.subscribeChanges(storageKey!, (oldValue) => { + el.requestUpdate(clsElement.key, oldValue); + }); + const getValue = (): any => { - return storage.hasKey(storageKey) - ? storage.getValue(storageKey) + return storage.hasKey(storageKey!) + ? storage.getValue(storageKey!) : initVal; }; - const setValue = (val: any) => { - storage.setValue(storageKey, val); + const setValue = (el: UpdatingElement, value: any) => { + let oldValue: unknown | undefined; + if (property) { + oldValue = getValue(); + } + storage.setValue(storageKey!, value); + if (property) { + el.requestUpdate(clsElement.key, oldValue); + } }; return { kind: "method", - placement: "own", - key: element.key, + placement: "prototype", + key: clsElement.key, descriptor: { - set(value) { - setValue(value); + set(this: UpdatingElement, value: unknown) { + setValue(this, value); }, get() { return getValue(); @@ -63,6 +132,24 @@ export const LocalStorage = (key?: string) => { enumerable: true, configurable: true, }, + finisher(cls: typeof UpdatingElement) { + if (property) { + const connectedCallback = cls.prototype.connectedCallback; + const disconnectedCallback = cls.prototype.disconnectedCallback; + cls.prototype.connectedCallback = function () { + connectedCallback.call(this); + this[`__unbsubLocalStorage${key}`] = subscribe(this); + }; + cls.prototype.disconnectedCallback = function () { + disconnectedCallback.call(this); + this[`__unbsubLocalStorage${key}`](); + }; + cls.createProperty(clsElement.key, { + noAccessor: true, + ...propertyOptions, + }); + } + }, }; }; }; diff --git a/src/components/ha-form/ha-form-select.ts b/src/components/ha-form/ha-form-select.ts index 2f3b82984c..359a06c5a8 100644 --- a/src/components/ha-form/ha-form-select.ts +++ b/src/components/ha-form/ha-form-select.ts @@ -1,4 +1,3 @@ -import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import { @@ -12,6 +11,7 @@ import { TemplateResult, } from "lit-element"; import { fireEvent } from "../../common/dom/fire_event"; +import "../ha-paper-dropdown-menu"; import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./ha-form"; @customElement("ha-form-select") @@ -24,7 +24,7 @@ export class HaFormSelect extends LitElement implements HaFormElement { @property() public suffix!: string; - @query("paper-dropdown-menu") private _input?: HTMLElement; + @query("ha-paper-dropdown-menu") private _input?: HTMLElement; public focus() { if (this._input) { @@ -34,7 +34,7 @@ export class HaFormSelect extends LitElement implements HaFormElement { protected render(): TemplateResult { return html` - + - + `; } @@ -74,7 +74,7 @@ export class HaFormSelect extends LitElement implements HaFormElement { static get styles(): CSSResult { return css` - paper-dropdown-menu { + ha-paper-dropdown-menu { display: block; } `; diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 9cfffb822c..12b1c17d39 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -191,11 +191,15 @@ class HaSidebar extends LitElement { private _recentKeydownActiveUntil = 0; // @ts-ignore - @LocalStorage("sidebarPanelOrder") + @LocalStorage("sidebarPanelOrder", true, { + attribute: false, + }) private _panelOrder: string[] = []; // @ts-ignore - @LocalStorage("sidebarHiddenPanels") + @LocalStorage("sidebarHiddenPanels", true, { + attribute: false, + }) private _hiddenPanels: string[] = []; private _sortable?; @@ -400,7 +404,9 @@ class HaSidebar extends LitElement { changedProps.has("_externalConfig") || changedProps.has("_notifications") || changedProps.has("_editMode") || - changedProps.has("_renderEmptySortable") + changedProps.has("_renderEmptySortable") || + changedProps.has("_hiddenPanels") || + (changedProps.has("_panelOrder") && !this._editMode) ) { return true; } From e63a78bcdb556dcb1e64605ec4145ca7e956e12a Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Fri, 4 Sep 2020 16:01:20 -0500 Subject: [PATCH 013/146] Media Browser Panel (#6772) Co-authored-by: Bram Kragten --- src/components/ha-camera-stream.ts | 184 ++------------ src/components/ha-dialog.ts | 9 +- src/components/ha-hls-player.ts | 216 +++++++++++++++++ .../dialog-media-player-browse.ts | 29 +-- .../media-player/ha-media-player-browse.ts | 227 +++++++++--------- src/data/media-player.ts | 14 +- .../controls/more-info-media_player.ts | 4 +- src/layouts/partial-panel-resolver.ts | 18 +- .../lovelace/cards/hui-media-control-card.ts | 4 +- .../media-browser/ha-panel-media-browser.ts | 151 ++++++++++++ .../hui-dialog-select-media-player.ts | 93 +++++++ .../hui-dialog-web-browser-play-media.ts | 122 ++++++++++ .../media-browser/show-media-player-dialog.ts | 21 ++ .../show-select-media-source-dialog.ts | 21 ++ src/translations/en.json | 7 +- 15 files changed, 802 insertions(+), 318 deletions(-) create mode 100644 src/components/ha-hls-player.ts create mode 100644 src/panels/media-browser/ha-panel-media-browser.ts create mode 100644 src/panels/media-browser/hui-dialog-select-media-player.ts create mode 100644 src/panels/media-browser/hui-dialog-web-browser-play-media.ts create mode 100644 src/panels/media-browser/show-media-player-dialog.ts create mode 100644 src/panels/media-browser/show-select-media-source-dialog.ts diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index e4eda4b3d8..a8d34c8a18 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -3,56 +3,39 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; import { fireEvent } from "../common/dom/fire_event"; import { computeStateName } from "../common/entity/compute_state_name"; import { supportsFeature } from "../common/entity/supports-feature"; -import { nextRender } from "../common/util/render-status"; -import { getExternalConfig } from "../external_app/external_config"; import { CAMERA_SUPPORT_STREAM, computeMJPEGStreamUrl, fetchStreamUrl, } from "../data/camera"; import { CameraEntity, HomeAssistant } from "../types"; - -type HLSModule = typeof import("hls.js"); +import "./ha-hls-player"; @customElement("ha-camera-stream") class HaCameraStream extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; - @property() public stateObj?: CameraEntity; + @property({ attribute: false }) public stateObj?: CameraEntity; @property({ type: Boolean }) public showControls = false; - @internalProperty() private _attached = false; - // We keep track if we should force MJPEG with a string // that way it automatically resets if we change entity. - @internalProperty() private _forceMJPEG: string | undefined = undefined; + @internalProperty() private _forceMJPEG?: string; - private _hlsPolyfillInstance?: Hls; - - private _useExoPlayer = false; - - public connectedCallback() { - super.connectedCallback(); - this._attached = true; - } - - public disconnectedCallback() { - super.disconnectedCallback(); - this._attached = false; - } + @internalProperty() private _url?: string; protected render(): TemplateResult { - if (!this.stateObj || !this._attached) { + if (!this.stateObj || (!this._forceMJPEG && !this._url)) { return html``; } @@ -70,50 +53,22 @@ class HaCameraStream extends LitElement { /> ` : html` - + .hass=${this.hass} + .url=${this._url!} + > `} `; } - protected updated(changedProps: PropertyValues) { - super.updated(changedProps); - - const stateObjChanged = changedProps.has("stateObj"); - const attachedChanged = changedProps.has("_attached"); - - const oldState = changedProps.get("stateObj") as this["stateObj"]; - const oldEntityId = oldState ? oldState.entity_id : undefined; - const curEntityId = this.stateObj ? this.stateObj.entity_id : undefined; - - if ( - (!stateObjChanged && !attachedChanged) || - (stateObjChanged && oldEntityId === curEntityId) - ) { - return; - } - - // If we are no longer attached, destroy polyfill. - if (attachedChanged && !this._attached) { - this._destroyPolyfill(); - return; - } - - // Nothing to do if we are render MJPEG. - if (this._shouldRenderMJPEG) { - return; - } - - // Tear down existing polyfill, if available - this._destroyPolyfill(); - - if (curEntityId) { - this._startHls(); + protected updated(changedProps: PropertyValues): void { + if (changedProps.has("stateObj")) { + this._forceMJPEG = undefined; + this._getStreamUrl(); } } @@ -125,136 +80,35 @@ class HaCameraStream extends LitElement { ); } - private get _videoEl(): HTMLVideoElement { - return this.shadowRoot!.querySelector("video")!; - } - - private async _getUseExoPlayer(): Promise { - if (!this.hass!.auth.external) { - return false; - } - const externalConfig = await getExternalConfig(this.hass!.auth.external); - return externalConfig && externalConfig.hasExoPlayer; - } - - private async _startHls(): Promise { - // eslint-disable-next-line - let hls; - const videoEl = this._videoEl; - this._useExoPlayer = await this._getUseExoPlayer(); - if (!this._useExoPlayer) { - hls = ((await import(/* webpackChunkName: "hls.js" */ "hls.js")) as any) - .default as HLSModule; - let hlsSupported = hls.isSupported(); - - if (!hlsSupported) { - hlsSupported = - videoEl.canPlayType("application/vnd.apple.mpegurl") !== ""; - } - - if (!hlsSupported) { - this._forceMJPEG = this.stateObj!.entity_id; - return; - } - } - + private async _getStreamUrl(): Promise { try { const { url } = await fetchStreamUrl( this.hass!, this.stateObj!.entity_id ); - if (this._useExoPlayer) { - this._renderHLSExoPlayer(url); - } else if (hls.isSupported()) { - this._renderHLSPolyfill(videoEl, hls, url); - } else { - this._renderHLSNative(videoEl, url); - } - return; + this._url = url; } catch (err) { // Fails if we were unable to get a stream // eslint-disable-next-line console.error(err); + this._forceMJPEG = this.stateObj!.entity_id; } } - private async _renderHLSExoPlayer(url: string) { - window.addEventListener("resize", this._resizeExoPlayer); - this.updateComplete.then(() => nextRender()).then(this._resizeExoPlayer); - this._videoEl.style.visibility = "hidden"; - await this.hass!.auth.external!.sendMessage({ - type: "exoplayer/play_hls", - payload: new URL(url, window.location.href).toString(), - }); - } - - private _resizeExoPlayer = () => { - const rect = this._videoEl.getBoundingClientRect(); - this.hass!.auth.external!.fireMessage({ - type: "exoplayer/resize", - payload: { - left: rect.left, - top: rect.top, - right: rect.right, - bottom: rect.bottom, - }, - }); - }; - - private async _renderHLSNative(videoEl: HTMLVideoElement, url: string) { - videoEl.src = url; - await new Promise((resolve) => - videoEl.addEventListener("loadedmetadata", resolve) - ); - videoEl.play(); - } - - private async _renderHLSPolyfill( - videoEl: HTMLVideoElement, - // eslint-disable-next-line - Hls: HLSModule, - url: string - ) { - const hls = new Hls({ - liveBackBufferLength: 60, - fragLoadingTimeOut: 30000, - manifestLoadingTimeOut: 30000, - levelLoadingTimeOut: 30000, - }); - this._hlsPolyfillInstance = hls; - hls.attachMedia(videoEl); - hls.on(Hls.Events.MEDIA_ATTACHED, () => { - hls.loadSource(url); - }); - } - private _elementResized() { fireEvent(this, "iron-resize"); } - private _destroyPolyfill() { - if (this._hlsPolyfillInstance) { - this._hlsPolyfillInstance.destroy(); - this._hlsPolyfillInstance = undefined; - } - if (this._useExoPlayer) { - window.removeEventListener("resize", this._resizeExoPlayer); - this.hass!.auth.external!.fireMessage({ type: "exoplayer/stop" }); - } - } - static get styles(): CSSResult { return css` :host, - img, - video { + img { display: block; } - img, - video { + img { width: 100%; } `; diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index 04dcbb1155..4459efb5b3 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -10,7 +10,7 @@ import "./ha-icon-button"; const MwcDialog = customElements.get("mwc-dialog") as Constructor; export const createCloseHeading = (hass: HomeAssistant, title: string) => html` - ${title} + ${title} + `; + } + + protected updated(changedProps: PropertyValues) { + super.updated(changedProps); + + const attachedChanged = changedProps.has("_attached"); + const urlChanged = changedProps.has("url"); + + if (!urlChanged && !attachedChanged) { + return; + } + + // If we are no longer attached, destroy polyfill + if (attachedChanged && !this._attached) { + // Tear down existing polyfill, if available + this._destroyPolyfill(); + return; + } + + this._destroyPolyfill(); + this._startHls(); + } + + private async _getUseExoPlayer(): Promise { + if (!this.hass!.auth.external) { + return false; + } + const externalConfig = await getExternalConfig(this.hass!.auth.external); + return externalConfig && externalConfig.hasExoPlayer; + } + + private async _startHls(): Promise { + let hls: any; + const videoEl = this._videoEl; + this._useExoPlayer = await this._getUseExoPlayer(); + if (!this._useExoPlayer) { + hls = ((await import(/* webpackChunkName: "hls.js" */ "hls.js")) as any) + .default as HLSModule; + let hlsSupported = hls.isSupported(); + + if (!hlsSupported) { + hlsSupported = + videoEl.canPlayType("application/vnd.apple.mpegurl") !== ""; + } + + if (!hlsSupported) { + this._videoEl.innerHTML = this.hass.localize( + "ui.components.media-browser.video_not_supported" + ); + return; + } + } + + const url = this.url; + + if (this._useExoPlayer) { + this._renderHLSExoPlayer(url); + } else if (hls.isSupported()) { + this._renderHLSPolyfill(videoEl, hls, url); + } else { + this._renderHLSNative(videoEl, url); + } + } + + private async _renderHLSExoPlayer(url: string) { + window.addEventListener("resize", this._resizeExoPlayer); + this.updateComplete.then(() => nextRender()).then(this._resizeExoPlayer); + this._videoEl.style.visibility = "hidden"; + await this.hass!.auth.external!.sendMessage({ + type: "exoplayer/play_hls", + payload: new URL(url, window.location.href).toString(), + }); + } + + private _resizeExoPlayer = () => { + const rect = this._videoEl.getBoundingClientRect(); + this.hass!.auth.external!.fireMessage({ + type: "exoplayer/resize", + payload: { + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + }, + }); + }; + + private async _renderHLSPolyfill( + videoEl: HTMLVideoElement, + Hls: HLSModule, + url: string + ) { + const hls = new Hls({ + liveBackBufferLength: 60, + fragLoadingTimeOut: 30000, + manifestLoadingTimeOut: 30000, + levelLoadingTimeOut: 30000, + }); + this._hlsPolyfillInstance = hls; + hls.attachMedia(videoEl); + hls.on(Hls.Events.MEDIA_ATTACHED, () => { + hls.loadSource(url); + }); + } + + private async _renderHLSNative(videoEl: HTMLVideoElement, url: string) { + videoEl.src = url; + await new Promise((resolve) => + videoEl.addEventListener("loadedmetadata", resolve) + ); + videoEl.play(); + } + + private _elementResized() { + fireEvent(this, "iron-resize"); + } + + private _destroyPolyfill() { + if (this._hlsPolyfillInstance) { + this._hlsPolyfillInstance.destroy(); + this._hlsPolyfillInstance = undefined; + } + if (this._useExoPlayer) { + window.removeEventListener("resize", this._resizeExoPlayer); + this.hass!.auth.external!.fireMessage({ type: "exoplayer/stop" }); + } + } + + static get styles(): CSSResult { + return css` + :host, + video { + display: block; + } + + video { + width: 100%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-hls-player": HaHLSPlayer; + } +} diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts index 5f4d14649a..9f0c07436f 100644 --- a/src/components/media-player/dialog-media-player-browse.ts +++ b/src/components/media-player/dialog-media-player-browse.ts @@ -8,7 +8,7 @@ import { property, TemplateResult, } from "lit-element"; -import { HASSDomEvent } from "../../common/dom/fire_event"; +import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event"; import type { MediaPickedEvent, MediaPlayerBrowseAction, @@ -33,16 +33,17 @@ class DialogMediaPlayerBrowse extends LitElement { @internalProperty() private _params?: MediaPlayerBrowseDialogParams; - public async showDialog( - params: MediaPlayerBrowseDialogParams - ): Promise { + public showDialog(params: MediaPlayerBrowseDialogParams): void { this._params = params; this._entityId = this._params.entityId; this._mediaContentId = this._params.mediaContentId; this._mediaContentType = this._params.mediaContentType; this._action = this._params.action || "play"; + } - await this.updateComplete; + public closeDialog() { + this._params = undefined; + fireEvent(this, "dialog-closed", {dialog: this.localName}); } protected render(): TemplateResult { @@ -57,7 +58,7 @@ class DialogMediaPlayerBrowse extends LitElement { escapeKeyAction hideActions flexContent - @closed=${this._closeDialog} + @closed=${this.closeDialog} > `; } - private _closeDialog() { - this._params = undefined; - } - private _mediaPicked(ev: HASSDomEvent): void { this._params!.mediaPickedCallback(ev.detail); if (this._action !== "play") { - this._closeDialog(); + this.closeDialog(); } } @@ -93,14 +90,6 @@ class DialogMediaPlayerBrowse extends LitElement { --dialog-content-padding: 0; } - ha-header-bar { - --mdc-theme-on-primary: var(--primary-text-color); - --mdc-theme-primary: var(--mdc-theme-surface); - flex-shrink: 0; - border-bottom: 1px solid - var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); - } - @media (min-width: 800px) { ha-dialog { --mdc-dialog-max-width: 800px; diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index c485a96453..5b3d637812 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -22,7 +22,13 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; -import { browseMediaPlayer, MediaPickedEvent } from "../../data/media-player"; +import { + browseLocalMediaPlayer, + browseMediaPlayer, + BROWSER_SOURCE, + MediaPickedEvent, + MediaPlayerBrowseAction, +} from "../../data/media-player"; import type { MediaPlayerItem } from "../../data/media-player"; import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer"; import { haStyle } from "../../resources/styles"; @@ -50,11 +56,7 @@ export class HaMediaPlayerBrowse extends LitElement { @property() public mediaContentType?: string; - @property() public action: "pick" | "play" = "play"; - - @property({ type: Boolean }) public hideBack = false; - - @property({ type: Boolean }) public hideTitle = false; + @property() public action: MediaPlayerBrowseAction = "play"; @property({ type: Boolean }) public dialog = false; @@ -88,52 +90,53 @@ export class HaMediaPlayerBrowse extends LitElement { } protected render(): TemplateResult { - if (!this._mediaPlayerItems.length) { - return html``; - } - if (this._loading) { return html``; } - const mostRecentItem = this._mediaPlayerItems[ + if (!this._mediaPlayerItems.length) { + return html``; + } + + const currentItem = this._mediaPlayerItems[ this._mediaPlayerItems.length - 1 ]; - const previousItem = + + const previousItem: MediaPlayerItem | undefined = this._mediaPlayerItems.length > 1 ? this._mediaPlayerItems[this._mediaPlayerItems.length - 2] : undefined; const hasExpandableChildren: | MediaPlayerItem - | undefined = this._hasExpandableChildren(mostRecentItem.children); + | undefined = this._hasExpandableChildren(currentItem.children); - const showImages = mostRecentItem.children?.some( - (child) => child.thumbnail && child.thumbnail !== mostRecentItem.thumbnail + const showImages: boolean | undefined = currentItem.children?.some( + (child) => child.thumbnail && child.thumbnail !== currentItem.thumbnail ); const mediaType = this.hass.localize( - `ui.components.media-browser.content-type.${mostRecentItem.media_content_type}` + `ui.components.media-browser.content-type.${currentItem.media_content_type}` ); return html`
- ${mostRecentItem.thumbnail + ${currentItem.thumbnail ? html`
- ${this._narrow && mostRecentItem?.can_play + ${this._narrow && currentItem?.can_play ? html` - ${this.hideTitle && (this._narrow || !mostRecentItem.thumbnail) - ? "" - : html``} - ${mostRecentItem?.can_play && - (!mostRecentItem.thumbnail || !this._narrow) + + ${currentItem.can_play && (!currentItem.thumbnail || !this._narrow) ? html` - + ` : ""}
- ${mostRecentItem.children?.length + ${currentItem.children?.length ? hasExpandableChildren ? html`
- ${mostRecentItem.children?.length - ? html` - ${mostRecentItem.children.map( - (child) => html` -
-
- html` +
+
+ + ${child.can_expand && !child.thumbnail + ? html` + + ` + : ""} + + ${child.can_play + ? html` + - ${child.can_expand && !child.thumbnail - ? html` - - ` - : ""} - - ${child.can_play - ? html` - - - - ` - : ""} -
-
${child.title}
-
- ${this.hass.localize( - `ui.components.media-browser.content-type.${child.media_content_type}` - )} -
-
- ` - )} - ` - : ""} + + + ` + : ""} +
+
${child.title}
+
+ ${this.hass.localize( + `ui.components.media-browser.content-type.${child.media_content_type}` + )} +
+
+ ` + )}
` : html` - ${mostRecentItem.children.map( + ${currentItem.children.map( (child) => html` { @@ -383,12 +373,15 @@ export class HaMediaPlayerBrowse extends LitElement { mediaContentId?: string, mediaContentType?: string ): Promise { - const itemData = await browseMediaPlayer( - this.hass, - this.entityId, - !mediaContentId ? undefined : mediaContentId, - mediaContentType - ); + const itemData = + this.entityId !== BROWSER_SOURCE + ? await browseMediaPlayer( + this.hass, + this.entityId, + mediaContentId, + mediaContentType + ) + : await browseLocalMediaPlayer(this.hass, mediaContentId); return itemData; } @@ -485,12 +478,6 @@ export class HaMediaPlayerBrowse extends LitElement { display: block; } - .breadcrumb-overflow { - display: flex; - flex-grow: 1; - justify-content: space-between; - } - .breadcrumb { display: flex; flex-direction: column; @@ -716,6 +703,10 @@ export class HaMediaPlayerBrowse extends LitElement { -webkit-line-clamp: 1; } + :host(:not([narrow])[scroll]) .header-info { + height: 75px; + } + :host([scroll]) .header-info mwc-button, .no-img .header-info mwc-button { padding-right: 4px; diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 0a3d289259..af11824211 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -20,9 +20,10 @@ export const CONTRAST_RATIO = 4.5; export type MediaPlayerBrowseAction = "pick" | "play"; +export const BROWSER_SOURCE = "browser"; + export interface MediaPickedEvent { - media_content_id: string; - media_content_type: string; + item: MediaPlayerItem; } export interface MediaPlayerThumbnail { @@ -58,6 +59,15 @@ export const browseMediaPlayer = ( media_content_type: mediaContentType, }); +export const browseLocalMediaPlayer = ( + hass: HomeAssistant, + mediaContentId?: string +): Promise => + hass.callWS({ + type: "media_source/browse_media", + media_content_id: mediaContentId, + }); + export const getCurrentProgress = (stateObj: HassEntity): number => { let progress = stateObj.attributes.media_position; diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts index 0e7a445c0b..aefc6bf92f 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.ts +++ b/src/dialogs/more-info/controls/more-info-media_player.ts @@ -409,8 +409,8 @@ class MoreInfoMediaPlayer extends LitElement { entityId: this.stateObj!.entity_id, mediaPickedCallback: (pickedMedia: MediaPickedEvent) => this._playMedia( - pickedMedia.media_content_id, - pickedMedia.media_content_type + pickedMedia.item.media_content_id, + pickedMedia.item.media_content_type ), }); } diff --git a/src/layouts/partial-panel-resolver.ts b/src/layouts/partial-panel-resolver.ts index 280a32eb16..ad04160caa 100644 --- a/src/layouts/partial-panel-resolver.ts +++ b/src/layouts/partial-panel-resolver.ts @@ -1,6 +1,13 @@ import { PolymerElement } from "@polymer/polymer"; +import { + STATE_NOT_RUNNING, + STATE_RUNNING, + STATE_STARTING, +} from "home-assistant-js-websocket"; import { customElement, property, PropertyValues } from "lit-element"; +import { deepActiveElement } from "../common/dom/deep-active-element"; import { deepEqual } from "../common/util/deep-equal"; +import { CustomPanelInfo } from "../data/panel_custom"; import { HomeAssistant, Panels } from "../types"; import { removeInitSkeleton } from "../util/init-skeleton"; import { @@ -8,13 +15,6 @@ import { RouteOptions, RouterOptions, } from "./hass-router-page"; -import { - STATE_STARTING, - STATE_NOT_RUNNING, - STATE_RUNNING, -} from "home-assistant-js-websocket"; -import { CustomPanelInfo } from "../data/panel_custom"; -import { deepActiveElement } from "../common/dom/deep-active-element"; const CACHE_URL_PATHS = ["lovelace", "developer-tools"]; const COMPONENTS = { @@ -64,6 +64,10 @@ const COMPONENTS = { import( /* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list" ), + "media-browser": () => + import( + /* webpackChunkName: "panel-media-browser" */ "../panels/media-browser/ha-panel-media-browser" + ), }; const getRoutes = (panels: Panels): RouterOptions => { diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index a176fc251c..0aa6389e21 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -667,8 +667,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { entityId: this._config!.entity, mediaPickedCallback: (pickedMedia: MediaPickedEvent) => this._playMedia( - pickedMedia.media_content_id, - pickedMedia.media_content_type + pickedMedia.item.media_content_id, + pickedMedia.item.media_content_type ), }); } diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts new file mode 100644 index 0000000000..33cd43dd0c --- /dev/null +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -0,0 +1,151 @@ +import "@material/mwc-icon-button"; +import { mdiPlayNetwork } from "@mdi/js"; +import "@polymer/app-layout/app-header/app-header"; +import "@polymer/app-layout/app-toolbar/app-toolbar"; +import { + css, + CSSResultArray, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import { LocalStorage } from "../../common/decorators/local-storage"; +import { HASSDomEvent } from "../../common/dom/fire_event"; +import { computeStateDomain } from "../../common/entity/compute_state_domain"; +import { supportsFeature } from "../../common/entity/supports-feature"; +import "../../components/ha-menu-button"; +import "../../components/media-player/ha-media-player-browse"; +import { + BROWSER_SOURCE, + MediaPickedEvent, + SUPPORT_BROWSE_MEDIA, +} from "../../data/media-player"; +import "../../layouts/ha-app-layout"; +import { haStyle } from "../../resources/styles"; +import type { HomeAssistant } from "../../types"; +import { showWebBrowserPlayMediaDialog } from "./show-media-player-dialog"; +import { showSelectMediaPlayerDialog } from "./show-select-media-source-dialog"; + +@customElement("ha-panel-media-browser") +class PanelMediaBrowser extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) + public narrow!: boolean; + + // @ts-ignore + @LocalStorage("mediaBrowseEntityId", true) + private _entityId = BROWSER_SOURCE; + + protected render(): TemplateResult { + const stateObj = this._entityId + ? this.hass.states[this._entityId] + : undefined; + + const title = + this._entityId === BROWSER_SOURCE + ? `${this.hass.localize("ui.components.media-browser.web-browser")} - ` + : stateObj?.attributes.friendly_name + ? `${stateObj?.attributes.friendly_name} - ` + : undefined; + + return html` + + + + +
+ ${title || ""}${this.hass.localize( + "ui.components.media-browser.media-player-browser" + )} +
+ + + +
+
+
+ +
+
+ `; + } + + private _showSelectMediaPlayerDialog(): void { + showSelectMediaPlayerDialog(this, { + mediaSources: this._mediaPlayerEntities, + sourceSelectedCallback: (entityId) => { + this._entityId = entityId; + }, + }); + } + + private async _mediaPicked( + ev: HASSDomEvent + ): Promise { + const item = ev.detail.item; + if (this._entityId === BROWSER_SOURCE) { + const resolvedUrl: any = await this.hass.callWS({ + type: "media_source/resolve_media", + media_content_id: item.media_content_id, + }); + + showWebBrowserPlayMediaDialog(this, { + sourceUrl: resolvedUrl.url, + sourceType: resolvedUrl.mime_type, + title: item.title, + }); + return; + } + + this.hass!.callService("media_player", "play_media", { + entity_id: this._entityId, + media_content_id: item.media_content_id, + media_content_type: item.media_content_type, + }); + } + + private get _mediaPlayerEntities() { + return Object.values(this.hass!.states).filter((entity) => { + if ( + computeStateDomain(entity) === "media_player" && + supportsFeature(entity, SUPPORT_BROWSE_MEDIA) + ) { + return true; + } + + return false; + }); + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + ha-media-player-browse { + height: calc(100vh - 84px); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-panel-media-browser": PanelMediaBrowser; + } +} diff --git a/src/panels/media-browser/hui-dialog-select-media-player.ts b/src/panels/media-browser/hui-dialog-select-media-player.ts new file mode 100644 index 0000000000..bacce8505e --- /dev/null +++ b/src/panels/media-browser/hui-dialog-select-media-player.ts @@ -0,0 +1,93 @@ +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import { fireEvent } from "../../common/dom/fire_event"; +import { createCloseHeading } from "../../components/ha-dialog"; +import { BROWSER_SOURCE } from "../../data/media-player"; +import type { HomeAssistant } from "../../types"; +import type { SelectMediaPlayerDialogParams } from "./show-select-media-source-dialog"; + +@customElement("hui-dialog-select-media-player") +export class HuiDialogSelectMediaPlayer extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) + private _params?: SelectMediaPlayerDialogParams; + + public showDialog(params: SelectMediaPlayerDialogParams): void { + this._params = params; + } + + public closeDialog() { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult { + if (!this._params) { + return html``; + } + + return html` + + ${this.hass.localize( + "ui.components.media-browser.web-browser" + )} + ${this._params.mediaSources.map( + (source) => html` + ${source.attributes.friendly_name} + ` + )} + + + `; + } + + private _selectSource(ev: CustomEvent): void { + const entityId = ev.detail.item.itemName; + this._params!.sourceSelectedCallback(entityId); + this.closeDialog(); + } + + static get styles(): CSSResult { + return css` + ha-dialog { + --dialog-content-padding: 0 24px 20px; + } + paper-item { + cursor: pointer; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dialog-select-media-player": HuiDialogSelectMediaPlayer; + } +} diff --git a/src/panels/media-browser/hui-dialog-web-browser-play-media.ts b/src/panels/media-browser/hui-dialog-web-browser-play-media.ts new file mode 100644 index 0000000000..05fbea66d6 --- /dev/null +++ b/src/panels/media-browser/hui-dialog-web-browser-play-media.ts @@ -0,0 +1,122 @@ +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import { fireEvent } from "../../common/dom/fire_event"; +import { createCloseHeading } from "../../components/ha-dialog"; +import "../../components/ha-hls-player"; +import type { HomeAssistant } from "../../types"; +import { WebBrowserPlayMediaDialogParams } from "./show-media-player-dialog"; + +@customElement("hui-dialog-web-browser-play-media") +export class HuiDialogWebBrowserPlayMedia extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) + private _params?: WebBrowserPlayMediaDialogParams; + + public showDialog(params: WebBrowserPlayMediaDialogParams): void { + this._params = params; + } + + public closeDialog() { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult { + if (!this._params || !this._params.sourceType || !this._params.sourceUrl) { + return html``; + } + + const mediaType = this._params.sourceType.split("/", 1)[0]; + + return html` + + ${mediaType === "audio" + ? html` + + ` + : mediaType === "video" + ? html` + + ` + : this._params.sourceType === "application/x-mpegURL" + ? html` + + ` + : mediaType === "image" + ? html`` + : html`${this.hass.localize( + "ui.components.media-browser.media_not_supported" + )}`} + + `; + } + + static get styles(): CSSResult { + return css` + ha-dialog { + --mdc-dialog-heading-ink-color: var(--primary-text-color); + } + + @media (min-width: 800px) { + ha-dialog { + --mdc-dialog-max-width: 800px; + --mdc-dialog-min-width: 400px; + } + } + + video, + audio, + img { + outline: none; + width: 100%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dialog-web-browser-play-media": HuiDialogWebBrowserPlayMedia; + } +} diff --git a/src/panels/media-browser/show-media-player-dialog.ts b/src/panels/media-browser/show-media-player-dialog.ts new file mode 100644 index 0000000000..161e97fd96 --- /dev/null +++ b/src/panels/media-browser/show-media-player-dialog.ts @@ -0,0 +1,21 @@ +import { fireEvent } from "../../common/dom/fire_event"; + +export interface WebBrowserPlayMediaDialogParams { + sourceUrl: string; + sourceType: string; + title?: string; +} + +export const showWebBrowserPlayMediaDialog = ( + element: HTMLElement, + webBrowserPlayMediaDialogParams: WebBrowserPlayMediaDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "hui-dialog-web-browser-play-media", + dialogImport: () => + import( + /* webpackChunkName: "hui-dialog-media-player" */ "./hui-dialog-web-browser-play-media" + ), + dialogParams: webBrowserPlayMediaDialogParams, + }); +}; diff --git a/src/panels/media-browser/show-select-media-source-dialog.ts b/src/panels/media-browser/show-select-media-source-dialog.ts new file mode 100644 index 0000000000..ec68a67a5b --- /dev/null +++ b/src/panels/media-browser/show-select-media-source-dialog.ts @@ -0,0 +1,21 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { fireEvent } from "../../common/dom/fire_event"; + +export interface SelectMediaPlayerDialogParams { + mediaSources: HassEntity[]; + sourceSelectedCallback: (entityId: string) => void; +} + +export const showSelectMediaPlayerDialog = ( + element: HTMLElement, + selectMediaPlayereDialogParams: SelectMediaPlayerDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "hui-dialog-select-media-player", + dialogImport: () => + import( + /* webpackChunkName: "hui-dialog-select-media-player" */ "./hui-dialog-select-media-player" + ), + dialogParams: selectMediaPlayereDialogParams, + }); +}; diff --git a/src/translations/en.json b/src/translations/en.json index 9f0c211c75..775859bb11 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -357,8 +357,13 @@ "play-media": "Play Media", "pick-media": "Pick Media", "no_items": "No items", - "choose-source": "Choose Source", + "choose_player": "Choose Player", "media-player-browser": "Media Player Browser", + "web-browser": "Web Browser", + "media_player": "Media Player", + "audio_not_supported": "Your browser does not support the audio element.", + "video_not_supported": "Your browser does not support the video element.", + "media_not_supported": "The Browser Media Player does not support this type of media", "content-type": { "server": "Server", "library": "Library", From 2adeb88fe69b9891d9886cfd10efcc85b3da97c4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Sep 2020 23:02:24 +0200 Subject: [PATCH 014/146] Bumped version to 20200904.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8ca7a0f6d6..4c386522cb 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200901.0", + version="20200904.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From fcc22ba5602c8443c0fdd712ef28c9e50ecafd03 Mon Sep 17 00:00:00 2001 From: Charles Garwood Date: Fri, 4 Sep 2020 17:14:15 -0400 Subject: [PATCH 015/146] Add node details shortcut to OZW device pages (#6791) Co-authored-by: Bram Kragten --- .../ozw/ha-device-actions-ozw.ts | 88 +++++++++++++++++++ .../ozw/ha-device-info-ozw.ts | 11 --- .../config/devices/ha-config-device-page.ts | 7 ++ src/translations/en.json | 2 + 4 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts diff --git a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts b/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts new file mode 100644 index 0000000000..7f0cac03f2 --- /dev/null +++ b/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-actions-ozw.ts @@ -0,0 +1,88 @@ +import { + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, + css, + PropertyValues, +} from "lit-element"; +import { DeviceRegistryEntry } from "../../../../../../data/device_registry"; +import { haStyle } from "../../../../../../resources/styles"; +import { HomeAssistant } from "../../../../../../types"; +import { + getIdentifiersFromDevice, + OZWNodeIdentifiers, +} from "../../../../../../data/ozw"; +import { showOZWRefreshNodeDialog } from "../../../../integrations/integration-panels/ozw/show-dialog-ozw-refresh-node"; +import { navigate } from "../../../../../../common/navigate"; +import "@material/mwc-button/mwc-button"; + +@customElement("ha-device-actions-ozw") +export class HaDeviceActionsOzw extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public device!: DeviceRegistryEntry; + + @property() + private node_id = 0; + + @property() + private ozw_instance = 1; + + + protected updated(changedProperties: PropertyValues) { + if (changedProperties.has("device")) { + const identifiers: + | OZWNodeIdentifiers + | undefined = getIdentifiersFromDevice(this.device); + if (!identifiers) { + return; + } + this.ozw_instance = identifiers.ozw_instance; + this.node_id = identifiers.node_id; + } + } + + protected render(): TemplateResult { + if (!this.ozw_instance || !this.node_id) { + return html``; + } + return html` + + ${this.hass.localize("ui.panel.config.ozw.node.button")} + + + ${this.hass.localize("ui.panel.config.ozw.refresh_node.button")} + + `; + } + + private async _refreshNodeClicked() { + showOZWRefreshNodeDialog(this, { + node_id: this.node_id, + ozw_instance: this.ozw_instance, + }); + } + + private async _nodeDetailsClicked() { + navigate( + this, + `/config/ozw/network/${this.ozw_instance}/node/${this.node_id}/dashboard` + ); + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + :host { + display: flex; + flex-direction: column; + align-items: flex-start; + } + `, + ]; + } +} diff --git a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts b/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts index 2197fe689b..ce639c75b8 100644 --- a/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts +++ b/src/panels/config/devices/device-detail/integration-elements/ozw/ha-device-info-ozw.ts @@ -18,7 +18,6 @@ import { getIdentifiersFromDevice, OZWNodeIdentifiers, } from "../../../../../../data/ozw"; -import { showOZWRefreshNodeDialog } from "../../../../integrations/integration-panels/ozw/show-dialog-ozw-refresh-node"; @customElement("ha-device-info-ozw") export class HaDeviceInfoOzw extends LitElement { @@ -83,19 +82,9 @@ export class HaDeviceInfoOzw extends LitElement { ? this.hass.localize("ui.common.yes") : this.hass.localize("ui.common.no")}
- - Refresh Node - `; } - private async _refreshNodeClicked() { - showOZWRefreshNodeDialog(this, { - node_id: this.node_id, - ozw_instance: this.ozw_instance, - }); - } - static get styles(): CSSResult[] { return [ haStyle, diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 0287b084cd..22c2ea190a 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -517,12 +517,19 @@ export class HaConfigDevicePage extends LitElement { `); } if (integrations.includes("ozw")) { + import("./device-detail/integration-elements/ozw/ha-device-actions-ozw"); import("./device-detail/integration-elements/ozw/ha-device-info-ozw"); templates.push(html` +
+ +
`); } if (integrations.includes("zha")) { diff --git a/src/translations/en.json b/src/translations/en.json index 775859bb11..9977bae247 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1764,6 +1764,7 @@ "complete": "Interview process is complete" }, "refresh_node": { + "button": "Refresh Node", "title": "Refresh Node Information", "complete": "Node Refresh Complete", "description": "This will tell OpenZWave to re-interview a node and update the node's command classes, capabilities, and values.", @@ -1818,6 +1819,7 @@ "failed": "Failed" }, "node": { + "button": "Node Details", "not_found": "Node not found" }, "services": { From d5794c3e2e0b9747c260312fead5b0be2fe2f61f Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Sat, 5 Sep 2020 00:32:46 +0000 Subject: [PATCH 016/146] [ci skip] Translation update --- translations/frontend/cs.json | 16 ++++++++++++-- translations/frontend/en.json | 34 ++++++++++++++++++++++++------ translations/frontend/nb.json | 16 ++++++++++++-- translations/frontend/ru.json | 22 ++++++++++++++----- translations/frontend/zh-Hans.json | 16 ++++++++++++-- translations/frontend/zh-Hant.json | 16 ++++++++++++-- 6 files changed, 100 insertions(+), 20 deletions(-) diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index ca77ec6015..135ed3075c 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -505,6 +505,7 @@ "back": "Zpět", "cancel": "Zrušit", "close": "Zavřít", + "continue": "Pokračovat", "delete": "Smazat", "error_required": "Povinné", "loading": "Načítání", @@ -689,8 +690,10 @@ "crop": "Oříznout" }, "more_info_control": { + "controls": "Ovládací prvky", "dismiss": "Zavřít dialog", "edit": "Upravit entitu", + "history": "Historie", "person": { "create_zone": "Vytvořit zónu z aktuálního umístění" }, @@ -1912,7 +1915,7 @@ "filter": "Nově načíst entity integrace Filter", "generic": "Nově načíst entity integrace Generic IP camera", "generic_thermostat": "Nově načíst entity integrace Generic thermostat", - "group": "Nově načíst skupiny", + "group": "Nově načíst skupiny, skupiny entit a notifikační služby", "heading": "Konfigurace se načítá", "history_stats": "Nově načíst entity integrace History stats", "homekit": "Nově načíst entity integrace HomeKit", @@ -1923,12 +1926,17 @@ "input_text": "Nově načíst pomocníky - texty", "introduction": "Některé části Home Assistant lze nově načíst bez nutnosti restartování. Nové načtení zahodí jejich aktuální konfiguraci a načte novou.", "min_max": "Nově načíst entity integrace Min/Max", + "mqtt": "Nově načíst entity integrace MQTT", "person": "Nově načíst osoby", "ping": "Nově načíst entity integrace Ping", - "rest": "Nově načíst entity integrace Rest", + "reload": "Nově načíst integraci {domain}", + "rest": "Nově načíst entity a notifikační služby integrace Rest", + "rpi_gpio": "Nově načíst entity integrace Raspberry Pi GPIO", "scene": "Nově načíst scény", "script": "Nově načíst skripty", + "smtp": "Nově načíst notifikační služby integrace SMTP", "statistics": "Nově načíst entity integrace Statistics", + "telegram": "Nově načíst notifikační služby integrace Telegram", "template": "Nově načíst entity integrace Template", "trend": "Nově načíst entity integrace Trend", "universal": "Nově načíst entity integrace Universal media player", @@ -2549,7 +2557,11 @@ } }, "cardpicker": { + "by_card": "Podle karty", + "by_entity": "Podle entity", "custom_card": "Vlastní", + "domain": "Doména", + "entity": "Entita", "no_description": "Žádný popis není k dispozici." }, "edit_card": { diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 482cf10f23..e762f3d3c1 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -563,6 +563,8 @@ "no_history_found": "No state history found." }, "media-browser": { + "audio_not_supported": "Your browser does not support the audio element.", + "choose_player": "Choose Player", "choose-source": "Choose Source", "content-type": { "album": "Album", @@ -571,12 +573,16 @@ "playlist": "Playlist", "server": "Server" }, + "media_not_supported": "The Browser Media Player does not support this type of media", + "media_player": "Media Player", "media-player-browser": "Media Player Browser", "no_items": "No items", "pick": "Pick", "pick-media": "Pick Media", "play": "Play", - "play-media": "Play Media" + "play-media": "Play Media", + "video_not_supported": "Your browser does not support the video element.", + "web-browser": "Web Browser" }, "picture-upload": { "label": "Picture", @@ -634,7 +640,7 @@ "icon": "Icon Override", "icon_error": "Icons should be in the format 'prefix:iconname', e.g. 'mdi:home'", "name": "Name Override", - "note": "Note: this might not work yet with all integrations.", + "note": "Note: This might not work yet with all integrations.", "unavailable": "This entity is not currently available.", "update": "Update" }, @@ -1765,8 +1771,21 @@ "versions": "Obtaining information about firmware and command class versions", "wakeup": "Setting up support for wakeup queues and messages" }, + "node": { + "button": "Node Details", + "not_found": "Node not found" + }, + "nodes_table": { + "failed": "Failed", + "id": "ID", + "manufacturer": "Manufacturer", + "model": "Model", + "query_stage": "Query Stage", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "If the node is battery powered, be sure to wake it before proceeding", + "button": "Refresh Node", "complete": "Node Refresh Complete", "description": "This will tell OpenZWave to re-interview a node and update the node's command classes, capabilities, and values.", "node_status": "Node Status", @@ -2025,7 +2044,7 @@ "system": "System" } }, - "users_privileges_note": "The users group is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators." + "users_privileges_note": "The user group feature is a work in progress. The user will be unable to administer the instance via the UI. We're still auditing all management API endpoints to ensure that they correctly limit access to administrators." }, "zha": { "add_device_page": { @@ -2063,7 +2082,7 @@ "clusters": { "header": "Clusters", "help_cluster_dropdown": "Select a cluster to view attributes and commands.", - "introduction": "Clusters are the building blocks for Zigbee functionality. They seperate functionality into logical units. There are client and server types and that are comprised of attributes and commands." + "introduction": "Clusters are the building blocks for Zigbee functionality. They separate functionality into logical units. There are client and server types and that are comprised of attributes and commands." }, "common": { "add_devices": "Add Devices", @@ -2196,7 +2215,7 @@ "true": "True" }, "node_management": { - "add_to_group": "Add To Group", + "add_to_group": "Add to Group", "entities": "Entities of this node", "entity_info": "Entity Information", "exclude_entity": "Exclude this entity from Home Assistant", @@ -2209,11 +2228,11 @@ "node_to_control": "Node to control", "nodes": "Nodes", "nodes_hint": "Select node to view per-node options", - "nodes_in_group": "Other Nodes in this group:", + "nodes_in_group": "Other nodes in this group:", "pooling_intensity": "Polling intensity", "protection": "Protection", "remove_broadcast": "Remove Broadcast", - "remove_from_group": "Remove From Group", + "remove_from_group": "Remove from Group", "set_protection": "Set Protection" }, "ozw_log": { @@ -2575,6 +2594,7 @@ "options": "More options", "pick_card": "Which card would you like to add?", "pick_card_view_title": "Which card would you like to add to your {name} view?", + "search_cards": "Search cards", "show_code_editor": "Show Code Editor", "show_visual_editor": "Show Visual Editor", "toggle_editor": "Toggle Editor", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index 235f6930b8..db02e1f81a 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -489,6 +489,7 @@ "back": "Tilbake", "cancel": "Avbryt", "close": "Lukk", + "continue": "Fortsette", "delete": "Slett", "error_required": "Nødvendig", "loading": "Laster", @@ -672,8 +673,10 @@ "crop": "Beskjære" }, "more_info_control": { + "controls": "Kontroller", "dismiss": "Avvis dialogboksen", "edit": "Redigér entitet", + "history": "Historie", "person": { "create_zone": "Opprett sone fra gjeldende plassering" }, @@ -1866,7 +1869,7 @@ "filter": "Last inn filter entiteter på nytt", "generic": "Last inn generiske IP-kamera entiteter på nytt", "generic_thermostat": "Last inn generiske termostat entiteter på nytt", - "group": "Last inn grupper på nytt", + "group": "Laste inn grupper, gruppere enheter og varsle tjenester på nytt", "heading": "YAML -Konfigurasjon lastes på nytt", "history_stats": "Last inn historiske tilstander på nytt", "homekit": "Last inn HomeKit på nytt", @@ -1877,12 +1880,17 @@ "input_text": "Last inn inndata tekst på nytt", "introduction": "Noen deler av Home Assistant kan laste inn uten å kreve omstart. Hvis du trykker last på nytt, vil du bytte den nåværende konfigurasjonen med den nye.", "min_max": "Last inn min/maks entiteter på nytt", + "mqtt": "Last inn mqtt-enheter på nytt", "person": "Last inn personer på nytt", "ping": "Last inn ping binære sensor entiteter på nytt", - "rest": "Last inn REST entiteter på nytt", + "reload": "Last inn {domain} på nytt", + "rest": "Last inn hvileenheter på nytt og varsle tjenester", + "rpi_gpio": "Last inn Raspberry Pi GPIO-enheter på nytt", "scene": "Last inn scener på nytt", "script": "Last inn skript på nytt", + "smtp": "Last inn smtp-varslingstjenester på nytt", "statistics": "Last inn statistiske entiteter på nytt", + "telegram": "Last inn telegram varslingstjenester på nytt", "template": "Laste inn mal entiteter på nytt", "trend": "Laste inn trend entiteter på nytt", "universal": "Laste inn universelle mediespiller entiteter på nytt", @@ -2493,7 +2501,11 @@ } }, "cardpicker": { + "by_card": "Med kort", + "by_entity": "Etter enhet", "custom_card": "Tilpasset", + "domain": "Domene", + "entity": "Entitet", "no_description": "Ingen beskrivelse tilgjengelig." }, "edit_card": { diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index b1acc8ea5f..110d802140 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -505,6 +505,7 @@ "back": "Назад", "cancel": "Отменить", "close": "Закрыть", + "continue": "Продолжить", "delete": "Удалить", "error_required": "Обязательное поле", "loading": "Загрузка", @@ -689,8 +690,10 @@ "crop": "Обрезать" }, "more_info_control": { + "controls": "Управление", "dismiss": "Закрыть диалог", "edit": "Изменить объект", + "history": "История", "person": { "create_zone": "Создать зону из текущего местоположения" }, @@ -1209,7 +1212,7 @@ "title": "Alexa" }, "caption": "Home Assistant Cloud", - "description_features": "Управление сервером вдали от дома, интеграция с Alexa и Google Assistant", + "description_features": "Удалённый доступ к серверу, интеграция с Alexa и Google Assistant", "description_login": "{email}", "description_not_login": "Вход не выполнен", "dialog_certificate": { @@ -1255,7 +1258,7 @@ "email": "Адрес электронной почты", "email_error_msg": "Неверный адрес электронной почты.", "forgot_password": "забыли пароль?", - "introduction": "Home Assistant Cloud обеспечивает безопасный доступ к Вашему серверу, даже если Вы находитесь вдали от дома. Также это даёт возможность подключения к функциям облачных сервисов Amazon Alexa и Google Assistant.", + "introduction": "Home Assistant Cloud обеспечивает безопасный доступ к Вашему серверу, даже если Вы находитесь вдали от дома. Также это даёт возможность простого подключения к функциям облачных сервисов Amazon Alexa и Google Assistant.", "introduction2": "Услуга предоставляется нашим партнером ", "introduction2a": ", компанией от основателей Home Assistant и Hass.io.", "introduction3": "Home Assistant Cloud предлагает одноразовый бесплатный пробный период продолжительностью один месяц. Для активации пробного периода платёжная информация не требуется.", @@ -1402,7 +1405,7 @@ }, "scripts": "Сценарии", "unknown_error": "Неизвестная ошибка.", - "unnamed_device": "Безымянное устройство", + "unnamed_device": "Устройство без названия", "update": "Обновить" }, "entities": { @@ -1902,7 +1905,7 @@ "filter": "Перезагрузить объекты интеграции \"Filter\"", "generic": "Перезагрузить объекты интеграции \"Generic IP Camera\"", "generic_thermostat": "Перезагрузить объекты интеграции \"Generic Thermostat\"", - "group": "Перезагрузить группы", + "group": "Перезагрузить группы, объекты групп и службы уведомлений", "heading": "Перезагрузка конфигурации YAML", "history_stats": "Перезагрузить объекты интеграции \"History Stats\"", "homekit": "Перезагрузить HomeKit", @@ -1913,12 +1916,17 @@ "input_text": "Перезагрузить вспомогательные элементы ввода текста", "introduction": "Некоторые компоненты Home Assistant можно перезагрузить без необходимости перезапуска всей системы. Перезагрузка выгружает текущую конфигурацию YAML и загружает новую.", "min_max": "Перезагрузить объекты интеграции \"Min/Max\"", + "mqtt": "Перезагрузить объекты интеграции \"MQTT\"", "person": "Перезагрузить персоны", "ping": "Перезагрузить объекты интеграции \"Ping (ICMP)\"", - "rest": "Перезагрузить объекты интеграции \"REST\"", + "reload": "Перезагрузить {domain}", + "rest": "Перезагрузить объекты и службы уведомлений интеграции \"REST\"", + "rpi_gpio": "Перезагрузить объекты интеграции \"Raspberry Pi GPIO\"", "scene": "Перезагрузить сцены", "script": "Перезагрузить сценарии", + "smtp": "Перезагрузить службы уведомлений SMTP", "statistics": "Перезагрузить объекты интеграции \"Statistics\"", + "telegram": "Перезагрузить службы уведомлений Telegram", "template": "Перезагрузить объекты шаблонов", "trend": "Перезагрузить объекты интеграции \"Trend\"", "universal": "Перезагрузить объекты интеграции \"Universal Media Player\"", @@ -2539,7 +2547,11 @@ } }, "cardpicker": { + "by_card": "Карточки", + "by_entity": "Объекты", "custom_card": "Custom", + "domain": "Домен", + "entity": "Объект", "no_description": "Описание недоступно." }, "edit_card": { diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index a318db0442..1e7b9bfa0a 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -505,6 +505,7 @@ "back": "返回", "cancel": "取消", "close": "关闭", + "continue": "继续", "delete": "删除", "error_required": "必填", "loading": "加载中", @@ -689,8 +690,10 @@ "crop": "剪裁" }, "more_info_control": { + "controls": "控制项", "dismiss": "关闭对话框", "edit": "编辑实体", + "history": "历史", "person": { "create_zone": "从当前位置创建地点" }, @@ -1912,7 +1915,7 @@ "filter": "重载 filter 实体", "generic": "重载通用 IP 摄像机实体", "generic_thermostat": "重载通用恒温器实体", - "group": "重载分组", + "group": "重载分组、分组实体及通知服务", "heading": "配置重载", "history_stats": "重载历史记录统计实体", "homekit": "重载 HomeKit", @@ -1923,12 +1926,17 @@ "input_text": "重载文字输入", "introduction": "Home Assistant 中的部分配置可以直接重载,而无需重启服务。点击重载按钮将重新载入新的配置。", "min_max": "重载最小值/最大值实体", + "mqtt": "重载 mqtt 实体", "person": "重载人员", "ping": "重载 ping 二元传感器实体", - "rest": "重载 REST 实体", + "reload": "重载{domain}", + "rest": "重载 REST 实体及通知服务", + "rpi_gpio": "重载树莓派 GPIO 实体", "scene": "重载场景", "script": "重载脚本", + "smtp": "重载 smtp 通知服务", "statistics": "重载 statistics 实体", + "telegram": "重载 telegram 通知服务", "template": "重载模板实体", "trend": "重载 trend 实体", "universal": "重载通用媒体播放器实体", @@ -2549,7 +2557,11 @@ } }, "cardpicker": { + "by_card": "按卡片", + "by_entity": "按实体", "custom_card": "自定义", + "domain": "域", + "entity": "实体", "no_description": "没有描述。" }, "edit_card": { diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 78b42e133e..025a931264 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -505,6 +505,7 @@ "back": "上一步", "cancel": "取消", "close": "關閉", + "continue": "繼續", "delete": "刪除", "error_required": "必填", "loading": "讀取中", @@ -689,8 +690,10 @@ "crop": "裁切" }, "more_info_control": { + "controls": "控制", "dismiss": "忽略對話", "edit": "編輯實體", + "history": "歷史", "person": { "create_zone": "使用目前位置新增區域" }, @@ -1912,7 +1915,7 @@ "filter": "重新載入過濾器實體", "generic": "重新載入通用 IP 攝影機實體", "generic_thermostat": "重新載入通用溫控器實體", - "group": "重新載入群組", + "group": "重新載入群組、群組實體及通知服務", "heading": "YAML 設定新載入中", "history_stats": "重新載入歷史狀態實體", "homekit": "重新載入 Homekit", @@ -1923,12 +1926,17 @@ "input_text": "重新載入輸入文字", "introduction": "Home Assistant 中部分設定無須重啟即可重新載入生效。點選重新載入按鈕,即可解除目前 YAML 設定,並重新載入最新設定。", "min_max": "重新載入最低/最高實體", + "mqtt": "重新載入 MQTT 實體", "person": "重新載入人員", "ping": "重新載入 Pung 二進位傳感器實體", - "rest": "重新載入剩餘實體", + "reload": "重新載入{domain}", + "rest": "重新載入剩餘實體及通知服務", + "rpi_gpio": "重新載入 Raspberry Pi GPIO 實體", "scene": "重新載入場景", "script": "重新載入腳本", + "smtp": "重新載入 SMTP 通知服務", "statistics": "重新載入統計資訊實體", + "telegram": "重新載入 Telegram 通知服務", "template": "重新載入範例實體", "trend": "重新載入趨勢實體", "universal": "重新載入通用媒體播放器實體", @@ -2549,7 +2557,11 @@ } }, "cardpicker": { + "by_card": "以面板", + "by_entity": "以實體", "custom_card": "自訂面板", + "domain": "區域", + "entity": "實體", "no_description": "無描述可使用。" }, "edit_card": { From 879011c8e9e2160b3c72ab3742883c6849e1ee12 Mon Sep 17 00:00:00 2001 From: Donnie Date: Sat, 5 Sep 2020 06:51:52 -0700 Subject: [PATCH 017/146] Fix incorrect link to mode documentation in automation editor (#6793) From 8f8a2cea56afb25488ccd521407d8698498014c6 Mon Sep 17 00:00:00 2001 From: Pawel Date: Sun, 6 Sep 2020 00:35:34 +0200 Subject: [PATCH 018/146] Don't show source select dialog when media player Unavailable. (#6799) --- src/dialogs/more-info/controls/more-info-media_player.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts index aefc6bf92f..45dee3e224 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.ts +++ b/src/dialogs/more-info/controls/more-info-media_player.ts @@ -130,7 +130,7 @@ class MoreInfoMediaPlayer extends LitElement {
` : ""} - ${stateObj.state !== "off" && + ${![UNAVAILABLE, UNKNOWN, "off"].includes(stateObj.state) && supportsFeature(stateObj, SUPPORT_SELECT_SOURCE) && stateObj.attributes.source_list?.length ? html` From 3fd7899b9323fc05ba6d9d75c74062ae18275ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sun, 6 Sep 2020 02:16:50 +0200 Subject: [PATCH 019/146] Display services as services and not devices (#6798) * Display services as services and not devices * remove seperator * Add comma * Update src/panels/config/integrations/ha-integration-card.ts Co-authored-by: Bram Kragten * Fix spacing * Remove check Co-authored-by: Bram Kragten --- .../integrations/ha-integration-card.ts | 34 ++++++++++++++++--- src/translations/en.json | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index dddeaf6754..a1625c7c2c 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -137,6 +137,7 @@ export class HaIntegrationCard extends LitElement { private _renderSingleEntry(item: ConfigEntryExtended): TemplateResult { const devices = this._getDevices(item); + const services = this._getServices(item); const entities = this._getEntities(item); return html` @@ -168,7 +169,7 @@ export class HaIntegrationCard extends LitElement {

${item.localized_domain_name === item.title ? "" : item.title}

- ${devices.length || entities.length + ${devices.length || services.length || entities.length ? html`
${devices.length @@ -180,10 +181,22 @@ export class HaIntegrationCard extends LitElement { "count", devices.length )}${services.length ? "," : ""} + ` + : ""} + ${services.length + ? html` + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.services", + "count", + services.length + )} ` : ""} - ${devices.length && entities.length + ${(devices.length || services.length) && entities.length ? this.hass.localize("ui.common.and") : ""} ${entities.length @@ -304,8 +317,21 @@ export class HaIntegrationCard extends LitElement { if (!this.deviceRegistryEntries) { return []; } - return this.deviceRegistryEntries.filter((device) => - device.config_entries.includes(configEntry.entry_id) + return this.deviceRegistryEntries.filter( + (device) => + device.config_entries.includes(configEntry.entry_id) && + device.entry_type !== "service" + ); + } + + private _getServices(configEntry: ConfigEntry): DeviceRegistryEntry[] { + if (!this.deviceRegistryEntries) { + return []; + } + return this.deviceRegistryEntries.filter( + (device) => + device.config_entries.includes(configEntry.entry_id) && + device.entry_type === "service" ); } diff --git a/src/translations/en.json b/src/translations/en.json index 9977bae247..8e4ced5344 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1638,6 +1638,7 @@ "config_entry": { "devices": "{count} {count, plural,\n one {device}\n other {devices}\n}", "entities": "{count} {count, plural,\n one {entity}\n other {entities}\n}", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "rename": "Rename", "options": "Options", "system_options": "System options", From d69333dea4c37ad3a71830edec2ba06055bb218a Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Sun, 6 Sep 2020 00:32:31 +0000 Subject: [PATCH 020/146] [ci skip] Translation update --- translations/frontend/cs.json | 26 +++++++++++++++--- translations/frontend/en.json | 1 + translations/frontend/fr.json | 40 ++++++++++++++++++++++++--- translations/frontend/pt.json | 42 +++++++++++++++++++++-------- translations/frontend/ru.json | 43 ++++++++++++++++++++++++------ translations/frontend/zh-Hans.json | 22 ++++++++++++++- translations/frontend/zh-Hant.json | 24 +++++++++++++++-- 7 files changed, 169 insertions(+), 29 deletions(-) diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 135ed3075c..b8ec36694b 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -563,6 +563,8 @@ "no_history_found": "Historie stavu chybí." }, "media-browser": { + "audio_not_supported": "Váš prohlížeč nepodporuje element \"audio\".", + "choose_player": "Vyberte přehrávač", "choose-source": "Zvolte zdroj", "content-type": { "album": "Album", @@ -571,12 +573,16 @@ "playlist": "Seznam skladeb", "server": "Server" }, + "media_not_supported": "Přehrávač médií v prohlížeči nepodporuje tento typ média", + "media_player": "Přehrávač médií", "media-player-browser": "Prohlížeč přehrávače médií", "no_items": "Žádné položky", "pick": "Vybrat", "pick-media": "Vybrat média", "play": "Přehrát", - "play-media": "Přehrát média" + "play-media": "Přehrát média", + "video_not_supported": "Váš prohlížeč nepodporuje element \"video\".", + "web-browser": "Webový prohlížeč" }, "picture-upload": { "label": "Obrázek", @@ -634,7 +640,7 @@ "icon": "Nahrazení ikony", "icon_error": "Ikony by měly být ve formátu 'prefix:nazevikony', např. 'mdi:home'", "name": "Přepsání názvu", - "note": "Poznámka: to nemusí fungovat se všemi integracemi.", + "note": "Poznámka: U všech integrací to ještě nemusí fungovat.", "unavailable": "Tato entita není momentálně k dispozici.", "update": "Aktualizovat" }, @@ -1765,8 +1771,21 @@ "versions": "Získávám informace o verzích firmwaru a typech příkazů", "wakeup": "Nastavuji podporu pro probouzecí fronty a zprávy" }, + "node": { + "button": "Podrobnosti uzlu", + "not_found": "Uzel nenalezen" + }, + "nodes_table": { + "failed": "Selhalo", + "id": "ID", + "manufacturer": "Výrobce", + "model": "Model", + "query_stage": "Fáze dotazu", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Pokud je uzel napájen z baterie, nezapomeňte jej probudit, než budete pokračovat", + "button": "Obnovit uzel", "complete": "Obnova uzlu dokončena", "description": "Toto řekne OpenZWave, aby znovu provedl komunikaci s uzlem a aktualizoval typy příkazů, schopnosti a hodnoty uzlu.", "node_status": "Stav uzlu", @@ -2025,7 +2044,7 @@ "system": "Systémový" } }, - "users_privileges_note": "Skupina uživatelů je v přípravě. Uživatel nebude moci spravovat instanci prostřednictvím uživatelského rozhraní. Stále kontrolujeme všechny koncové body API pro správu, abychom zajistili, že správně omezují přístup." + "users_privileges_note": "Skupiny uživatelů jsou v přípravě. Uživatel je nebude moci spravovat prostřednictvím uživatelského rozhraní. Stále kontrolujeme API pro správu, abychom zajistili, že správně omezuje přístup pouze pro administrátory." }, "zha": { "add_device_page": { @@ -2575,6 +2594,7 @@ "options": "Více možností", "pick_card": "Kterou kartu chcete přidat?", "pick_card_view_title": "Kterou kartu byste chtěli přidat do svého {name} pohledu?", + "search_cards": "Vyhledat karty", "show_code_editor": "Zobrazit editor kódu", "show_visual_editor": "Zobrazit vizuální editor", "toggle_editor": "Přepnout Editor", diff --git a/translations/frontend/en.json b/translations/frontend/en.json index e762f3d3c1..e320c6c31e 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -1552,6 +1552,7 @@ "reload_restart_confirm": "Restart Home Assistant to finish reloading this integration", "rename": "Rename", "restart_confirm": "Restart Home Assistant to finish removing this integration", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Edit settings for {integration}", "system_options": "System options", "system_options_button": "System options for {integration}", diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 939706d0e1..86646023b1 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -505,6 +505,7 @@ "back": "Retour", "cancel": "Annuler", "close": "Fermer", + "continue": "Continuer", "delete": "Supprimer", "error_required": "Obligatoire", "loading": "Chargement", @@ -562,6 +563,8 @@ "no_history_found": "Aucun historique des valeurs trouvé." }, "media-browser": { + "audio_not_supported": "Votre navigateur ne prend pas en charge l'élément audio.", + "choose_player": "Choisissez le lecteur", "choose-source": "Choisissez la source", "content-type": { "album": "Album", @@ -570,12 +573,16 @@ "playlist": "Liste de lecture", "server": "Serveur" }, + "media_not_supported": "Le Browser Media Player ne prend pas en charge ce type de média", + "media_player": "Lecteur multimédia", "media-player-browser": "Lecteur multimédia", "no_items": "Aucun éléments", "pick": "Choisir", "pick-media": "Choisissez un média", "play": "Lecture", - "play-media": "Lire le média" + "play-media": "Lire le média", + "video_not_supported": "Votre navigateur ne prend pas en charge l'élément vidéo.", + "web-browser": "Navigateur web" }, "picture-upload": { "label": "Image", @@ -689,8 +696,10 @@ "crop": "Recadrer" }, "more_info_control": { + "controls": "Contrôles", "dismiss": "Fermer la fenêtre de dialogue", "edit": "Modifier l'entité", + "history": "Historique", "person": { "create_zone": "Créer une zone à partir de l'emplacement actuel" }, @@ -1762,8 +1771,21 @@ "versions": "Obtention d'informations sur les versions des microprogrammes et des classes de commande", "wakeup": "Configuration de la prise en charge des files d'attente et des messages de réveil" }, + "node": { + "button": "Détails du nœud", + "not_found": "Nœud introuvable" + }, + "nodes_table": { + "failed": "Echec", + "id": "ID", + "manufacturer": "Fabricant", + "model": "Modèle", + "query_stage": "Étape de requête", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Si le nœud est alimenté par batterie, assurez-vous de l'activer avant de continuer", + "button": "Actualiser le nœud", "complete": "Actualisation du nœud terminée", "description": "Cela indiquera à OpenZWave de réinterroger le nœud et de le mettre à jour (commandes, possibilités et valeurs).", "node_status": "État du nœud", @@ -1923,12 +1945,17 @@ "input_text": "Recharger les entrées de texte (input text)", "introduction": "Certaines parties de Home Assistant peuvent être rechargées sans nécessiter de redémarrage. Le fait de cliquer sur recharger déchargera leur configuration actuelle et chargera la nouvelle.", "min_max": "Recharger les entités min/max", + "mqtt": "Recharger les entités mqtt", "person": "Recharger les personnes", "ping": "Recharger les entités de capteur binaire ping", - "rest": "Recharger les entités REST", + "reload": "Recharger {domain}", + "rest": "Recharger les entités REST et notifier les services", + "rpi_gpio": "Recharger les entités GPIO du Raspberry Pi", "scene": "Recharger les scènes", "script": "Recharger les scripts", + "smtp": "Recharger les services de notification smtp", "statistics": "Recharger les entités de statistiques", + "telegram": "Recharger les services de notification de telegram", "template": "Recharger les entités modèles", "trend": "Recharger les entités de tendance", "universal": "Recharger les entités de lecteur média universel", @@ -2017,7 +2044,7 @@ "system": "Système" } }, - "users_privileges_note": "Le groupe d'utilisateurs est en cours de développement. L'utilisateur ne pourra pas gérer l'instance via l'interface. Nous vérifions les entrées de l'interface de gestion pour assurer que les accès soient limités aux administrateurs." + "users_privileges_note": "La fonctionnalité de groupe d'utilisateurs est en cours de développement. L'utilisateur ne pourra pas gérer l'instance via l'interface. Nous vérifions toujours tous les points terminaisons d'API pour assurer que les accès soient limités aux administrateurs." }, "zha": { "add_device_page": { @@ -2055,7 +2082,7 @@ "clusters": { "header": "Clusters", "help_cluster_dropdown": "Sélectionnez un cluster pour afficher les attributs et les commandes.", - "introduction": "Les clusters sont les blocs de construction de la fonctionnalité Zigbee. Ils séparent les fonctionnalités en unités logiques. Il existe des types de clients et de serveurs qui sont composés d'attributs et de commandes." + "introduction": "Les grappes sont les éléments de construction de la fonctionnalité Zigbee. Ils séparent les fonctionnalités en unités logiques. Il en existe de type clients et serveurs qui sont composés d'attributs et de commandes." }, "common": { "add_devices": "Ajouter des appareils", @@ -2549,7 +2576,11 @@ } }, "cardpicker": { + "by_card": "Par carte", + "by_entity": "Par entité", "custom_card": "Personnalisé", + "domain": "Domaine", + "entity": "Entité", "no_description": "Aucune description disponible." }, "edit_card": { @@ -2563,6 +2594,7 @@ "options": "Plus d'options", "pick_card": "Quelle carte aimeriez-vous ajouter ?", "pick_card_view_title": "Quelle carte souhaitez-vous ajouter à votre vue {name} ?", + "search_cards": "Rechercher des cartes", "show_code_editor": "Afficher l'éditeur de code", "show_visual_editor": "Afficher l'éditeur visuel", "toggle_editor": "Permuter l’éditeur", diff --git a/translations/frontend/pt.json b/translations/frontend/pt.json index f34d128cfd..96cf3681a2 100644 --- a/translations/frontend/pt.json +++ b/translations/frontend/pt.json @@ -419,7 +419,7 @@ "unlock": "Desbloquear" }, "media_player": { - "browse_media": "Pesquisar mídia", + "browse_media": "Pesquisar media", "media_next_track": "Próximo", "media_play": "Reproduzir", "media_play_pause": "Reproduzir/pausar", @@ -505,6 +505,7 @@ "back": "Retroceder", "cancel": "Cancelar", "close": "Fechar", + "continue": "Continuar", "delete": "Apagar", "error_required": "Obrigatório", "loading": "A carregar", @@ -562,6 +563,8 @@ "no_history_found": "Nenhum histórico de estado encontrado." }, "media-browser": { + "audio_not_supported": "O seu navegador não suporta o elemento de áudio.", + "choose_player": "Escolha o Leitor", "choose-source": "Escolha a fonte", "content-type": { "album": "Álbum", @@ -570,12 +573,15 @@ "playlist": "Lista de reprodução", "server": "Servidor" }, + "media_player": "Leitor multimédia", "media-player-browser": "Navegador do Media Player", "no_items": "Sem itens", "pick": "Escolher", - "pick-media": "Escolha a mídia", + "pick-media": "Escolha a média", "play": "Reproduzir", - "play-media": "Reproduzir Mídia" + "play-media": "Reproduzir Média", + "video_not_supported": "O seu navegador não suporta o elemento de vídeo.", + "web-browser": "Navegador web" }, "picture-upload": { "label": "Imagem", @@ -600,7 +606,7 @@ "second": "{count} {count, plural,\n one {segundo}\n other {segundos}\n}", "week": "{count} {count, plural,\n one {semana}\n other {semanas}\n}" }, - "future": "À {time}", + "future": "Há {time}", "just_now": "Agora mesmo", "never": "Nunca", "past": "{time} atrás" @@ -690,6 +696,7 @@ "more_info_control": { "dismiss": "Descartar diálogo", "edit": "Editar entidade", + "history": "Histórico", "person": { "create_zone": "Criar zona a partir da localização atual" }, @@ -851,7 +858,7 @@ }, "automation": { "caption": "Automação", - "description": "Criar e editar automações", + "description": "Gerir Automações", "editor": { "actions": { "add": "Adicionar ação", @@ -1357,7 +1364,7 @@ "caption": "Dispositivos", "confirm_delete": "Tem a certeza que quer apagar este dispositivo?", "confirm_rename_entity_ids": "Deseja também renomear os id's de entidade de suas entidades?", - "confirm_rename_entity_ids_warning": "Isso não mudará nenhuma configuração (como automações, scripts, cenas, Lovelace) que está usando essas entidades, você mesmo terá que atualizá-las.", + "confirm_rename_entity_ids_warning": "Tal não altera nenhuma configuração (como automações, scripts, cenas, Lovelace) que esteja a usar essas entidades, terá que atualizá-las por si.", "data_table": { "area": "Área", "battery": "Bateria", @@ -1720,9 +1727,15 @@ }, "network": { "header": "Gestão de Rede", - "introduction": "Gerenciar funções em toda a rede.", + "introduction": "Gerir funções de rede.", "node_count": "{count} nós" }, + "nodes_table": { + "id": "ID", + "manufacturer": "Fabricante", + "model": "Modelo", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "node_status": "Estado do Nó", "refreshing_description": "A atualizar as informações do nó ...", @@ -1733,7 +1746,7 @@ }, "select_instance": { "header": "Selecione uma instância OpenZWave", - "introduction": "Você tem mais de uma instância OpenZWave em execução. Qual instância você gostaria de gerenciar?" + "introduction": "Tem mais do que uma instância Openzwave em execução. Que instância deseja gerir?" }, "services": { "add_node": "Adicionar nó", @@ -1880,9 +1893,11 @@ "input_text": "Recarregar input texts", "introduction": "Algumas partes do Home Assistant podem ser recarregadas sem a necessidade de reiniciar. Ao carregar em Recarregar a configuração irá descartar a configuração atual e carregar a nova.", "min_max": "Recarregar entidades Mín. / Máx.", + "mqtt": "Recarregar entidades mqtt", "person": "Recarregar pessoas", "ping": "Recarregar entidades de sensor binárias de ping", - "rest": "Recarregar entidades REST", + "reload": "Recarregar {domain}", + "rest": "Recarregar as restantes entidades e notificar serviços", "scene": "Recarregar cenas", "script": "Recarregar scripts", "statistics": "Recarregar entidades estatísticas", @@ -2011,7 +2026,7 @@ "clusters": { "header": "Clusters", "help_cluster_dropdown": "Selecione um cluster para visualizar atributos e comandos.", - "introduction": "Clusters são os blocos de construção para a funcionalidade Zigbee. Eles separam a funcionalidade em unidades lógicas. Existem tipos de cliente e servidor e que são compostos de atributos e comandos." + "introduction": "Os Clusters são os blocos de construção da funcionalidade Zigbee. Eles separam a funcionalidade em unidades lógicas. Existem do tipo cliente e servidor e são compostos de atributos e comandos." }, "common": { "add_devices": "Adicionar dispositivos", @@ -2505,7 +2520,11 @@ } }, "cardpicker": { + "by_card": "Pelo Cartão", + "by_entity": "Pela Entidade", "custom_card": "Personalizado", + "domain": "Domínio", + "entity": "Entidade", "no_description": "Não há descrição disponível." }, "edit_card": { @@ -2519,6 +2538,7 @@ "options": "Mais opções", "pick_card": "Que cartão gostaria de adicionar?", "pick_card_view_title": "Que cartão você gostaria de adicionar à sua vista {name}?", + "search_cards": "Procurar cartões", "show_code_editor": "Mostrar Editor de Código", "show_visual_editor": "Mostrar Editor Visual", "toggle_editor": "Alternar Editor", @@ -2606,7 +2626,7 @@ }, "menu": { "close": "Fechar", - "configure_ui": "Configurar UI", + "configure_ui": "Configurar Painel", "exit_edit_mode": "Sair do modo de edição do IU", "help": "Ajuda", "refresh": "Atualizar", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 110d802140..1ecc5c2e62 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -563,6 +563,7 @@ "no_history_found": "История не найдена." }, "media-browser": { + "choose_player": "Выберите медиаплеер", "choose-source": "Выбрать источник", "content-type": { "album": "Альбом", @@ -571,12 +572,14 @@ "playlist": "Плейлист", "server": "Сервер" }, + "media_player": "Медиаплеер", "media-player-browser": "Браузер медиаплеера", "no_items": "Нет элементов", "pick": "Выбрать", "pick-media": "Выбрать Медиа", "play": "Воспроизведение", - "play-media": "Воспроизведение Медиа" + "play-media": "Воспроизведение Медиа", + "web-browser": "Веб-браузер" }, "picture-upload": { "label": "Изображение", @@ -618,7 +621,7 @@ "update": "Обновить" }, "domain_toggler": { - "reset_entities": "Сбросить объекты", + "reset_entities": "Сбросить настройки доступа объектов", "title": "Переключить домены" }, "entity_registry": { @@ -1204,11 +1207,15 @@ }, "alexa": { "banner": "Редактирование списка доступных объектов через пользовательский интерфейс отключено, так как Вы уже настроили фильтры в файле configuration.yaml.", + "dont_expose_entity": "Закрыть доступ", "expose": "Предоставить доступ", - "expose_entity": "Предоставить доступ к объекту", - "exposed_entities": "Объекты, к которым предоставлен доступ", + "expose_entity": "Открыть доступ", + "exposed": "Всего: {selected}", + "exposed_entities": "Объекты, к которым открыт доступ", + "follow_domain": "По домену", "manage_domains": "Управление доменами", - "not_exposed_entities": "Объекты, к которым не предоставлен доступ", + "not_exposed": "Всего: {selected}", + "not_exposed_entities": "Объекты, к которым закрыт доступ", "title": "Alexa" }, "caption": "Home Assistant Cloud", @@ -1245,9 +1252,15 @@ "google": { "banner": "Редактирование списка доступных объектов через пользовательский интерфейс отключено, так как Вы уже настроили фильтры в файле configuration.yaml.", "disable_2FA": "Отключить двухфакторную аутентификацию", + "dont_expose_entity": "Закрыть доступ", "expose": "Предоставить доступ", - "exposed_entities": "Объекты, к которым предоставлен доступ", - "not_exposed_entities": "Объекты, к которым не предоставлен доступ", + "expose_entity": "Открыть доступ", + "exposed": "Всего: {selected}", + "exposed_entities": "Объекты, к которым открыт доступ", + "follow_domain": "По домену", + "manage_domains": "Управление доменами", + "not_exposed": "Всего: {selected}", + "not_exposed_entities": "Объекты, к которым закрыт доступ", "sync_to_google": "Синхронизация изменений с Google.", "title": "Google Assistant" }, @@ -1755,8 +1768,21 @@ "versions": "Получение информации о версиях прошивки и классов команд", "wakeup": "Настройка поддержки очередей пробуждения и сообщений" }, + "node": { + "button": "Подробности об узле", + "not_found": "Узел не найден" + }, + "nodes_table": { + "failed": "Сбой", + "id": "ID", + "manufacturer": "Производитель", + "model": "Модель", + "query_stage": "Стадия запроса", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Если узел работает от батареи, обязательно разбудите его, прежде чем продолжить", + "button": "Обновить узел", "complete": "Обновление узла завершено", "description": "Повторный опрос узла и обновление классов команд, возможностей и значений узла.", "node_status": "Статус узла", @@ -2015,7 +2041,7 @@ "system": "Системный" } }, - "users_privileges_note": "Группа пользователей находится в стадии разработки. В дальнейшем пользователи не смогут администрировать сервер через пользовательский интерфейс. Мы все еще проверяем все конечные точки API управления, чтобы убедиться, что они правильно ограничивают доступ." + "users_privileges_note": "Функционал пользователей всё ещё в стадии разработки. В дальнейшем пользователи не смогут администрировать сервер через пользовательский интерфейс. Мы все еще проверяем все конечные точки API управления, чтобы убедиться, что они правильно ограничивают доступ." }, "zha": { "add_device_page": { @@ -2565,6 +2591,7 @@ "options": "Больше параметров", "pick_card": "Какую карточку Вы хотели бы добавить?", "pick_card_view_title": "Какую карточку Вы хотели бы добавить на вкладку {name}?", + "search_cards": "Поиск карточек", "show_code_editor": "Текстовый редактор", "show_visual_editor": "Форма ввода", "toggle_editor": "Переключить редактор", diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index 1e7b9bfa0a..525efe5654 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -563,6 +563,8 @@ "no_history_found": "没有找到历史状态。" }, "media-browser": { + "audio_not_supported": "您的浏览器不支持音频元素。", + "choose_player": "选择播放器", "choose-source": "选择媒体源", "content-type": { "album": "专辑", @@ -571,12 +573,16 @@ "playlist": "播放列表", "server": "服务器" }, + "media_not_supported": "浏览器媒体播放器不支持此类型的媒体", + "media_player": "媒体播放器", "media-player-browser": "媒体播放浏览器", "no_items": "没有项目", "pick": "选定", "pick-media": "选定媒体", "play": "播放", - "play-media": "播放媒体" + "play-media": "播放媒体", + "video_not_supported": "您的浏览器不支持视频元素。", + "web-browser": "网页浏览器" }, "picture-upload": { "label": "图片", @@ -1765,8 +1771,21 @@ "versions": "正在获取固件和命令类版本信息", "wakeup": "正在设置对唤醒队列和消息的支持" }, + "node": { + "button": "节点详细信息", + "not_found": "节点未找到" + }, + "nodes_table": { + "failed": "故障", + "id": "ID", + "manufacturer": "制造商", + "model": "型号", + "query_stage": "查询阶段", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "如果节点由电池供电,请确保在继续操作之前将其唤醒", + "button": "刷新节点", "complete": "节点刷新完成", "description": "这将通知 OpenZWave 重新访问节点并更新节点的命令类、功能和值。", "node_status": "节点状态", @@ -2575,6 +2594,7 @@ "options": "更多选项", "pick_card": "请选择要添加的卡片。", "pick_card_view_title": "您想将哪张卡片添加到 {name} 视图?", + "search_cards": "搜索卡片", "show_code_editor": "显示代码编辑器", "show_visual_editor": "显示可视化编辑器", "toggle_editor": "切换编辑器", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 025a931264..fd857908ae 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -563,6 +563,8 @@ "no_history_found": "找不到狀態歷史。" }, "media-browser": { + "audio_not_supported": "瀏覽器不支援音效元件。", + "choose_player": "選擇播放器", "choose-source": "選擇來源", "content-type": { "album": "專輯", @@ -571,12 +573,16 @@ "playlist": "播放列表", "server": "伺服器" }, + "media_not_supported": "瀏覽器媒體播放器不支援此類型媒體", + "media_player": "媒體播放器", "media-player-browser": "媒體播放器瀏覽器", "no_items": "沒有項目", "pick": "選擇", "pick-media": "選擇媒體", "play": "播放", - "play-media": "播放媒體" + "play-media": "播放媒體", + "video_not_supported": "瀏覽器不支援影片元件。", + "web-browser": "網頁瀏覽器" }, "picture-upload": { "label": "照片", @@ -1765,8 +1771,21 @@ "versions": "獲得韌體與命令 Class 版本資訊", "wakeup": "設定喚醒序列與訊息之支援" }, + "node": { + "button": "節點詳細資訊", + "not_found": "找不到節點" + }, + "nodes_table": { + "failed": "失敗", + "id": "ID", + "manufacturer": "廠牌", + "model": "型號", + "query_stage": "查詢階段", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "假如節點為電池供電、請確定先行喚醒以繼續", + "button": "更新節點", "complete": "節點更新完成", "description": "將會通知 OpenZWave 重新探訪節點並更新節點命令 Class、相容性與數值。", "node_status": "節點狀態", @@ -2025,7 +2044,7 @@ "system": "系統" } }, - "users_privileges_note": "使用者群組功能進行中。將無法透過 UI 進行使用者管理,仍在檢視所有管理 API Endpoint 以確保能夠正確符合管理員存取需求。" + "users_privileges_note": "使用者群組功能仍在開發中。將無法透過 UI 進行使用者管理,仍在檢視所有管理 API Endpoint 以確保能夠正確符合管理員存取需求。" }, "zha": { "add_device_page": { @@ -2575,6 +2594,7 @@ "options": "更多選項", "pick_card": "選擇所要新增的面板?", "pick_card_view_title": "要加入 {name} 視圖的面板?", + "search_cards": "搜尋面板", "show_code_editor": "顯示編碼編輯器", "show_visual_editor": "顯示視覺編輯器", "toggle_editor": "切換編輯器", From bd66bd6cf088a2c6a7f389202d7e680289dd332b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sun, 6 Sep 2020 18:29:22 +0200 Subject: [PATCH 021/146] Add color to hass-error-screen (#6803) --- src/layouts/hass-error-screen.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/hass-error-screen.ts b/src/layouts/hass-error-screen.ts index eab453b29e..8776184498 100644 --- a/src/layouts/hass-error-screen.ts +++ b/src/layouts/hass-error-screen.ts @@ -63,6 +63,7 @@ class HassErrorScreen extends LitElement { pointer-events: auto; } .content { + color: var(--primary-text-color); height: calc(100% - 64px); display: flex; align-items: center; From 76f59d99a22d085dcbdf9758963bea2283db7165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sun, 6 Sep 2020 18:29:39 +0200 Subject: [PATCH 022/146] Remove extra > from button (#6804) --- hassio/src/addon-view/config/hassio-addon-network.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hassio/src/addon-view/config/hassio-addon-network.ts b/hassio/src/addon-view/config/hassio-addon-network.ts index d90a68ffd3..99f871be99 100644 --- a/hassio/src/addon-view/config/hassio-addon-network.ts +++ b/hassio/src/addon-view/config/hassio-addon-network.ts @@ -88,8 +88,8 @@ class HassioAddonNetwork extends LitElement {
- Reset to defaults > + Reset to defaults + Save From 9aa8175e23eb33cb1d8ce720128b0f7fcaed8093 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sun, 6 Sep 2020 19:59:29 +0200 Subject: [PATCH 023/146] Update ha-logbook.ts --- src/panels/logbook/ha-logbook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 5bf0f92acf..1270db9619 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -216,7 +216,7 @@ class HaLogbook extends LitElement { display: flex; justify-content: center; flex-direction: column; - width: 65px; + width: 70px; flex-shrink: 0; font-size: 12px; color: var(--secondary-text-color); From 509481ef06029178412b25cf979d5250ddd01a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sun, 6 Sep 2020 20:29:02 +0200 Subject: [PATCH 024/146] Ignore more proxy disconnect codes (#6805) Co-authored-by: Bram Kragten --- hassio/src/dashboard/hassio-update.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index 3bb15444ed..9bb6cc73ec 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -164,8 +164,9 @@ export class HassioUpdate extends LitElement { try { await this.hass.callApi>("POST", item.apiPath); } catch (err) { - // Only show an error if the status code was not 504, or no status at all (connection terminated) - if (err.status_code && err.status_code !== 504) { + // Only show an error if the status code was not expected (user behind proxy) + // or no status at all(connection terminated) + if (err.status_code && ![502, 503, 504].includes(err.status_code)) { showAlertDialog(this, { title: "Update failed", text: extractApiErrorMessage(err), From cfa0c452132847b8488cdc0f91403be4703fecf3 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Sun, 6 Sep 2020 22:17:34 +0200 Subject: [PATCH 025/146] Fix media browser panel title + selection header color (#6807) --- .../hui-dialog-select-media-player.ts | 22 +++++++----- .../hui-dialog-web-browser-play-media.ts | 36 +++++++++---------- src/translations/en.json | 1 + 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/panels/media-browser/hui-dialog-select-media-player.ts b/src/panels/media-browser/hui-dialog-select-media-player.ts index bacce8505e..0c45026e81 100644 --- a/src/panels/media-browser/hui-dialog-select-media-player.ts +++ b/src/panels/media-browser/hui-dialog-select-media-player.ts @@ -13,6 +13,7 @@ import { fireEvent } from "../../common/dom/fire_event"; import { createCloseHeading } from "../../components/ha-dialog"; import { BROWSER_SOURCE } from "../../data/media-player"; import type { HomeAssistant } from "../../types"; +import { haStyleDialog } from "../../resources/styles"; import type { SelectMediaPlayerDialogParams } from "./show-select-media-source-dialog"; @customElement("hui-dialog-select-media-player") @@ -74,15 +75,18 @@ export class HuiDialogSelectMediaPlayer extends LitElement { this.closeDialog(); } - static get styles(): CSSResult { - return css` - ha-dialog { - --dialog-content-padding: 0 24px 20px; - } - paper-item { - cursor: pointer; - } - `; + static get styles(): CSSResult[] { + return [ + haStyleDialog, + css` + ha-dialog { + --dialog-content-padding: 0 24px 20px; + } + paper-item { + cursor: pointer; + } + `, + ]; } } diff --git a/src/panels/media-browser/hui-dialog-web-browser-play-media.ts b/src/panels/media-browser/hui-dialog-web-browser-play-media.ts index 05fbea66d6..d528bec1e4 100644 --- a/src/panels/media-browser/hui-dialog-web-browser-play-media.ts +++ b/src/panels/media-browser/hui-dialog-web-browser-play-media.ts @@ -11,6 +11,7 @@ import { fireEvent } from "../../common/dom/fire_event"; import { createCloseHeading } from "../../components/ha-dialog"; import "../../components/ha-hls-player"; import type { HomeAssistant } from "../../types"; +import { haStyleDialog } from "../../resources/styles"; import { WebBrowserPlayMediaDialogParams } from "./show-media-player-dialog"; @customElement("hui-dialog-web-browser-play-media") @@ -92,26 +93,25 @@ export class HuiDialogWebBrowserPlayMedia extends LitElement { `; } - static get styles(): CSSResult { - return css` - ha-dialog { - --mdc-dialog-heading-ink-color: var(--primary-text-color); - } - - @media (min-width: 800px) { - ha-dialog { - --mdc-dialog-max-width: 800px; - --mdc-dialog-min-width: 400px; + static get styles(): CSSResult[] { + return [ + haStyleDialog, + css` + @media (min-width: 800px) { + ha-dialog { + --mdc-dialog-max-width: 800px; + --mdc-dialog-min-width: 400px; + } } - } - video, - audio, - img { - outline: none; - width: 100%; - } - `; + video, + audio, + img { + outline: none; + width: 100%; + } + `, + ]; } } diff --git a/src/translations/en.json b/src/translations/en.json index 8e4ced5344..436dd97a9e 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -9,6 +9,7 @@ "mailbox": "Mailbox", "shopping_list": "Shopping list", "developer_tools": "Developer Tools", + "media_browser": "Media Browser", "profile": "Profile" }, "state": { From a37aad18b773e41d859e8f5645e7b6beb8e97d1d Mon Sep 17 00:00:00 2001 From: Sean Mooney Date: Sun, 6 Sep 2020 17:53:00 -0400 Subject: [PATCH 026/146] Minor typo fix (#6809) Unkown = Unknown --- src/data/hassio/common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/hassio/common.ts b/src/data/hassio/common.ts index b3681d72a8..4ab432b855 100644 --- a/src/data/hassio/common.ts +++ b/src/data/hassio/common.ts @@ -9,7 +9,7 @@ export const hassioApiResultExtractor = (response: HassioResponse) => export const extractApiErrorMessage = (error: any): string => { return typeof error === "object" ? typeof error.body === "object" - ? error.body.message || "Unkown error, see logs" - : error.body || "Unkown error, see logs" + ? error.body.message || "Unknown error, see logs" + : error.body || "Unknown error, see logs" : error; }; From efe8eca4e39ead38cff7ec53d4f2cd4985e292ff Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Sun, 6 Sep 2020 18:28:15 -0500 Subject: [PATCH 027/146] Media browser updates (#6801) --- src/components/ha-dialog.ts | 1 + .../dialog-media-player-browse.ts | 5 +- .../media-player/ha-media-player-browse.ts | 86 +++++++++++++------ src/translations/en.json | 1 + 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index 4459efb5b3..8c34add0e3 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -64,6 +64,7 @@ export class HaDialog extends MwcDialog { } .mdc-dialog .mdc-dialog__surface { position: var(--dialog-surface-position, relative); + top: var(--dialog-surface-top); min-height: var(--mdc-dialog-min-height, auto); } :host([flexContent]) .mdc-dialog .mdc-dialog__content { diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts index 9f0c07436f..311536844f 100644 --- a/src/components/media-player/dialog-media-player-browse.ts +++ b/src/components/media-player/dialog-media-player-browse.ts @@ -43,7 +43,7 @@ class DialogMediaPlayerBrowse extends LitElement { public closeDialog() { this._params = undefined; - fireEvent(this, "dialog-closed", {dialog: this.localName}); + fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render(): TemplateResult { @@ -93,6 +93,9 @@ class DialogMediaPlayerBrowse extends LitElement { @media (min-width: 800px) { ha-dialog { --mdc-dialog-max-width: 800px; + --dialog-surface-position: fixed; + --dialog-surface-top: 40px; + --mdc-dialog-max-height: calc(100% - 72px); } ha-media-player-browse { width: 700px; diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 5b3d637812..df10572558 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -18,8 +18,10 @@ import { } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { ifDefined } from "lit-html/directives/if-defined"; +import { styleMap } from "lit-html/directives/style-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; +import { compare } from "../../common/string/compare"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; import { @@ -30,6 +32,7 @@ import { MediaPlayerBrowseAction, } from "../../data/media-player"; import type { MediaPlayerItem } from "../../data/media-player"; +import { showAlertDialog } from "../../dialogs/generic/show-dialog-box"; import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer"; import { haStyle } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; @@ -130,7 +133,11 @@ export class HaMediaPlayerBrowse extends LitElement { ? html`
${this._narrow && currentItem?.can_play ? html` @@ -218,12 +225,16 @@ export class HaMediaPlayerBrowse extends LitElement {
${child.can_expand && !child.thumbnail ? html` @@ -333,6 +344,10 @@ export class HaMediaPlayerBrowse extends LitElement { this._fetchData(this.mediaContentId, this.mediaContentType).then( (itemData) => { + if (!itemData) { + return; + } + this._mediaPlayerItems = [itemData]; } ); @@ -349,13 +364,19 @@ export class HaMediaPlayerBrowse extends LitElement { fireEvent(this, "media-picked", { item }); } - private async _navigateForward(ev: MouseEvent): Promise { + private async _childClicked(ev: MouseEvent): Promise { const target = ev.currentTarget as any; const item: MediaPlayerItem = target.item; if (!item) { return; } + + if (!item.can_expand) { + this._runAction(item); + return; + } + this._navigate(item); } @@ -365,6 +386,10 @@ export class HaMediaPlayerBrowse extends LitElement { item.media_content_type ); + if (!itemData) { + return; + } + this.scrollTo(0, 0); this._mediaPlayerItems = [...this._mediaPlayerItems, itemData]; } @@ -372,16 +397,33 @@ export class HaMediaPlayerBrowse extends LitElement { private async _fetchData( mediaContentId?: string, mediaContentType?: string - ): Promise { - const itemData = - this.entityId !== BROWSER_SOURCE - ? await browseMediaPlayer( - this.hass, - this.entityId, - mediaContentId, - mediaContentType - ) - : await browseLocalMediaPlayer(this.hass, mediaContentId); + ): Promise { + let itemData: MediaPlayerItem | undefined; + try { + itemData = + this.entityId !== BROWSER_SOURCE + ? await browseMediaPlayer( + this.hass, + this.entityId, + mediaContentId, + mediaContentType + ) + : await browseLocalMediaPlayer(this.hass, mediaContentId); + itemData.children = itemData.children?.sort((first, second) => + !first.can_expand && second.can_expand + ? 1 + : first.can_expand && !second.can_expand + ? -1 + : compare(first.title, second.title) + ); + } catch (error) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.components.media-browser.media_browsing_error" + ), + text: error.message, + }); + } return itemData; } @@ -435,12 +477,6 @@ export class HaMediaPlayerBrowse extends LitElement { border-bottom: 1px solid var(--divider-color); } - .header_button { - position: relative; - top: 14px; - right: -8px; - } - .header { background-color: var(--card-background-color); position: sticky; @@ -539,9 +575,6 @@ export class HaMediaPlayerBrowse extends LitElement { ); grid-gap: 16px; margin: 8px 0px; - } - - :host(:not([narrow])) .children { padding: 0px 24px; } @@ -683,8 +716,7 @@ export class HaMediaPlayerBrowse extends LitElement { padding: 20px 24px 10px; } - :host([narrow]) .media-source, - :host([narrow]) .children { + :host([narrow]) .media-source { padding: 0 24px; } @@ -703,8 +735,8 @@ export class HaMediaPlayerBrowse extends LitElement { -webkit-line-clamp: 1; } - :host(:not([narrow])[scroll]) .header-info { - height: 75px; + :host(:not([narrow])[scroll]) .header:not(.no-img) mwc-icon-button { + align-self: center; } :host([scroll]) .header-info mwc-button, diff --git a/src/translations/en.json b/src/translations/en.json index 436dd97a9e..cd234428f5 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -365,6 +365,7 @@ "audio_not_supported": "Your browser does not support the audio element.", "video_not_supported": "Your browser does not support the video element.", "media_not_supported": "The Browser Media Player does not support this type of media", + "media_browsing_error": "Media Browsing Error", "content-type": { "server": "Server", "library": "Library", From c73330a466689e52dda643195ca7d22d9e4d8caf Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Mon, 7 Sep 2020 00:32:51 +0000 Subject: [PATCH 028/146] [ci skip] Translation update --- translations/frontend/cs.json | 1 + translations/frontend/en.json | 2 ++ translations/frontend/fr.json | 24 +++++++++---------- translations/frontend/pl.json | 37 ++++++++++++++++++++++++++++-- translations/frontend/pt.json | 6 +++++ translations/frontend/ru.json | 5 ++-- translations/frontend/sk.json | 21 +++++++++++++++++ translations/frontend/zh-Hant.json | 1 + 8 files changed, 81 insertions(+), 16 deletions(-) diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index b8ec36694b..cea97f6d86 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -1552,6 +1552,7 @@ "reload_restart_confirm": "Restartujte Home Assistant pro nové načtení této integrace", "rename": "Přejmenovat", "restart_confirm": "Restartujte Home Assistant pro odstranění této integrace", + "services": "{count} {count, plural,\n one {služba}\n few {služby}\n other {služeb}\n}", "settings_button": "Upravit nastavení pro {integration}", "system_options": "Více možností", "system_options_button": "Upravit nastavení pro {integration}", diff --git a/translations/frontend/en.json b/translations/frontend/en.json index e320c6c31e..2eac397f6b 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -19,6 +19,7 @@ "logbook": "Logbook", "mailbox": "Mailbox", "map": "Map", + "media_browser": "Media Browser", "profile": "Profile", "shopping_list": "Shopping list", "states": "Overview" @@ -573,6 +574,7 @@ "playlist": "Playlist", "server": "Server" }, + "media_browsing_error": "Media Browsing Error", "media_not_supported": "The Browser Media Player does not support this type of media", "media_player": "Media Player", "media-player-browser": "Media Player Browser", diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 86646023b1..40a60cb610 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -79,7 +79,7 @@ "default": { "entity_not_found": "Entité introuvable", "error": "Erreur", - "unavailable": "Indisponible", + "unavailable": "Indispo.", "unknown": "Inconnu" }, "device_tracker": { @@ -170,7 +170,7 @@ "on": "Problème" }, "safety": { - "off": "Sécurisé", + "off": "Sûr", "on": "Dangereux" }, "smoke": { @@ -318,7 +318,7 @@ "fog": "Brouillard", "hail": "Grêle", "lightning": "Orage", - "lightning-rainy": "Orage / Pluie", + "lightning-rainy": "Orage / Pluvieux", "partlycloudy": "Partiellement nuageux", "pouring": "Averses", "rainy": "Pluvieux", @@ -351,7 +351,7 @@ "alarm_control_panel": { "arm_away": "Armer (absent)", "arm_custom_bypass": "Bypass personnalisé", - "arm_home": "Armer (domicile)", + "arm_home": "Armer (présent)", "arm_night": "Armer nuit", "clear_code": "Effacer", "code": "Code", @@ -1451,7 +1451,7 @@ "name": "Nom", "status": "Statut" }, - "introduction": "Home Assistant tient un registre de chaque entité qu'il a déjà vu au moins une fois et qui peut être identifié de manière unique. Chacune de ces entités se verra attribuer un identifiant qui sera réservé à cette seule entité.", + "introduction": "Home Assistant tient un registre de chaque entité qu'il a déjà vu au moins une fois et qui peut être identifiée de manière unique. Chacune de ces entités se verra attribuer un identifiant qui sera réservé à cette seule entité.", "introduction2": "Utilisé le registre des entités pour remplacer le nom, modifier l'ID de l'entité ou supprimer l'entrée de Home Assistant.", "remove_selected": { "button": "Supprimer la sélection", @@ -1543,7 +1543,7 @@ "firmware": "Firmware: {version}", "hub": "Connecté via", "manuf": "par {manufacturer}", - "no_area": "Pas de pièce", + "no_area": "Pas de zone", "no_device": "Entités sans appareils", "no_devices": "Cette intégration n'a pas d'appareils.", "options": "Options", @@ -1749,7 +1749,7 @@ }, "network": { "header": "Gestion du réseau", - "introduction": "Διαχείρηση λειτουργιών δικτύου", + "introduction": "Gérez les fonctions de niveau réseau.", "node_count": "{count} nœuds" }, "node_query_stages": { @@ -1934,7 +1934,7 @@ "filter": "Recharger les entités de filtre", "generic": "Recharger les entités de caméra IP générique", "generic_thermostat": "Recharger les entités de thermostat générique", - "group": "Recharger les groupes", + "group": "Recharger les groupes, les entités de groupe et notifier les services", "heading": "Rechargement de la configuration", "history_stats": "Recharger les entités des statistiques historiques", "homekit": "Recharger HomeKit", @@ -2082,7 +2082,7 @@ "clusters": { "header": "Clusters", "help_cluster_dropdown": "Sélectionnez un cluster pour afficher les attributs et les commandes.", - "introduction": "Les grappes sont les éléments de construction de la fonctionnalité Zigbee. Ils séparent les fonctionnalités en unités logiques. Il en existe de type clients et serveurs qui sont composés d'attributs et de commandes." + "introduction": "Les grappes sont les éléments de construction de la fonctionnalité Zigbee. Ils séparent les fonctionnalités en unités logiques. Il en existe de types client et serveur et sont composés d'attributs et de commandes." }, "common": { "add_devices": "Ajouter des appareils", @@ -2523,7 +2523,7 @@ }, "markdown": { "content": "Contenu", - "description": "La carte Markdown est utilisée pour afficher du Markdown.", + "description": "La carte Markdown est utilisée pour le rendu du Markdown.", "name": "Markdown" }, "media-control": { @@ -2797,7 +2797,7 @@ "data": { "code": "Code d'authentification à deux facteurs" }, - "description": "Ouvrez le **{mfa_module_name}** sur votre appareil pour afficher votre code d'authentification à deux facteurs et vérifier votre identité:" + "description": "Ouvrez le ** {mfa_module_name} ** sur votre appareil pour afficher votre code d'authentification à deux facteurs et confirmer votre identité:" } } }, @@ -2817,7 +2817,7 @@ } }, "start_over": "Recommencer", - "unknown_error": "Quelque chose a mal tourné", + "unknown_error": "Un problème est survenu", "working": "Veuillez patienter" }, "initializing": "Initialisation", diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index 901952d6c0..cb96ebd163 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -505,6 +505,7 @@ "back": "Wstecz", "cancel": "Anuluj", "close": "Zamknij", + "continue": "Kontynuuj", "delete": "Usuń", "error_required": "To pole jest wymagane", "loading": "Ładowanie", @@ -562,6 +563,8 @@ "no_history_found": "Nie znaleziono historii." }, "media-browser": { + "audio_not_supported": "Twoja przeglądarka nie obsługuje elementu audio.", + "choose_player": "Wybierz odtwarzacz", "choose-source": "Wybierz źródło", "content-type": { "album": "Album", @@ -570,12 +573,16 @@ "playlist": "Lista odtwarzania", "server": "Serwer" }, + "media_not_supported": "Przeglądarka odtwarzacza mediów nie obsługuje tego typu mediów", + "media_player": "Odtwarzacz mediów", "media-player-browser": "Przeglądarka odtwarzacza mediów", "no_items": "Brak elementów", "pick": "Wybierz", "pick-media": "Wybierz media", "play": "Odtwarzaj", - "play-media": "Odtwarzaj media" + "play-media": "Odtwarzaj media", + "video_not_supported": "Twoja przeglądarka nie obsługuje elementu wideo.", + "web-browser": "Przeglądarka internetowa" }, "picture-upload": { "label": "Obraz", @@ -689,8 +696,10 @@ "crop": "Przytnij" }, "more_info_control": { + "controls": "Sterowanie", "dismiss": "Zamknij okno dialogowe", "edit": "Edytuj encję", + "history": "Historia", "person": { "create_zone": "Utwórz strefę z bieżącej lokalizacji" }, @@ -1543,6 +1552,7 @@ "reload_restart_confirm": "Uruchom ponownie Home Assistanta, aby dokończyć ponowne wczytywanie tej integracji", "rename": "Zmień nazwę", "restart_confirm": "Zrestartuj Home Assistanta, aby zakończyć usuwanie tej integracji", + "services": "{count} {count, plural,\n one {usługa}\n few {usługi}\n many {usług}\n other {usług}\n}", "settings_button": "Edytuj ustawienia dla {integration}", "system_options": "Opcje systemowe", "system_options_button": "Opcje systemowe dla {integration}", @@ -1746,7 +1756,7 @@ "node_query_stages": { "associations": "Odświeżanie grup skojarzeń i członkostwa", "cacheload": "Ładowanie informacji z pliku pamięci podręcznej OpenZWave. Węzły baterii pozostaną na tym etapie, dopóki węzeł się nie wybudzi.", - "complete": "Proces wywiadu jest zakończony", + "complete": "Proces odpytywania jest zakończony", "configuration": "Pobieranie wartości konfiguracyjnych z węzła", "dynamic": "Pobieranie często zmieniających się wartości z węzła", "instances": "Pobieranie szczegółowych informacji o instancjach lub kanałach obsługiwanych przez urządzenie", @@ -1762,8 +1772,21 @@ "versions": "Pobieranie informacji o wersjach oprogramowania i klas poleceń", "wakeup": "Konfigurowanie obsługi kolejek wybudzania i wiadomości" }, + "node": { + "button": "Szczegóły węzła", + "not_found": "Nie znaleziono węzła" + }, + "nodes_table": { + "failed": "Uszkodzony", + "id": "Identyfikator", + "manufacturer": "Producent", + "model": "Model", + "query_stage": "Etap odpytywania", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Jeśli węzeł jest zasilany bateryjnie, przed kontynuowaniem należy go wybudzić", + "button": "Odśwież węzeł", "complete": "Odświeżanie węzła zakończone", "description": "Poinformuje to OpenZWave o konieczności ponownego odpytania węzła i zaktualizowaniu jego klas poleceń, możliwości i wartości.", "node_status": "Stan węzła", @@ -1923,12 +1946,17 @@ "input_text": "Pomocnicy typu tekst", "introduction": "Niektóre fragmenty konfiguracji można przeładować bez ponownego uruchamiania. Poniższe przyciski pozwalają na ponowne wczytanie danej części konfiguracji YAML.", "min_max": "Encje komponentu min/max", + "mqtt": "Encje komponentu MQTT", "person": "Osoby", "ping": "Encje komponentu ping", + "reload": "Domenę {domain}", "rest": "Encje komponentu rest", + "rpi_gpio": "Encje komponentu Raspberry Pi GPIO", "scene": "Sceny", "script": "Skrypty", + "smtp": "Usługi powiadomień komponentu SMTP", "statistics": "Encje komponentu statystyka", + "telegram": "Usługi powiadomień komponentu Telegram", "template": "Szablony encji", "trend": "Encje komponentu trend", "universal": "Encje komponentu uniwersalny odtwarzacz mediów", @@ -2549,7 +2577,11 @@ } }, "cardpicker": { + "by_card": "Według karty", + "by_entity": "Według encji", "custom_card": "Niestandardowa", + "domain": "Domena", + "entity": "Encja", "no_description": "Brak dostępnego opisu." }, "edit_card": { @@ -2563,6 +2595,7 @@ "options": "Więcej opcji", "pick_card": "Wybierz kartę, którą chcesz dodać.", "pick_card_view_title": "Którą kartę chcesz dodać do widoku {name}?", + "search_cards": "Szukaj kart", "show_code_editor": "Edytor kodu", "show_visual_editor": "Edytor wizualny", "toggle_editor": "Przełącz edytor", diff --git a/translations/frontend/pt.json b/translations/frontend/pt.json index 96cf3681a2..302d98036b 100644 --- a/translations/frontend/pt.json +++ b/translations/frontend/pt.json @@ -1730,13 +1730,19 @@ "introduction": "Gerir funções de rede.", "node_count": "{count} nós" }, + "node": { + "button": "Detalhes do nó", + "not_found": "Nó não encontrado" + }, "nodes_table": { + "failed": "Falhou", "id": "ID", "manufacturer": "Fabricante", "model": "Modelo", "zwave_plus": "Z-Wave Plus" }, "refresh_node": { + "button": "Atualizar nó", "node_status": "Estado do Nó", "refreshing_description": "A atualizar as informações do nó ...", "start_refresh_button": "Iniciar atualização", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 1ecc5c2e62..8bc907ffb4 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -1549,6 +1549,7 @@ "reload_restart_confirm": "Перезапустите Home Assistant, чтобы завершить перезагрузку этой интеграции", "rename": "Переименовать", "restart_confirm": "Перезапустите Home Assistant, чтобы завершить удаление этой интеграции", + "services": "{count} {count, plural,\n one {служба}\n other {служб}\n}", "settings_button": "Настройки интеграции {integration}", "system_options": "Настройки интеграции", "system_options_button": "Системные параметры интеграции {integration}", @@ -1595,7 +1596,7 @@ "none_found_detail": "Измените критерии поиска", "note_about_integrations": "Пока что не все интеграции могут быть настроены через пользовательский интерфейс.", "note_about_website_reference": "Все доступные интеграции Вы можете найти на ", - "rename_dialog": "Изменение названия интеграции", + "rename_dialog": "Название интеграции", "rename_input_label": "Название", "search": "Поиск интеграций" }, @@ -2877,7 +2878,7 @@ }, "integration": { "finish": "Готово", - "intro": "Устройства и сервисы представлены в Home Assistant как интеграции. Вы можете добавить их сейчас или сделать это позже в разделе настроек.", + "intro": "Устройства и службы представлены в Home Assistant как интеграции. Вы можете добавить их сейчас или сделать это позже в разделе настроек.", "more_integrations": "Ещё" }, "intro": "Готовы ли Вы разбудить свой дом, вернуть свою конфиденциальность и присоединиться к всемирному сообществу?", diff --git a/translations/frontend/sk.json b/translations/frontend/sk.json index 6e865645c7..9e3bacaab7 100644 --- a/translations/frontend/sk.json +++ b/translations/frontend/sk.json @@ -484,6 +484,7 @@ "back": "Späť", "cancel": "Zrušiť", "close": "Zavrieť", + "continue": "Pokračovať", "delete": "Odstrániť", "loading": "Načítava sa", "next": "Ďalej", @@ -533,6 +534,11 @@ "loading_history": "Načítavam históriu stavov", "no_history_found": "Nenašla sa žiadna história stavov" }, + "media-browser": { + "choose_player": "Vyberte prehrávač", + "media_player": "Prehrávač médií", + "web-browser": "Webový prehliadač" + }, "related-items": { "area": "Oblasť", "automation": "Súčasťou nasledujúcich automatizácií", @@ -635,6 +641,7 @@ "more_info_control": { "dismiss": "Zrušiť dialógové okno", "edit": "Upraviť entitu", + "history": "História", "person": { "create_zone": "Vytvoriť zónu z aktuálnej polohy" }, @@ -1534,6 +1541,11 @@ "title": "MQTT", "topic": "téma" }, + "ozw": { + "nodes_table": { + "id": "ID" + } + }, "person": { "add_person": "Pridať osobu", "caption": "Osoby", @@ -1683,6 +1695,8 @@ "create": "Vytvoriť", "name": "Meno", "password": "Heslo", + "password_confirm": "Potvrdiť heslo", + "password_not_match": "Heslá sa nezhodujú", "username": "Užívateľské meno" }, "caption": "Používatelia", @@ -1699,7 +1713,9 @@ "group": "Skupina", "id": "ID", "name": "Názov", + "new_password": "Nové heslo", "owner": "Vlastník", + "password_changed": "Heslo je zmenené!", "system_generated": "Systémom vytvorený", "system_generated_users_not_editable": "Nie je možné aktualizovať používateľov generovaných systémom.", "system_generated_users_not_removable": "Nie je možné odstrániť používateľov generovaných systémom.", @@ -1872,6 +1888,10 @@ "set_wakeup": "Nastaviť interval prebudenia", "true": "True" }, + "node_management": { + "add_to_group": "Pridať do skupiny", + "remove_from_group": "Odstrániť zo skupiny" + }, "ozw_log": { "introduction": "Zobraziť denník. 0 je minimum (načíta celý protokol) a 1000 je maximum. Načítanie zobrazí statický protokol a posledný riadok sa automaticky aktualizuje s určeným počtom riadkov protokolu." }, @@ -2176,6 +2196,7 @@ "options": "Viac možností", "pick_card": "Ktorú kartu chcete pridať?", "pick_card_view_title": "Ktorú kartu chcete pridať do svojho zobrazenia {name} ?", + "search_cards": "Vyhľadať karty", "show_code_editor": "Zobraziť editor kódu", "show_visual_editor": "Zobraziť vizuálny editor", "toggle_editor": "Prepnúť editor" diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index fd857908ae..a805c1b832 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -1552,6 +1552,7 @@ "reload_restart_confirm": "重啟 Home Assistant 以為重整合重新載入", "rename": "重新命名", "restart_confirm": "重啟 Home Assistant 以完成此整合移動", + "services": "{count} {count, plural,\n one {項服務}\n other {項服務}\n}", "settings_button": "編輯 {integration} 設定", "system_options": "系統選項", "system_options_button": "{integration} 系統選項", From 979b7ae6518c01be2c3c78b76fd5f9bb36efbf8f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Sep 2020 09:09:42 +0200 Subject: [PATCH 029/146] Add attention required for config flows in progress (#6808) --- src/data/config_flow.ts | 2 + .../integrations/ha-config-integrations.ts | 119 +++++++++++------- src/translations/en.json | 2 + 3 files changed, 78 insertions(+), 45 deletions(-) diff --git a/src/data/config_flow.ts b/src/data/config_flow.ts index a06f8f42ec..94cce83ba8 100644 --- a/src/data/config_flow.ts +++ b/src/data/config_flow.ts @@ -13,6 +13,8 @@ export const DISCOVERY_SOURCES = [ "discovery", ]; +export const ATTENTION_SOURCES = ["reauth"]; + export const createConfigFlow = (hass: HomeAssistant, handler: string) => hass.callApi("POST", "config/config_entries/flow", { handler, diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index dd738133f7..99d7f6b266 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -10,12 +10,13 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import memoizeOne from "memoize-one"; import { HASSDomEvent } from "../../../common/dom/fire_event"; import "../../../common/search/search-input"; @@ -32,6 +33,7 @@ import { getConfigEntries, } from "../../../data/config_entries"; import { + ATTENTION_SOURCES, DISCOVERY_SOURCES, getConfigFlowInProgressCollection, ignoreConfigFlow, @@ -355,52 +357,67 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { : ""} ${configEntriesInProgress.length ? configEntriesInProgress.map( - (flow: DataEntryFlowProgressExtended) => html` - -
- ${this.hass.localize( - "ui.panel.config.integrations.discovered" - )} -
-
-
- + (flow: DataEntryFlowProgressExtended) => { + const attention = ATTENTION_SOURCES.includes( + flow.context.source + ); + return html` + +
+ ${this.hass.localize( + `ui.panel.config.integrations.${ + attention ? "attention" : "discovered" + }` + )}
-

- ${flow.localized_title} -

-
- - ${this.hass.localize( - "ui.panel.config.integrations.configure" - )} - - ${DISCOVERY_SOURCES.includes(flow.context.source) && - flow.context.unique_id - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.ignore.ignore" - )} - - ` - : ""} +
+
+ +
+

+ ${flow.localized_title} +

+
+ + ${this.hass.localize( + `ui.panel.config.integrations.${ + attention ? "reconfigure" : "configure" + }` + )} + + ${DISCOVERY_SOURCES.includes(flow.context.source) && + flow.context.unique_id + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.ignore.ignore" + )} + + ` + : ""} +
-
-
- ` + + `; + } ) : ""} ${groupedConfigEntries.size @@ -639,6 +656,18 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { flex-direction: column; justify-content: space-between; } + .attention { + --ha-card-border-color: var(--error-color); + } + .attention .header { + background: var(--error-color); + color: var(--text-primary-color); + padding: 8px; + text-align: center; + } + .attention mwc-button { + --mdc-theme-primary: var(--error-color); + } .discovered { --ha-card-border-color: var(--primary-color); } diff --git a/src/translations/en.json b/src/translations/en.json index cd234428f5..434e67cb0c 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1610,6 +1610,7 @@ "description": "Manage integrations", "integration": "integration", "discovered": "Discovered", + "attention": "Attention required", "configured": "Configured", "new": "Set up a new integration", "add_integration": "Add integration", @@ -1618,6 +1619,7 @@ "note_about_website_reference": "More are available on the ", "home_assistant_website": "Home Assistant website", "configure": "Configure", + "reconfigure": "Reconfigure", "none": "Nothing configured yet", "none_found": "No integrations found", "none_found_detail": "Adjust your search criteria.", From d5bc498373fc9822789b30ae6d54d948eb8d4a06 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Sep 2020 13:41:59 +0200 Subject: [PATCH 030/146] Fix action handler bugs (#6811) --- src/components/ha-sidebar.ts | 6 ++++ .../directives/action-handler-directive.ts | 32 +++++++++---------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 12b1c17d39..2f6c72bb4b 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -31,6 +31,7 @@ import memoizeOne from "memoize-one"; import { LocalStorage } from "../common/decorators/local-storage"; import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; +import { navigate } from "../common/navigate"; import { compare } from "../common/string/compare"; import { computeRTL } from "../common/util/compute_rtl"; import { ActionHandlerDetail } from "../data/lovelace"; @@ -649,6 +650,10 @@ class HaSidebar extends LitElement { ); } + private _handlePanelTap(ev: Event) { + navigate(this, (ev.currentTarget as HTMLAnchorElement).href); + } + private _renderPanel( urlPath: string, title: string | null, @@ -661,6 +666,7 @@ class HaSidebar extends LitElement { href="${`/${urlPath}`}" data-panel="${urlPath}" tabindex="-1" + @tap=${this._handlePanelTap} @mouseenter=${this._itemMouseEnter} @mouseleave=${this._itemMouseLeave} > diff --git a/src/panels/lovelace/common/directives/action-handler-directive.ts b/src/panels/lovelace/common/directives/action-handler-directive.ts index 09dc3c4351..0c245a4764 100644 --- a/src/panels/lovelace/common/directives/action-handler-directive.ts +++ b/src/panels/lovelace/common/directives/action-handler-directive.ts @@ -44,6 +44,8 @@ class ActionHandler extends HTMLElement implements ActionHandler { protected held = false; + private cancelled = false; + private dblClickTimeout?: number; constructor() { @@ -76,9 +78,12 @@ class ActionHandler extends HTMLElement implements ActionHandler { document.addEventListener( ev, () => { - clearTimeout(this.timer); - this.stopAnimation(); - this.timer = undefined; + this.cancelled = true; + if (this.timer) { + this.stopAnimation(); + clearTimeout(this.timer); + this.timer = undefined; + } }, { passive: true } ); @@ -124,7 +129,7 @@ class ActionHandler extends HTMLElement implements ActionHandler { } element.actionHandler.start = (ev: Event) => { - this.held = false; + this.cancelled = false; let x; let y; if ((ev as TouchEvent).touches) { @@ -136,6 +141,7 @@ class ActionHandler extends HTMLElement implements ActionHandler { } if (options.hasHold) { + this.held = false; this.timer = window.setTimeout(() => { this.startAnimation(x, y); this.held = true; @@ -144,24 +150,20 @@ class ActionHandler extends HTMLElement implements ActionHandler { }; element.actionHandler.end = (ev: Event) => { - // Don't respond on our own generated click - if (!ev.isTrusted) { + // Don't respond when moved or scrolled while touch + if (["touchend", "touchcancel"].includes(ev.type) && this.cancelled) { return; } // Prevent mouse event if touch event - ev.preventDefault(); + if (ev.cancelable) { + ev.preventDefault(); + } if (options.hasHold) { - if ( - ["touchend", "touchcancel"].includes(ev.type) && - this.timer === undefined - ) { - return; - } clearTimeout(this.timer); this.stopAnimation(); this.timer = undefined; } - if (this.held) { + if (options.hasHold && this.held) { fireEvent(element, "action", { action: "hold" }); } else if (options.hasDoubleClick) { if ( @@ -179,8 +181,6 @@ class ActionHandler extends HTMLElement implements ActionHandler { } } else { fireEvent(element, "action", { action: "tap" }); - // Fire the click we prevented the action for - (ev.target as HTMLElement)?.click(); } }; From bb2462483e289dc2cce8a7378b9f0f77cc88450b Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Mon, 7 Sep 2020 06:47:24 -0500 Subject: [PATCH 031/146] Use Sortable to move entities in entities editor (#6810) --- package.json | 1 + src/components/ha-sidebar-sort-styles.ts | 77 -------- src/components/ha-sidebar.ts | 15 +- .../lovelace/components/hui-entity-editor.ts | 176 +++++++++++------- src/resources/ha-sortable-style.ts | 75 ++++++++ yarn.lock | 5 + 6 files changed, 204 insertions(+), 145 deletions(-) delete mode 100644 src/components/ha-sidebar-sort-styles.ts create mode 100644 src/resources/ha-sortable-style.ts diff --git a/package.json b/package.json index 1410d6bf3b..1db467294c 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "@polymer/polymer": "3.1.0", "@thomasloven/round-slider": "0.5.0", "@types/chromecast-caf-sender": "^1.0.3", + "@types/sortablejs": "^1.10.6", "@vaadin/vaadin-combo-box": "^5.0.10", "@vaadin/vaadin-date-picker": "^4.0.7", "@vue/web-component-wrapper": "^1.2.0", diff --git a/src/components/ha-sidebar-sort-styles.ts b/src/components/ha-sidebar-sort-styles.ts deleted file mode 100644 index b87b3c92ba..0000000000 --- a/src/components/ha-sidebar-sort-styles.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { html } from "lit-element"; - -export const sortStyles = html` - -`; diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 2f6c72bb4b..1226fffbc4 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -23,7 +23,6 @@ import { LitElement, property, PropertyValues, - TemplateResult, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { guard } from "lit-html/directives/guard"; @@ -161,7 +160,7 @@ const computePanels = memoizeOne( let Sortable; -let sortStyles: TemplateResult; +let sortStyles: CSSResult; @customElement("ha-sidebar") class HaSidebar extends LitElement { @@ -229,7 +228,13 @@ class HaSidebar extends LitElement { } return html` - ${this._editMode ? sortStyles : ""} + ${this._editMode + ? html` + + ` + : ""}
- + > Date: Mon, 7 Sep 2020 13:11:49 -0500 Subject: [PATCH 036/146] Updates to correct zindexs on new dialogs (#6816) --- src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts | 1 + src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts | 1 + .../lovelace/editor/card-editor/hui-dialog-suggest-card.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts index 60ed1350e7..9b3963485c 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -171,6 +171,7 @@ export class HuiCreateDialogCard extends LitElement implements HassDialog { ha-dialog { --mdc-dialog-max-width: 845px; --dialog-content-padding: 2px 24px 20px 24px; + --dialog-z-index: 5; } ha-dialog.table { diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index c8caba2a48..87d5ea3115 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -366,6 +366,7 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { ha-dialog { --mdc-dialog-max-width: 845px; + --dialog-z-index: 5; } ha-header-bar { diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts index 00e7d8b5a0..0a23b164e6 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts @@ -140,6 +140,7 @@ export class HuiDialogSuggestCard extends LitElement { } ha-paper-dialog { max-width: 845px; + --dialog-z-index: 5; } mwc-button ha-circular-progress { width: 14px; From 046f7b51539aee691f6d27c775298740d1a0ffb2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Sep 2020 20:39:26 +0200 Subject: [PATCH 037/146] Handle media browser errors (#6813) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joakim Sørensen --- .../media-player/ha-media-player-browse.ts | 143 +++++++++++++----- 1 file changed, 103 insertions(+), 40 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index df10572558..8f54e1204c 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -68,6 +68,8 @@ export class HaMediaPlayerBrowse extends LitElement { @internalProperty() private _loading = false; + @internalProperty() private _error?: { message: string; code: string }; + @internalProperty() private _mediaPlayerItems: MediaPlayerItem[] = []; private _resizeObserver?: ResizeObserver; @@ -92,11 +94,55 @@ export class HaMediaPlayerBrowse extends LitElement { this._navigate(item); } + private _renderError(err: { message: string; code: string }) { + if (err.message === "Media directory does not exist.") { + return html` +

No local media found.

+

+ It looks like you have not yet created a media directory. +
Create a directory with the name "media" in the + configuration directory of Home Assistant + (${this.hass.config.config_dir}).
Place your video, audio and + image files in this directory to be able to browse and play them in + the browser or on supported media players. +

+ +

+ Check the + documentation + for more info +

+ `; + } + return err.message; + } + protected render(): TemplateResult { if (this._loading) { return html``; } + if (this._error && !this._mediaPlayerItems.length) { + if (this.dialog) { + this._closeDialogAction(); + showAlertDialog(this, { + title: this.hass.localize( + "ui.components.media-browser.media_browsing_error" + ), + text: this._renderError(this._error), + }); + } else { + return html`
+ ${this._renderError(this._error)} +
`; + } + } + if (!this._mediaPlayerItems.length) { return html``; } @@ -216,7 +262,11 @@ export class HaMediaPlayerBrowse extends LitElement { ` : ""}
- ${currentItem.children?.length + ${this._error + ? html`
+ ${this._renderError(this._error)} +
` + : currentItem.children?.length ? hasExpandableChildren ? html`
@@ -316,7 +366,9 @@ export class HaMediaPlayerBrowse extends LitElement { )} ` - : this.hass.localize("ui.components.media-browser.no_items")} + : html`
+ ${this.hass.localize("ui.components.media-browser.no_items")} +
`} `; } @@ -342,15 +394,22 @@ export class HaMediaPlayerBrowse extends LitElement { return; } - this._fetchData(this.mediaContentId, this.mediaContentType).then( - (itemData) => { + if (changedProps.has("entityId")) { + this._error = undefined; + this._mediaPlayerItems = []; + } + + this._fetchData(this.mediaContentId, this.mediaContentType) + .then((itemData) => { if (!itemData) { return; } this._mediaPlayerItems = [itemData]; - } - ); + }) + .catch((err) => { + this._error = err; + }); } private _actionClicked(ev: MouseEvent): void { @@ -381,12 +440,22 @@ export class HaMediaPlayerBrowse extends LitElement { } private async _navigate(item: MediaPlayerItem) { - const itemData = await this._fetchData( - item.media_content_id, - item.media_content_type - ); + this._error = undefined; - if (!itemData) { + let itemData: MediaPlayerItem; + + try { + itemData = await this._fetchData( + item.media_content_id, + item.media_content_type + ); + } catch (err) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.components.media-browser.media_browsing_error" + ), + text: this._renderError(err), + }); return; } @@ -397,33 +466,23 @@ export class HaMediaPlayerBrowse extends LitElement { private async _fetchData( mediaContentId?: string, mediaContentType?: string - ): Promise { - let itemData: MediaPlayerItem | undefined; - try { - itemData = - this.entityId !== BROWSER_SOURCE - ? await browseMediaPlayer( - this.hass, - this.entityId, - mediaContentId, - mediaContentType - ) - : await browseLocalMediaPlayer(this.hass, mediaContentId); - itemData.children = itemData.children?.sort((first, second) => - !first.can_expand && second.can_expand - ? 1 - : first.can_expand && !second.can_expand - ? -1 - : compare(first.title, second.title) - ); - } catch (error) { - showAlertDialog(this, { - title: this.hass.localize( - "ui.components.media-browser.media_browsing_error" - ), - text: error.message, - }); - } + ): Promise { + const itemData = + this.entityId !== BROWSER_SOURCE + ? await browseMediaPlayer( + this.hass, + this.entityId, + mediaContentId, + mediaContentType + ) + : await browseLocalMediaPlayer(this.hass, mediaContentId); + itemData.children = itemData.children?.sort((first, second) => + !first.can_expand && second.can_expand + ? 1 + : first.can_expand && !second.can_expand + ? -1 + : compare(first.title, second.title) + ); return itemData; } @@ -451,8 +510,8 @@ export class HaMediaPlayerBrowse extends LitElement { this._resizeObserver.observe(this); } - private _hasExpandableChildren = memoizeOne((children) => - children.find((item: MediaPlayerItem) => item.can_expand) + private _hasExpandableChildren = memoizeOne((children?: MediaPlayerItem[]) => + children?.find((item: MediaPlayerItem) => item.can_expand) ); private _closeDialogAction(): void { @@ -471,6 +530,10 @@ export class HaMediaPlayerBrowse extends LitElement { flex-direction: column; } + .container { + padding: 16px; + } + .header { display: flex; justify-content: space-between; From f15fbe53cfcaa122a00427ca9bd7bc882b3f05e3 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 7 Sep 2020 20:40:03 +0200 Subject: [PATCH 038/146] Bumped version to 20200907.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4c386522cb..b1a1ef7a73 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200904.0", + version="20200907.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From 0889f42a0096fcb24e34a3783ad529cf66c2e387 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Tue, 8 Sep 2020 00:32:39 +0000 Subject: [PATCH 039/146] [ci skip] Translation update --- translations/frontend/ar.json | 22 +++++ translations/frontend/bs.json | 51 ++++++++++- translations/frontend/ca.json | 41 ++++++++- translations/frontend/cs.json | 2 + translations/frontend/cy.json | 13 ++- translations/frontend/en.json | 25 +++++- translations/frontend/eo.json | 52 +++++++++++- translations/frontend/es-419.json | 11 ++- translations/frontend/es.json | 49 +++++++++-- translations/frontend/et.json | 4 + translations/frontend/fa.json | 3 + translations/frontend/fr.json | 2 + translations/frontend/fy.json | 2 + translations/frontend/gl.json | 20 ++++- translations/frontend/gsw.json | 46 +++++++++- translations/frontend/hi.json | 60 ++++++++++++- translations/frontend/ja.json | 3 + translations/frontend/lt.json | 18 +++- translations/frontend/nb.json | 130 +++++++++++++++++++++++------ translations/frontend/nl.json | 2 + translations/frontend/pt-BR.json | 3 + translations/frontend/pt.json | 12 ++- translations/frontend/sr-Latn.json | 124 +++++++++++++++++++++++++++ translations/frontend/sr.json | 120 +++++++++++++++++++++++++- translations/frontend/sv.json | 11 ++- translations/frontend/ta.json | 49 ++++++++++- translations/frontend/te.json | 25 +++++- translations/frontend/tr.json | 6 +- translations/frontend/ur.json | 52 ++++++++++++ translations/frontend/zh-Hant.json | 2 + 30 files changed, 902 insertions(+), 58 deletions(-) diff --git a/translations/frontend/ar.json b/translations/frontend/ar.json index 15f0aca0ea..bb67ab7519 100644 --- a/translations/frontend/ar.json +++ b/translations/frontend/ar.json @@ -1469,12 +1469,34 @@ "next": "التالى", "providers": { "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "" + }, "step": { "init": { "data": { "password": "كلمة السر", "username": "اسم المستخدم" } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" } } }, diff --git a/translations/frontend/bs.json b/translations/frontend/bs.json index f3a2c84456..e85972507c 100644 --- a/translations/frontend/bs.json +++ b/translations/frontend/bs.json @@ -36,7 +36,8 @@ "unknown": "Nepoznat" }, "device_tracker": { - "home": "Kod kuće" + "home": "Kod kuće", + "not_home": "" }, "person": { "home": "Kod kuće" @@ -93,6 +94,7 @@ "on": "Otvoren" }, "presence": { + "off": "", "on": "Kod kuće" }, "problem": { @@ -159,6 +161,7 @@ "closing": "Zatvoreno", "home": "Kod kuće", "locked": "Zaključan", + "not_home": "", "off": "Isključen", "ok": "OK", "on": "Uključen", @@ -242,6 +245,13 @@ "config": { "automation": { "editor": { + "conditions": { + "type": { + "zone": { + "entity": "" + } + } + }, "triggers": { "type": { "mqtt": { @@ -293,6 +303,45 @@ "empty": "Nemate poruke", "playback_title": "Poruku preslušati" }, + "page-authorize": { + "form": { + "providers": { + "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "", + "invalid_code": "" + }, + "step": { + "init": { + "data": { + "password": "", + "username": "" + } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + } + } + } + }, "shopping-list": { "add_item": "Dodajte objekat", "clear_completed": "Čišćenje završeno", diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 5e49c1fdd1..9e2888ab08 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -19,6 +19,7 @@ "logbook": "Diari de registre", "mailbox": "Bústia", "map": "Mapa", + "media_browser": "Navegador multimèdia", "profile": "Perfil", "shopping_list": "Llista de compres", "states": "Visualització general" @@ -505,6 +506,7 @@ "back": "Torna", "cancel": "Cancel·la", "close": "Tanca", + "continue": "Continua", "delete": "Elimina", "error_required": "Obligatori", "loading": "Carregant", @@ -562,6 +564,8 @@ "no_history_found": "No s'ha trobat cap historial d'estats." }, "media-browser": { + "audio_not_supported": "El teu navegador no és compatible amb l'element d'àudio.", + "choose_player": "Tria el reproductor", "choose-source": "Tria la font", "content-type": { "album": "Àlbum", @@ -570,12 +574,17 @@ "playlist": "Llista de reproducció", "server": "Servidor" }, + "media_browsing_error": "Error de navegació multimèdia", + "media_not_supported": "El reproductor multimèdia de navegador no és compatible amb aquest tipus de mitjà", + "media_player": "Reproductor multimèdia", "media-player-browser": "Navegador del reproductor multimèdia", "no_items": "Sense elements", "pick": "Escull", "pick-media": "Tria mitjans", "play": "Reprodueix", - "play-media": "Reprodueix mitjans" + "play-media": "Reprodueix mitjans", + "video_not_supported": "El teu navegador no és compatible amb l'element de vídeo.", + "web-browser": "Navegador web" }, "picture-upload": { "label": "Imatge", @@ -689,8 +698,10 @@ "crop": "Retalla" }, "more_info_control": { + "controls": "Controls", "dismiss": "Desestimar el diàleg", "edit": "Edita entitat", + "history": "Historial", "person": { "create_zone": "Crea una zona a partir de la ubicació actual" }, @@ -1543,6 +1554,7 @@ "reload_restart_confirm": "Reinicia Home Assistant per acabar de carregar aquesta integració", "rename": "Canvia el nom", "restart_confirm": "Reinicia Home Assistant per acabar d'eliminar aquesta integració", + "services": "{count} {count, plural,\n one {servei}\n other {serveis}\n}", "settings_button": "Edita la configuració de {integration}", "system_options": "Opcions de sistema", "system_options_button": "Opcions de sistema de {integration}", @@ -1762,8 +1774,21 @@ "versions": "Obtenint informació de programari i versions de classes de comandes", "wakeup": "Configurant el suport per a cues i missatges" }, + "node": { + "button": "Detalls del node", + "not_found": "No s'ha trobat el node" + }, + "nodes_table": { + "failed": "Ha fallat", + "id": "ID", + "manufacturer": "Fabricant", + "model": "Model", + "query_stage": "Fase de consulta", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Si el node funciona amb bateria, assegura't de que estigui actiu abans de continuar", + "button": "Actualitza node", "complete": "Actualització del node completa", "description": "Això farà que OpenZWave torni a consultar el node i n'actualitzi les classes de comandes, funcions i valors.", "node_status": "Estat del node", @@ -1912,7 +1937,7 @@ "filter": "Torna a carregar entitats de filtre", "generic": "Torna a carregar entitats genèriques de càmera IP", "generic_thermostat": "Torna a carregar entitats genèriques de termòstat", - "group": "Actualitza grups", + "group": "Torna a carregar grups, grups d'entitats i serveis de notificació", "heading": "Tornant a carregar la configuració", "history_stats": "Torna a carregar entitats d'estadístiques històriques", "homekit": "Torna a carregar HomeKit", @@ -1923,12 +1948,17 @@ "input_text": "Actualitza entrades de text", "introduction": "Algunes parts de Home Assistant es poden actualitzar sense necessitat reiniciar-lo. Si prems actualitza s'esborrarà la configuració YAML actual i se'n carregarà la nova.", "min_max": "Torna a carregar entitats min/max", + "mqtt": "Torna a carregar entitats MQTT", "person": "Actualitza persones", "ping": "Torna a carregar entitats de sensors binaris de ping", - "rest": "Torna a carregar entitats de repòs", + "reload": "Torna a carregar {domain}", + "rest": "Torna a carregar entitats de repòs i serveis de notificació", + "rpi_gpio": "Torna a carregar entitats GPIO de la Raspberry Pi", "scene": "Actualitza escenes", "script": "Actualitza programes", + "smtp": "Torna a carregar serveis de notificació SMTP", "statistics": "Torna a carregar entitats estadístiques", + "telegram": "Torna a carregar serveis de notificació de Telegram", "template": "Torna a carregar entitats de plantilla", "trend": "Torna a carregar entitats de tendència", "universal": "Torna a carregar entitats del reproductor universal", @@ -2549,7 +2579,11 @@ } }, "cardpicker": { + "by_card": "Per targeta", + "by_entity": "Per entitat", "custom_card": "Personalitzada", + "domain": "Domini", + "entity": "Entitat", "no_description": "No hi ha cap descripció disponible." }, "edit_card": { @@ -2563,6 +2597,7 @@ "options": "Més opcions", "pick_card": "Quina targeta vols afegir?", "pick_card_view_title": "Quina targeta vols afegir a la visualització {name}?", + "search_cards": "Cerca targetes", "show_code_editor": "Mostra l'editor de codi", "show_visual_editor": "Mostra l'editor visual", "toggle_editor": "Commutar l'editor", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index cea97f6d86..e8048e50a0 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -19,6 +19,7 @@ "logbook": "Záznamy", "mailbox": "Schránka", "map": "Mapa", + "media_browser": "Prohlížeč médií", "profile": "Profil", "shopping_list": "Nákupní seznam", "states": "Přehled" @@ -573,6 +574,7 @@ "playlist": "Seznam skladeb", "server": "Server" }, + "media_browsing_error": "Chyba při procházení médií", "media_not_supported": "Přehrávač médií v prohlížeči nepodporuje tento typ média", "media_player": "Přehrávač médií", "media-player-browser": "Prohlížeč přehrávače médií", diff --git a/translations/frontend/cy.json b/translations/frontend/cy.json index 15f461bf91..abd54bda52 100644 --- a/translations/frontend/cy.json +++ b/translations/frontend/cy.json @@ -1283,7 +1283,18 @@ "mfa": { "data": { "code": "Cod dilysu dwy-ffactor" - } + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" } } } diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 2eac397f6b..250666624b 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -553,6 +553,10 @@ "toggle": "Toggle" }, "entity": { + "entity-attribute-picker": { + "attribute": "Attribute", + "show_attributes": "Show attributes" + }, "entity-picker": { "clear": "Clear", "entity": "Entity", @@ -929,7 +933,13 @@ "label": "Call service", "service_data": "Service data" }, + "wait_for_trigger": { + "continue_timeout": "Continue on timeout", + "label": "Wait for trigger", + "timeout": "Timeout (optional)" + }, "wait_template": { + "continue_timeout": "Continue on timeout", "label": "Wait", "timeout": "Timeout (optional)", "wait_template": "Wait Template" @@ -993,7 +1003,9 @@ "time": { "after": "After", "before": "Before", - "label": "Time" + "label": "Time", + "type_input": "Value of a date/time helper", + "type_value": "Fixed time" }, "zone": { "entity": "Entity with location", @@ -1081,6 +1093,7 @@ "value_template": "Value template (optional)" }, "state": { + "attribute": "Attribute (Optional)", "for": "For", "from": "From", "label": "State", @@ -1107,8 +1120,10 @@ "seconds": "Seconds" }, "time": { - "at": "At", - "label": "Time" + "at": "At time", + "label": "Time", + "type_input": "Value of a date/time helper", + "type_value": "Fixed time" }, "webhook": { "label": "Webhook", @@ -1131,6 +1146,8 @@ "add_automation": "Add automation", "delete_automation": "Delete automation", "delete_confirm": "Are you sure you want to delete this automation?", + "duplicate": "Duplicate", + "duplicate_automation": "Duplicate automation", "edit_automation": "Edit automation", "header": "Automation Editor", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "Add integration", + "attention": "Attention required", "caption": "Integrations", "config_entry": { "area": "In {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "Adjust your search criteria.", "note_about_integrations": "Not all integrations can be configured via the UI yet.", "note_about_website_reference": "More are available on the ", + "reconfigure": "Reconfigure", "rename_dialog": "Edit the name of this config entry", "rename_input_label": "Entry name", "search": "Search integrations" diff --git a/translations/frontend/eo.json b/translations/frontend/eo.json index 23fc03e9a9..8b05489328 100644 --- a/translations/frontend/eo.json +++ b/translations/frontend/eo.json @@ -1,4 +1,49 @@ { + "state_badge": { + "device_tracker": { + "home": "" + } + }, + "state": { + "automation": { + "off": "" + }, + "binary_sensor": { + "default": { + "on": "" + }, + "presence": { + "on": "" + } + }, + "calendar": { + "on": "" + }, + "group": { + "home": "", + "off": "", + "on": "" + }, + "input_boolean": { + "on": "" + }, + "light": { + "off": "", + "on": "" + }, + "media_player": { + "off": "" + }, + "script": { + "off": "" + }, + "sensor": { + "off": "" + }, + "switch": { + "on": "" + } + }, "ui": { "card": { "climate": { @@ -79,7 +124,12 @@ "edit_ui": "Redakti kun UI", "edit_yaml": "Redakti kiel YAML", "triggers": { - "name": "Ellasilo" + "name": "Ellasilo", + "type": { + "mqtt": { + "label": "" + } + } } }, "picker": { diff --git a/translations/frontend/es-419.json b/translations/frontend/es-419.json index 62e01af2d6..ced016f2a1 100644 --- a/translations/frontend/es-419.json +++ b/translations/frontend/es-419.json @@ -13,9 +13,15 @@ }, "panel": { "calendar": "Calendario", + "config": "", "developer_tools": "Herramientas para desarrolladores", + "history": "", + "logbook": "", + "mailbox": "", + "map": "", "profile": "Perfil", - "shopping_list": "Lista de compras" + "shopping_list": "Lista de compras", + "states": "" }, "state_attributes": { "climate": { @@ -994,6 +1000,7 @@ "start": "Inicio" }, "mqtt": { + "label": "", "payload": "Payload (opcional)", "topic": "Topic" }, @@ -1609,6 +1616,7 @@ "start_listening": "Comenzar a escuchar", "stop_listening": "Deja de escuchar", "subscribe_to": "Tema para suscribirse", + "title": "", "topic": "tema" }, "person": { @@ -1939,6 +1947,7 @@ }, "zwave": { "button": "Configurar", + "caption": "", "common": { "index": "Índice", "instance": "Instancia", diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 1b45b58764..9b22dc0cde 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -19,6 +19,7 @@ "logbook": "Registro", "mailbox": "Buzón", "map": "Mapa", + "media_browser": "Navegador de medios", "profile": "Perfil", "shopping_list": "Lista de la compra", "states": "Resumen" @@ -505,6 +506,7 @@ "back": "Volver", "cancel": "Cancelar", "close": "Cerrar", + "continue": "Continuar", "delete": "Eliminar", "error_required": "Obligatorio", "loading": "Cargando", @@ -562,6 +564,8 @@ "no_history_found": "No se encontró historial de estado." }, "media-browser": { + "audio_not_supported": "Tu navegador no es compatible con el elemento de audio.", + "choose_player": "Elige reproductor", "choose-source": "Elige la fuente", "content-type": { "album": "Álbum", @@ -570,12 +574,17 @@ "playlist": "Lista de reproducción", "server": "Servidor" }, + "media_browsing_error": "Error de navegación de medios", + "media_not_supported": "El Reproductor multimedia del navegador no es compatible con este tipo de medio", + "media_player": "Reproductor multimedia", "media-player-browser": "Navegador del Reproductor Multimedia", "no_items": "No hay elementos", "pick": "Elegir", "pick-media": "Elegir medio", "play": "Reproducir", - "play-media": "Reproducir medio" + "play-media": "Reproducir medio", + "video_not_supported": "Tu navegador no es compatible con el elemento de vídeo.", + "web-browser": "Navegador web" }, "picture-upload": { "label": "Imagen", @@ -689,8 +698,10 @@ "crop": "Recortar" }, "more_info_control": { + "controls": "Controles", "dismiss": "Descartar diálogo", "edit": "Editar entidad", + "history": "Historial", "person": { "create_zone": "Crear zona a partir de la ubicación actual" }, @@ -1543,6 +1554,7 @@ "reload_restart_confirm": "Reinicia Home Assistant para terminar de recargar esta integración", "rename": "Renombrar", "restart_confirm": "Reinicia Home Assistant para terminar de eliminar esta integración.", + "services": "{count} {count, plural,\n one {servicio}\n other {servicios}\n}", "settings_button": "Editar configuración para {integration}", "system_options": "Opciones del sistema", "system_options_button": "Opciones del sistema para {integration}", @@ -1762,8 +1774,21 @@ "versions": "Obteniendo información sobre versiones de firmware y clases de órdenes", "wakeup": "Configurando soporte para colas de despertador y mensajes" }, + "node": { + "button": "Detalles del nodo", + "not_found": "Nodo no encontrado" + }, + "nodes_table": { + "failed": "Ha fallado", + "id": "ID", + "manufacturer": "Fabricante", + "model": "Modelo", + "query_stage": "Etapa de consulta", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Si el nodo funciona con batería, asegúrate de despertarlo antes de continuar", + "button": "Actualizar nodo", "complete": "Refresco del Nodo Finalizado", "description": "Esto le indicará a OpenZWave que vuelva a entrevistar un nodo y actualice las clases de órdenes, las capacidades y los valores del mismo.", "node_status": "Estado del Nodo", @@ -1912,7 +1937,7 @@ "filter": "Recargar entidades de filtro", "generic": "Recargar entidades de cámara IP genéricas", "generic_thermostat": "Recargar entidades de termostato genéricas", - "group": "Recargar grupos", + "group": "Recargar grupos, entidades de grupo, y notificar servicios", "heading": "Recargando la configuración YAML", "history_stats": "Recargar entidades de estadísticas del historial", "homekit": "Recargar HomeKit", @@ -1923,12 +1948,17 @@ "input_text": "Recargar los campos de texto", "introduction": "Algunas partes de Home Assistant pueden recargarse sin necesidad de reiniciar. Al pulsar en recargar se descartará la configuración YAML actual y se cargará la nueva.", "min_max": "Recargar entidades min/max", + "mqtt": "Recargar entidades mqtt", "person": "Recargar personas", "ping": "Recargar entidades de sensor binario de ping", - "rest": "Recargar entidades rest", + "reload": "Recargar {domain}", + "rest": "Recargar entidades rest y notificar servicios", + "rpi_gpio": "Recargar entidades GPIO de Raspberry Pi", "scene": "Recargar escenas", "script": "Recargar scripts", + "smtp": "Recargar servicios de notificación smtp", "statistics": "Recargar entidades de estadísticas", + "telegram": "Recargar servicios de notificación de telegram", "template": "Recargar entidades de plantilla", "trend": "Recargar entidades de tendencia", "universal": "Recargar entidades de reproductor multimedia universal", @@ -2017,7 +2047,7 @@ "system": "Sistema" } }, - "users_privileges_note": "El grupo de usuarios es un trabajo en progreso. El usuario no podrá administrar la instancia a través de la interfaz de usuario. Todavía estamos auditando todos los endpoints de la API de administración para garantizar que se limita correctamente el acceso sólo a los administradores." + "users_privileges_note": "El grupo de usuarios es un trabajo en progreso. El usuario no podrá administrar la instancia a través de la IU. Todavía estamos auditando todos los endpoints de la API de administración para garantizar que se limita correctamente el acceso sólo a los administradores." }, "zha": { "add_device_page": { @@ -2055,7 +2085,7 @@ "clusters": { "header": "Clústeres", "help_cluster_dropdown": "Selecciona un clúster para ver atributos y comandos.", - "introduction": "Los clústeres son los bloques de construcción para la funcionalidad de Zigbee. Separan la funcionalidad en unidades lógicas. Hay tipos de cliente y servidor y se componen de atributos y comandos." + "introduction": "Los clústeres son los bloques de construcción para la funcionalidad de Zigbee. Separan la funcionalidad en unidades lógicas. Hay tipos de cliente y de servidor y se componen de atributos y comandos." }, "common": { "add_devices": "Añadir dispositivos", @@ -2188,7 +2218,7 @@ "true": "Verdadero" }, "node_management": { - "add_to_group": "Añadir al grupo", + "add_to_group": "Añadir al Grupo", "entities": "Entidades de este nodo", "entity_info": "Información de la entidad", "exclude_entity": "Excluir esta entidad de Home Assistant", @@ -2205,7 +2235,7 @@ "pooling_intensity": "Intensidad de sondeo", "protection": "Protección", "remove_broadcast": "Eliminar difusión", - "remove_from_group": "Eliminar del grupo", + "remove_from_group": "Eliminar del Grupo", "set_protection": "Establecer protección" }, "ozw_log": { @@ -2549,7 +2579,11 @@ } }, "cardpicker": { + "by_card": "Por tarjeta", + "by_entity": "Por entidad", "custom_card": "Personalizado", + "domain": "Dominio", + "entity": "Entidad", "no_description": "No hay descripción disponible." }, "edit_card": { @@ -2563,6 +2597,7 @@ "options": "Más opciones", "pick_card": "¿Qué tarjeta te gustaría añadir?", "pick_card_view_title": "¿Qué tarjeta te gustaría añadir a tu vista {name} ?", + "search_cards": "Buscar tarjetas", "show_code_editor": "Mostrar editor de código", "show_visual_editor": "Mostrar editor visual", "toggle_editor": "Alternar editor", diff --git a/translations/frontend/et.json b/translations/frontend/et.json index 4b3c342d9b..51e3f2f620 100644 --- a/translations/frontend/et.json +++ b/translations/frontend/et.json @@ -923,6 +923,10 @@ "at": "Kell", "label": "Aeg" }, + "webhook": { + "label": "", + "webhook_id": "" + }, "zone": { "enter": "Sisenemine", "entity": "Asukohaga olem", diff --git a/translations/frontend/fa.json b/translations/frontend/fa.json index 28992b49cb..2fc44ddc5d 100644 --- a/translations/frontend/fa.json +++ b/translations/frontend/fa.json @@ -1689,6 +1689,9 @@ }, "step": { "mfa": { + "data": { + "code": "" + }, "description": "باز کردن **{mfa_module_name}** * * * در دستگاه خود را برای مشاهده شما دو فاکتور تأیید هویت کد و هویت خود را تایید کنید:" } } diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 40a60cb610..8af22d4ab6 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -19,6 +19,7 @@ "logbook": "Journal", "mailbox": "Boîtes aux lettres", "map": "Carte", + "media_browser": "Navigateur multimédia", "profile": "Profil", "shopping_list": "Liste de courses", "states": "Aperçu" @@ -573,6 +574,7 @@ "playlist": "Liste de lecture", "server": "Serveur" }, + "media_browsing_error": "Erreur de navigation multimédia", "media_not_supported": "Le Browser Media Player ne prend pas en charge ce type de média", "media_player": "Lecteur multimédia", "media-player-browser": "Lecteur multimédia", diff --git a/translations/frontend/fy.json b/translations/frontend/fy.json index 1f8841d202..918eb995c3 100644 --- a/translations/frontend/fy.json +++ b/translations/frontend/fy.json @@ -195,8 +195,10 @@ }, "triggers": { "add": "Trigger tafoegje", + "header": "", "type": { "homeassistant": { + "label": "", "shutdown": "Ofslúte", "start": "Opstarte" }, diff --git a/translations/frontend/gl.json b/translations/frontend/gl.json index 9e26dfeeb6..15018eec65 100644 --- a/translations/frontend/gl.json +++ b/translations/frontend/gl.json @@ -1 +1,19 @@ -{} \ No newline at end of file +{ + "ui": { + "panel": { + "config": { + "automation": { + "editor": { + "triggers": { + "type": { + "mqtt": { + "label": "" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/translations/frontend/gsw.json b/translations/frontend/gsw.json index b4e756236a..2e0d098575 100644 --- a/translations/frontend/gsw.json +++ b/translations/frontend/gsw.json @@ -37,7 +37,8 @@ "unknown": "Unbekannt" }, "device_tracker": { - "home": "Dahei" + "home": "Dahei", + "not_home": "" }, "person": { "home": "Dahei" @@ -96,6 +97,7 @@ "on": "Offä" }, "presence": { + "off": "", "on": "Dahei" }, "problem": { @@ -417,6 +419,9 @@ "time": { "after": "Nachhär", "before": "Vorhär" + }, + "zone": { + "entity": "" } } }, @@ -510,6 +515,45 @@ "mailbox": { "delete_button": "Lösche" }, + "page-authorize": { + "form": { + "providers": { + "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "", + "invalid_code": "" + }, + "step": { + "init": { + "data": { + "password": "", + "username": "" + } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + } + } + } + }, "profile": { "change_password": { "confirm_new_password": "Nöis Passwort bestätige", diff --git a/translations/frontend/hi.json b/translations/frontend/hi.json index f71427a683..0f06beb774 100644 --- a/translations/frontend/hi.json +++ b/translations/frontend/hi.json @@ -19,6 +19,9 @@ }, "state_badge": { "alarm_control_panel": { + "armed_away": "", + "armed_custom_bypass": "", + "armed_night": "", "pending": "अपूर्ण" }, "default": { @@ -28,7 +31,8 @@ "unknown": "अज्ञात" }, "device_tracker": { - "home": "घर" + "home": "घर", + "not_home": "" }, "person": { "home": "घर" @@ -75,16 +79,27 @@ "off": "विशद", "on": "अनुसन्धानित" }, + "occupancy": { + "off": "", + "on": "" + }, "opening": { "on": "खुला" }, "presence": { + "off": "", "on": "घर" }, "safety": { "off": "सुरक्षित", "on": "असुरक्षित" }, + "smoke": { + "off": "" + }, + "vibration": { + "on": "" + }, "window": { "off": "बंद", "on": "खुली" @@ -115,10 +130,15 @@ "on": "चालू" }, "group": { + "closing": "", "home": "घर", + "not_home": "", "off": "बंद", + "ok": "", "on": "चालू", - "problem": "समस्या" + "open": "", + "problem": "समस्या", + "stopped": "" }, "input_boolean": { "off": "बंद", @@ -234,6 +254,9 @@ "time": { "after": "बाद", "before": "पहले" + }, + "zone": { + "entity": "" } } }, @@ -405,6 +428,39 @@ "form": { "next": "अगला", "providers": { + "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "", + "invalid_code": "" + }, + "step": { + "init": { + "data": { + "password": "", + "username": "" + } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, "trusted_networks": { "step": { "init": { diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index f0d6e46475..19aaf17e50 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -65,7 +65,10 @@ }, "state_badge": { "alarm_control_panel": { + "armed_away": "", + "armed_custom_bypass": "", "armed_home": "アームしました", + "armed_night": "", "disarmed": "解除", "disarming": "解除", "pending": "保留", diff --git a/translations/frontend/lt.json b/translations/frontend/lt.json index 567cd131d2..08fc0028b0 100644 --- a/translations/frontend/lt.json +++ b/translations/frontend/lt.json @@ -189,6 +189,7 @@ "ok": "Ok", "on": "Įjungta", "open": "Atidarytas", + "problem": "", "stopped": "Sustabdytas", "unlocked": "Atrakinta" }, @@ -408,6 +409,7 @@ "label": "Laikas" }, "zone": { + "entity": "", "label": "Vieta", "zone": "Vieta" } @@ -688,6 +690,9 @@ "form": { "providers": { "command_line": { + "abort": { + "login_expired": "" + }, "error": { "invalid_auth": "Netinkamas vartotojo vardas arba slaptažodis", "invalid_code": "Netinkamas autentifikacijos kodas" @@ -702,7 +707,18 @@ "mfa": { "data": { "code": "Dvieju lygiu autentifikacija" - } + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" } } } diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index db02e1f81a..523a9a2718 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -19,6 +19,7 @@ "logbook": "Loggbok", "mailbox": "Postkasse", "map": "Kart", + "media_browser": "Medieleser", "profile": "Profil", "shopping_list": "Handleliste", "states": "Oversikt" @@ -26,6 +27,7 @@ "state_attributes": { "climate": { "fan_mode": { + "auto": "", "off": "Av", "on": "På" }, @@ -50,11 +52,14 @@ }, "humidifier": { "mode": { + "auto": "", "away": "Borte", + "baby": "", "boost": "Øke", "comfort": "Komfort", "eco": "Øko", "home": "Hjem", + "normal": "", "sleep": "Sove" } } @@ -110,6 +115,7 @@ "on": "Lavt" }, "cold": { + "off": "", "on": "Kald" }, "connectivity": { @@ -160,6 +166,10 @@ "off": "Borte", "on": "Hjemme" }, + "problem": { + "off": "", + "on": "" + }, "safety": { "off": "Sikker", "on": "Usikker" @@ -229,6 +239,7 @@ "locked": "Låst", "not_home": "Borte", "off": "Av", + "ok": "", "on": "På", "open": "Åpen", "opening": "Åpner", @@ -260,6 +271,7 @@ "home": "Hjemme" }, "plant": { + "ok": "", "problem": "Problem" }, "remote": { @@ -365,6 +377,7 @@ "low": "lav", "on_off": "På / av", "operation": "Operasjon", + "preset_mode": "", "swing_mode": "Svingmodus", "target_humidity": "Ønsket luftfuktighet", "target_temperature": "Ønsket temperatur", @@ -409,7 +422,7 @@ "media_player": { "browse_media": "Bla gjennom medier", "media_next_track": "Neste", - "media_play": "Spille", + "media_play": "Spill av", "media_play_pause": "Spill av/pause", "media_previous_track": "Forrige", "sound_mode": "Lydmodus", @@ -435,7 +448,9 @@ "timer": { "actions": { "cancel": "Avbryt", - "finish": "Ferdig" + "finish": "Ferdig", + "pause": "", + "start": "" } }, "vacuum": { @@ -467,10 +482,12 @@ "e": "Ø", "ene": "ØNØ", "ese": "ØSØ", + "n": "", "ne": "NØ", "nne": "NNØ", "nnw": "NNV", "nw": "NV", + "s": "", "se": "SØ", "sse": "SSØ", "ssw": "SSV", @@ -547,6 +564,8 @@ "no_history_found": "Ingen statushistorikk funnet." }, "media-browser": { + "audio_not_supported": "Nettleseren din støtter ikke lydelementet.", + "choose_player": "Velg spiller", "choose-source": "Velg kilde", "content-type": { "album": "Album", @@ -555,12 +574,17 @@ "playlist": "Spilleliste", "server": "Server" }, + "media_browsing_error": "Feil ved medievisning", + "media_not_supported": "Browser Media Player støtter ikke denne typen medier", + "media_player": "Mediaspiller", "media-player-browser": "Nettleser for Mediespiller", "no_items": "Ingen elementer", "pick": "Velg", "pick-media": "Velg Media", - "play": "Spille", - "play-media": "Spill media" + "play": "Spill av", + "play-media": "Spill media", + "video_not_supported": "Nettleseren din støtter ikke videoelementet.", + "web-browser": "Nettleser" }, "picture-upload": { "label": "Bilde", @@ -618,7 +642,7 @@ "icon": "Overstyring av ikon", "icon_error": "Ikoner bør være i formatet 'prefiks:ikonnavn', f.eks 'mdi:home'", "name": "Overstyr Navn", - "note": "Merk: dette fungerer kanskje ikke med alle integrasjoner ennå .", + "note": "Merk: Dette fungerer kanskje ikke ennå med alle integrasjoner.", "unavailable": "Denne entiteten er ikke tilgjengelig for øyeblikket.", "update": "Oppdater" }, @@ -629,7 +653,8 @@ "generic": { "cancel": "Avbryt", "close": "Lukk", - "default_confirmation_title": "Er du sikker?" + "default_confirmation_title": "Er du sikker?", + "ok": "" }, "helper_settings": { "generic": { @@ -705,8 +730,11 @@ "commands": "Støvsugerkommandoer:", "fan_speed": "Viftehastighet", "locate": "Lokaliser", + "pause": "", "return_home": "Returner hjem", + "start": "", "start_pause": "Start / Pause", + "status": "", "stop": "Stopp" } }, @@ -753,6 +781,7 @@ "manuf": "av {manufacturer}", "no_area": "Intet område", "power_source": "Strømkilde", + "quirk": "", "services": { "reconfigure": "Rekonfigurer ZHA-enhet (heal enhet). Bruk dette hvis du har problemer med enheten. Hvis den aktuelle enheten er en batteridrevet enhet, sørg for at den er våken og aksepterer kommandoer når du bruker denne tjenesten.", "remove": "Fjern en enhet fra Zigbee-nettverket.", @@ -881,7 +910,7 @@ "type_select": "Gjenta type", "type": { "count": { - "label": "Telle" + "label": "Antall" }, "until": { "conditions": "Inntil forholdene", @@ -1036,7 +1065,9 @@ }, "homeassistant": { "event": "Hendelse:", - "shutdown": "Slå av" + "label": "", + "shutdown": "Slå av", + "start": "" }, "mqtt": { "label": "MQTT", @@ -1050,6 +1081,7 @@ "value_template": "Verdi fra mal (valgfritt)" }, "state": { + "for": "", "from": "Fra", "label": "Tilstand", "to": "Til" @@ -1078,6 +1110,10 @@ "at": "Klokken", "label": "Tid" }, + "webhook": { + "label": "", + "webhook_id": "" + }, "zone": { "enter": "Ankommer", "entity": "Entitet med posisjon", @@ -1121,7 +1157,8 @@ "manage_entities": "Håndtér entiteter", "state_reporting_error": "Kan ikke {enable_disable} rapportere status.", "sync_entities": "Synkronisér entiteter", - "sync_entities_error": "Kunne ikke synkronisere entiteter:" + "sync_entities_error": "Kunne ikke synkronisere entiteter:", + "title": "" }, "connected": "Tilkoblet", "connection_status": "Status for skytilkobling", @@ -1139,7 +1176,8 @@ "manage_entities": "Håndtér entiteter", "security_devices": "Sikkerhetsenheter", "sync_entities": "Synkronisér entiteter til Google", - "sync_entities_404_message": "Kunne ikke synkronisere enhetene dine med Google, be Google 'Hei Google, synkroniser enhetene mine' for å synkronisere enhetene dine." + "sync_entities_404_message": "Kunne ikke synkronisere enhetene dine med Google, be Google 'Hei Google, synkroniser enhetene mine' for å synkronisere enhetene dine.", + "title": "" }, "integrations": "Integrasjoner", "integrations_introduction": "Integrasjoner for Home Assistant Cloud lar deg koble til tjenester i skyen uten å måtte avsløre Home Assistant-forekomsten offentlig på internett.", @@ -1168,7 +1206,8 @@ "no_hooks_yet": "Ser ut som du ikke har noen webhooks ennå. Kom i gang ved å konfigurere en ", "no_hooks_yet_link_automation": "webhook-automasjon", "no_hooks_yet_link_integration": "webhook-basert integrasjon", - "no_hooks_yet2": " eller ved å opprette en " + "no_hooks_yet2": " eller ved å opprette en ", + "title": "" } }, "alexa": { @@ -1181,8 +1220,10 @@ "follow_domain": "Følg domenet", "manage_domains": "Administrer domener", "not_exposed": "{selected} ikke eksponert", - "not_exposed_entities": "Ikke eksponerte enheter" + "not_exposed_entities": "Ikke eksponerte enheter", + "title": "" }, + "caption": "", "description_features": "Kontroller borte fra hjemmet, integrer med Alexa og Google Assistant.", "description_login": "Logget inn som {email}", "description_not_login": "Ikke pålogget", @@ -1225,7 +1266,8 @@ "manage_domains": "Administrer domener", "not_exposed": "{selected} ikke eksponert", "not_exposed_entities": "Ikke eksponerte enheter", - "sync_to_google": "Synkroniserer endringer til Google." + "sync_to_google": "Synkroniserer endringer til Google.", + "title": "" }, "login": { "alert_email_confirm_necessary": "Du må bekrefte e-posten din før du logger inn.", @@ -1399,6 +1441,7 @@ "confirm_title": "Vil du aktivere {number} enheter?" }, "filter": { + "filter": "", "show_disabled": "Vis deaktiverte entiteter", "show_readonly": "Vis skrivebeskyttede enheter", "show_unavailable": "Vis utilgjengelige enheter" @@ -1407,7 +1450,8 @@ "headers": { "entity_id": "Entitets-ID", "integration": "Integrasjon", - "name": "Navn" + "name": "Navn", + "status": "" }, "introduction": "Home Assistant bygger opp et register over hver entitet den har sett som kan identifiseres unikt. Hver av disse entitetene vil ha en ID som er reservert kun til denne.", "introduction2": "Bruk entitetsregistret til å overstyre navnet, endre id-en eller fjerne den fra Home Assistant.", @@ -1422,6 +1466,7 @@ "selected": "{number} valgte", "status": { "disabled": "Deaktivert", + "ok": "", "readonly": "Skrivebeskyttet", "restored": "Gjennopprettet", "unavailable": "Utilgjengelig" @@ -1446,7 +1491,8 @@ "headers": { "editable": "Redigerbare", "entity_id": "Entitets-ID", - "name": "Navn" + "name": "Navn", + "type": "" }, "no_helpers": "Det ser ut som om du ikke har noen hjelpere ennå!" }, @@ -1454,6 +1500,7 @@ "input_boolean": "Veksle", "input_datetime": "Dato og/eller klokkeslett", "input_number": "Nummer", + "input_select": "", "input_text": "Tekst" } }, @@ -1466,6 +1513,7 @@ "documentation": "Dokumentasjon", "frontend": "frontend", "frontend_version": "Brukergrensesnittet-versjon: {version} - {type}", + "home_assistant_logo": "", "icons_by": "Ikoner fra", "integrations": "Integrasjoner", "issues": "Problemer", @@ -1506,6 +1554,7 @@ "reload_restart_confirm": "Start Home Assistant på nytt for å fullføre omlastingen av denne integrasjonen", "rename": "Gi nytt navn", "restart_confirm": "Start Home Assistant på nytt for å fullføre fjerningen av denne integrasjonen", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Rediger innstillinger for {integration}", "system_options": "Systemalternativer", "system_options_button": "Systemalternativer for {integration}", @@ -1596,6 +1645,7 @@ "title": "Tittel", "title_required": "Tittel er påkrevd.", "update": "Oppdater", + "url": "", "url_error_msg": "URLen skal inneholde en - og kan ikke inneholde mellomrom eller spesialtegn, bortsett fra _ og -" }, "picker": { @@ -1623,12 +1673,17 @@ "new_resource": "Legg til ny ressurs", "type": "Ressurstype", "update": "Oppdater", + "url": "", "url_error_msg": "URL-adresse er et obligatorisk felt", "warning_header": "Vær forsiktig!", "warning_text": "Det kan være farlig å legge til ressurser, sørg for at du kjenner kilden til ressursen og stoler på dem. Dårlige ressurser kan skade systemet ditt alvorlig." }, "picker": { "add_resource": "Legg til ressurs", + "headers": { + "type": "", + "url": "" + }, "no_resources": "Ingen ressurser" }, "refresh_body": "Du må oppdatere siden for å fullføre fjerningen, vil du oppdatere nå?", @@ -1719,8 +1774,21 @@ "versions": "Hente informasjon om fastvare- og kommandoklasseversjoner", "wakeup": "Sette opp støtte for vekkingskøer og meldinger" }, + "node": { + "button": "Node Detaljer", + "not_found": "Finner ikke noden" + }, + "nodes_table": { + "failed": "Mislyktes", + "id": "ID", + "manufacturer": "Produsent", + "model": "Modell", + "query_stage": "Spørringsstadiet", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Hvis noden er batteridrevet, må du passe på å vekke den før du fortsetter", + "button": "Oppdater node", "complete": "Node oppdatering fullført", "description": "Dette vil fortelle OpenZWave å re-intervjue en node og oppdatere nodens kommandoklasser, evner og verdier.", "node_status": "Node-status", @@ -1954,6 +2022,7 @@ "editor": { "activate_user": "Aktiver bruker", "active": "Aktiv", + "admin": "", "caption": "Vis bruker", "change_password": "Endre passord", "confirm_user_deletion": "Er du sikker på at du vil slette {name} ?", @@ -1974,10 +2043,11 @@ "picker": { "headers": { "group": "Gruppe", - "name": "Navn" + "name": "Navn", + "system": "" } }, - "users_privileges_note": "Brukere-gruppen er et pågående arbeid. Brukeren kan ikke administrere forekomsten via brukergrensesnittet. Vi reviderer fortsatt alle API-endepunkter for å sikre at de begrenser tilgangen til administratorer på riktig måte." + "users_privileges_note": "Brukergruppefunksjonen er et pågående arbeid. Brukeren vil ikke kunne administrere forekomsten via brukergrensesnittet. Vi overvåker fortsatt alle administrasjons-API-endepunkter for å sikre at de begrenser tilgangen til administratorer på riktig måte." }, "zha": { "add_device_page": { @@ -1994,6 +2064,7 @@ "description": "Legg til enheter i Zigbee-nettverket" }, "button": "Konfigurer", + "caption": "", "cluster_attributes": { "attributes_of_cluster": "Attributter for den valgte klyngen", "get_zigbee_attribute": "Hent ZigBee-attributt", @@ -2014,7 +2085,7 @@ "clusters": { "header": "Klynger", "help_cluster_dropdown": "Velg en klynge for å vise attributter og kommandoer.", - "introduction": "Klynger er byggesteinene for ZigBee-funksjonalitet. De skiller funksjonalitet i logiske enheter. Det finnes klient og server typer som består av attributter og kommandoer." + "introduction": "Klynger er byggesteinene for Zigbee-funksjonalitet. De skiller funksjonalitet i logiske enheter. Det finnes klient- og servertyper, og som består av attributter og kommandoer." }, "common": { "add_devices": "Legg til enheter", @@ -2101,6 +2172,7 @@ "new_zone": "Ny sone", "passive": "Passiv", "passive_note": "Passive soner er skjult i grensesnittet og brukes ikke som sted for enhetssporere. Dette er nyttig hvis du bare vil bruke dem til automasjoner.", + "radius": "", "required_error_msg": "Dette feltet er påkrevd", "update": "Oppdater" }, @@ -2146,7 +2218,7 @@ "true": "Sant" }, "node_management": { - "add_to_group": "Legg til i gruppe", + "add_to_group": "Legg til gruppe", "entities": "Entiteter fra denne noden", "entity_info": "Entitetsinformasjon", "exclude_entity": "Ekskluder denne entiteten fra Home Assistant", @@ -2163,7 +2235,7 @@ "pooling_intensity": "Intensitet for polling", "protection": "Beskyttelse", "remove_broadcast": "Fjern kringkasting", - "remove_from_group": "Fjern fra gruppe", + "remove_from_group": "Fjern fra gruppen", "set_protection": "Angi beskyttelse" }, "ozw_log": { @@ -2230,6 +2302,7 @@ "call_service": "Tilkall tjeneste", "column_description": "Beskrivelse", "column_example": "Eksempel", + "column_parameter": "", "data": "Tjenestedata (YAML, valgfritt)", "description": "Service utviklingsverktøyet lar deg tilkalle alle tilgjengelige tjenester i Home Assistant.", "fill_example_data": "Fyll ut eksempeldata", @@ -2305,6 +2378,7 @@ }, "picture-elements": { "call_service": "Tilkall tjeneste {name}", + "hold": "", "more_info": "Vis mer info: {name}", "navigate_to": "Naviger til {location}", "tap": "Trykk:", @@ -2399,6 +2473,7 @@ "manual": "Manuell", "manual_description": "Trenger du å legge til et tilpasset kort eller bare ønsker å skrive yaml manuelt?", "maximum": "Maksimalt", + "minimum": "", "name": "Navn", "no_theme": "Ingen tema", "refresh_interval": "Oppdateringsintervall", @@ -2412,7 +2487,8 @@ "tap_action": "Trykk handling", "theme": "Tema", "title": "Tittel", - "unit": "Betegnelse" + "unit": "Betegnelse", + "url": "" }, "glance": { "columns": "Kolonner", @@ -2450,7 +2526,8 @@ }, "markdown": { "content": "Innhold", - "description": "Markdown-kortet brukes til å gjengi Markdown." + "description": "Markdown-kortet brukes til å gjengi Markdown.", + "name": "" }, "media-control": { "description": "Mediekontroll kortet brukes til å vise mediespillerenheter på et grensesnitt med brukervennlige kontroller.", @@ -2479,7 +2556,8 @@ "sensor": { "description": "Sensorkortet gir deg en rask oversikt over sensortilstanden din med en valgfri graf for å visualisere endring over tid.", "graph_detail": "Detaljer for graf", - "graph_type": "Graf type" + "graph_type": "Graf type", + "name": "" }, "shopping-list": { "description": "På handlelistekortet kan du legge til, redigere, sjekke av og fjerne gjenstander fra handlelisten din.", @@ -2519,6 +2597,7 @@ "options": "Flere alternativer", "pick_card": "Hvilket kort vil du legge til?", "pick_card_view_title": "Hvilket kort vil du legge til i {name} visningen?", + "search_cards": "Søk på kort", "show_code_editor": "Vis koderedigering", "show_visual_editor": "Vis visuell redigering", "toggle_editor": "Bytt redigering", @@ -2786,6 +2865,7 @@ "upstairs": "Oppe" }, "unit": { + "minutes_abbr": "", "watching": "Ser på" } } @@ -2839,7 +2919,9 @@ }, "current_user": "Du er logget inn som {fullName}.", "dashboard": { - "description": "Velg et standard instrumentbord for denne enheten." + "description": "Velg et standard instrumentbord for denne enheten.", + "dropdown_label": "", + "header": "" }, "force_narrow": { "description": "Dette vil skjule sidepanelet som standard, tilsvarende opplevelsen på en mobil.", diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 8e3ce0ba70..8e5aaece5d 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -1023,6 +1023,7 @@ "delete": "Verwijderen", "delete_confirm": "Weet je zeker dat je dit item wilt verwijderen?", "duplicate": "Dupliceren", + "header": "", "introduction": "Triggers starten de verwerking van een automatiseringsregel. Het is mogelijk om meerdere triggers voor dezelfde regel op te geven. Zodra een trigger start, valideert Home Assistant de eventuele voorwaarden en roept hij de actie aan.", "learn_more": "Meer informatie over triggers", "name": "Trigger", @@ -1052,6 +1053,7 @@ }, "homeassistant": { "event": "Gebeurtenis:", + "label": "", "shutdown": "Afsluiten", "start": "Opstarten" }, diff --git a/translations/frontend/pt-BR.json b/translations/frontend/pt-BR.json index 078f61a7fc..8326daffff 100644 --- a/translations/frontend/pt-BR.json +++ b/translations/frontend/pt-BR.json @@ -1039,6 +1039,7 @@ }, "homeassistant": { "event": "Evento:", + "label": "", "shutdown": "Desligar", "start": "Iniciar" }, @@ -1664,6 +1665,7 @@ "start_listening": "Começar a ouvir", "stop_listening": "Parar de ouvir", "subscribe_to": "Evento para se inscrever", + "title": "", "topic": "tópico" }, "ozw": { @@ -2040,6 +2042,7 @@ }, "zwave": { "button": "Configurar", + "caption": "", "common": { "index": "Índice", "instance": "Instância", diff --git a/translations/frontend/pt.json b/translations/frontend/pt.json index 302d98036b..2d315f637e 100644 --- a/translations/frontend/pt.json +++ b/translations/frontend/pt.json @@ -1152,7 +1152,8 @@ "manage_entities": "Gerir Entidades", "state_reporting_error": "Indisponível para {enable_disable} reportar estado.", "sync_entities": "Sincronizar Entidades", - "sync_entities_error": "Falha na sincronização das entidades:" + "sync_entities_error": "Falha na sincronização das entidades:", + "title": "" }, "connected": "Ligado", "connection_status": "Estado da ligação na cloud", @@ -1170,7 +1171,8 @@ "manage_entities": "Gerir Entidades", "security_devices": "Dispositivos de segurança", "sync_entities": "Sincronizar entidades com o Google", - "sync_entities_404_message": "Falha ao sincronizar suas entidades com o Google, peça ao Google 'Ok Google, sincronize os meus dispositivos' para sincronizar suas entidades." + "sync_entities_404_message": "Falha ao sincronizar suas entidades com o Google, peça ao Google 'Ok Google, sincronize os meus dispositivos' para sincronizar suas entidades.", + "title": "" }, "integrations": "Integrações", "integrations_introduction": "As integrações para o Home Assistant Cloud permitem-lhe ligar-se aos serviços na nuvem sem ter de expor publicamente o seu Home Assistant na Internet.", @@ -1199,7 +1201,8 @@ "no_hooks_yet": "Parece que você ainda não tem webhooks. Comece, configurando um", "no_hooks_yet_link_automation": "automação de webhook", "no_hooks_yet_link_integration": "integração baseada em webhook", - "no_hooks_yet2": " ou criando um " + "no_hooks_yet2": " ou criando um ", + "title": "" } }, "alexa": { @@ -1233,6 +1236,7 @@ }, "forgot_password": { "check_your_email": "Verifique o seu e-mail para obter instruções sobre como redefinir a sua palavra-passe.", + "email": "", "email_error_msg": "E-mail inválido", "instructions": "Introduza o seu endereço de e-mail e nós lhe enviaremos um link para redefinir sua password.", "send_reset_email": "Enviar e-mail de redefinição", @@ -1252,6 +1256,7 @@ "alert_email_confirm_necessary": "É necessário confirmar o seu e-mail antes de fazer login.", "alert_password_change_required": "É necessário alterar a sua password antes de fazer login.", "dismiss": "Fechar", + "email": "", "email_error_msg": "E-mail inválido", "forgot_password": "Esqueceu-se da palavra-passe?", "introduction": "O Home Assistant Cloud fornece uma conexão remota segura à sua instância enquanto estiver fora de casa. Também permite que você se conecte com serviços que apenas utilizam a nuvem: Amazon Alexa e Google Assistant.", @@ -1489,6 +1494,7 @@ "description": "Ver informações sobre a instalação do Home Assistant", "developed_by": "Desenvolvido por um punhado de pessoas incríveis.", "documentation": "Documentação", + "frontend": "", "frontend_version": "Versão frontend: {version} - {type}", "home_assistant_logo": "Logotipo do Home Assistant", "icons_by": "Ícones por", diff --git a/translations/frontend/sr-Latn.json b/translations/frontend/sr-Latn.json index 7f497faa5b..11f7af24b5 100644 --- a/translations/frontend/sr-Latn.json +++ b/translations/frontend/sr-Latn.json @@ -8,7 +8,85 @@ "shopping_list": "Lista za kupovinu", "states": "Pregled" }, + "state_badge": { + "alarm_control_panel": { + "armed_away": "", + "armed_custom_bypass": "", + "armed_night": "" + }, + "device_tracker": { + "home": "", + "not_home": "" + } + }, "state": { + "automation": { + "off": "", + "on": "" + }, + "binary_sensor": { + "default": { + "off": "", + "on": "" + }, + "motion": { + "on": "" + }, + "occupancy": { + "off": "" + }, + "opening": { + "on": "" + }, + "presence": { + "off": "", + "on": "" + }, + "vibration": { + "off": "", + "on": "" + } + }, + "calendar": { + "off": "", + "on": "" + }, + "fan": { + "off": "", + "on": "" + }, + "group": { + "home": "", + "not_home": "", + "off": "", + "on": "", + "problem": "" + }, + "input_boolean": { + "on": "" + }, + "light": { + "off": "", + "on": "" + }, + "media_player": { + "off": "", + "on": "" + }, + "remote": { + "on": "" + }, + "script": { + "off": "", + "on": "" + }, + "sensor": { + "off": "", + "on": "" + }, + "switch": { + "on": "" + }, "weather": { "clear-night": "Vedra noć", "cloudy": "Oblačno", @@ -73,6 +151,13 @@ "config": { "automation": { "editor": { + "conditions": { + "type": { + "zone": { + "entity": "" + } + } + }, "triggers": { "type": { "mqtt": { @@ -107,6 +192,45 @@ "set_config_parameter": "Подесите параметар Цонфиг" } } + }, + "page-authorize": { + "form": { + "providers": { + "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "", + "invalid_code": "" + }, + "step": { + "init": { + "data": { + "password": "", + "username": "" + } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + } + } + } } } } diff --git a/translations/frontend/sr.json b/translations/frontend/sr.json index 2a32b3aca6..b5debac397 100644 --- a/translations/frontend/sr.json +++ b/translations/frontend/sr.json @@ -11,21 +11,93 @@ }, "state_badge": { "alarm_control_panel": { + "armed_away": "", + "armed_custom_bypass": "", + "armed_night": "", "arming": "Aktiviranje", "disarming": "Deaktiviraj" }, "default": { "entity_not_found": "Вредност није пронађена", "error": "Грешка" + }, + "device_tracker": { + "home": "", + "not_home": "" } }, "state": { + "automation": { + "off": "", + "on": "" + }, + "binary_sensor": { + "default": { + "off": "", + "on": "" + }, + "motion": { + "on": "" + }, + "occupancy": { + "off": "" + }, + "opening": { + "on": "" + }, + "presence": { + "off": "", + "on": "" + }, + "vibration": { + "off": "", + "on": "" + } + }, + "calendar": { + "off": "", + "on": "" + }, + "fan": { + "off": "", + "on": "" + }, + "group": { + "home": "", + "not_home": "", + "off": "", + "on": "", + "problem": "" + }, + "input_boolean": { + "on": "" + }, + "light": { + "off": "", + "on": "" + }, + "media_player": { + "off": "", + "on": "" + }, + "remote": { + "on": "" + }, + "script": { + "off": "", + "on": "" + }, + "sensor": { + "off": "", + "on": "" + }, "sun": { "above_horizon": "Iznad horizonta", "below_horizon": "Ispod horizonta" }, "switch": { - "off": "Isključen" + "off": "Isključen", + "on": "" }, "timer": { "active": "укључен", @@ -56,6 +128,13 @@ "config": { "automation": { "editor": { + "conditions": { + "type": { + "zone": { + "entity": "" + } + } + }, "triggers": { "learn_more": "Сазнајте више о окидачима", "type": { @@ -113,6 +192,45 @@ } } }, + "page-authorize": { + "form": { + "providers": { + "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "", + "invalid_code": "" + }, + "step": { + "init": { + "data": { + "password": "", + "username": "" + } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + } + } + } + }, "page-onboarding": { "integration": { "finish": "Крај" diff --git a/translations/frontend/sv.json b/translations/frontend/sv.json index f684b51098..cf1615ecc1 100644 --- a/translations/frontend/sv.json +++ b/translations/frontend/sv.json @@ -1145,7 +1145,8 @@ "manage_entities": "Hantera Entiteter", "state_reporting_error": "Det går inte att {enable_disable} rapportera tillståndet.", "sync_entities": "Synkronisera Entiteter", - "sync_entities_error": "Det gick inte att synkronisera entiteter:" + "sync_entities_error": "Det gick inte att synkronisera entiteter:", + "title": "" }, "connected": "Ansluten", "connection_status": "Status för molnanslutning", @@ -1193,14 +1194,16 @@ "no_hooks_yet": "Det verkar som du inte har några webhooks ännu. Kom igång genom att konfigurera en ", "no_hooks_yet_link_automation": "webhook automation", "no_hooks_yet_link_integration": "webhook-baserad integration", - "no_hooks_yet2": " eller genom att skapa en " + "no_hooks_yet2": " eller genom att skapa en ", + "title": "" } }, "alexa": { "banner": "Redigering av vilka entiteter som visas via det här användargränssnittet är inaktiverat eftersom du har konfigurerat entitetsfilter i configuration.yaml.", "expose": "Exponera för Alexa", "exposed_entities": "Exponerade entiteter", - "not_exposed_entities": "Ej exponerade entiteter" + "not_exposed_entities": "Ej exponerade entiteter", + "title": "" }, "caption": "Home Assistant Cloud", "description_features": "Styra även när du inte är hemma, integrera med Alexa och Google Assistant.", @@ -2375,6 +2378,8 @@ "image": "Bildsökväg", "manual": "Manuell", "manual_description": "Behöver du lägga till ett anpassat kort eller vill du bara skriva yaml manuellt?", + "maximum": "", + "minimum": "", "name": "Namn", "no_theme": "Inget tema", "refresh_interval": "Uppdateringsintervall", diff --git a/translations/frontend/ta.json b/translations/frontend/ta.json index 7445b2d66a..9043c84c30 100644 --- a/translations/frontend/ta.json +++ b/translations/frontend/ta.json @@ -35,7 +35,8 @@ "unknown": "தெரியாத" }, "device_tracker": { - "home": "முகப்பு" + "home": "முகப்பு", + "not_home": "" }, "person": { "home": "முகப்பு" @@ -239,6 +240,13 @@ "config": { "automation": { "editor": { + "conditions": { + "type": { + "zone": { + "entity": "" + } + } + }, "triggers": { "type": { "mqtt": { @@ -257,6 +265,45 @@ "set_config_parameter": "கட்டமைப்பு அளவுருவை அமைக்கவும்" } } + }, + "page-authorize": { + "form": { + "providers": { + "command_line": { + "abort": { + "login_expired": "" + }, + "error": { + "invalid_auth": "", + "invalid_code": "" + }, + "step": { + "init": { + "data": { + "password": "", + "username": "" + } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + } + } + } } } } diff --git a/translations/frontend/te.json b/translations/frontend/te.json index 1bddd1e80b..50cf3c0969 100644 --- a/translations/frontend/te.json +++ b/translations/frontend/te.json @@ -37,7 +37,8 @@ "unknown": "తెలియదు" }, "device_tracker": { - "home": "ఇంట" + "home": "ఇంట", + "not_home": "" }, "person": { "home": "ఇంట" @@ -410,6 +411,9 @@ "time": { "after": "తరువాత", "before": "ముందు" + }, + "zone": { + "entity": "" } } }, @@ -531,7 +535,8 @@ "login_expired": "సెషన్ గడువు ముగిసింది, మళ్ళీ లాగిన్ అవ్వండి." }, "error": { - "invalid_auth": "తప్పు యూజర్ పేరు లేదా తప్పు పాస్ వర్డ్" + "invalid_auth": "తప్పు యూజర్ పేరు లేదా తప్పు పాస్ వర్డ్", + "invalid_code": "" }, "step": { "init": { @@ -539,6 +544,12 @@ "password": "పాస్వర్డ్", "username": "యూజర్ పేరు" } + }, + "mfa": { + "data": { + "code": "" + }, + "description": "" } } }, @@ -558,6 +569,16 @@ } } }, + "legacy_api_password": { + "step": { + "mfa": { + "data": { + "code": "" + }, + "description": "" + } + } + }, "trusted_networks": { "abort": { "not_whitelisted": "మీ కంప్యూటర్ అనుమతి జాబితాలో లేదు." diff --git a/translations/frontend/tr.json b/translations/frontend/tr.json index 13376adc80..f0e477f212 100644 --- a/translations/frontend/tr.json +++ b/translations/frontend/tr.json @@ -2434,7 +2434,8 @@ "mfa": { "data": { "code": "İki adımlı kimlik doğrulama kodu" - } + }, + "description": "" } } }, @@ -2479,7 +2480,8 @@ "mfa": { "data": { "code": "İki adımlı kimlik doğrulama kodu" - } + }, + "description": "" } } }, diff --git a/translations/frontend/ur.json b/translations/frontend/ur.json index c130104798..2f7a214405 100644 --- a/translations/frontend/ur.json +++ b/translations/frontend/ur.json @@ -1,4 +1,49 @@ { + "state_badge": { + "device_tracker": { + "home": "" + } + }, + "state": { + "automation": { + "off": "" + }, + "binary_sensor": { + "default": { + "on": "" + }, + "presence": { + "on": "" + } + }, + "calendar": { + "on": "" + }, + "group": { + "home": "", + "off": "", + "on": "" + }, + "input_boolean": { + "on": "" + }, + "light": { + "off": "", + "on": "" + }, + "media_player": { + "off": "" + }, + "script": { + "off": "" + }, + "sensor": { + "off": "" + }, + "switch": { + "on": "" + } + }, "ui": { "card": { "fan": { @@ -39,6 +84,13 @@ "label": "آلہ" } } + }, + "triggers": { + "type": { + "mqtt": { + "label": "" + } + } } } }, diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index a805c1b832..30f780c639 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -19,6 +19,7 @@ "logbook": "日誌", "mailbox": "郵箱", "map": "地圖", + "media_browser": "媒體瀏覽器", "profile": "個人設定", "shopping_list": "購物清單", "states": "總覽" @@ -573,6 +574,7 @@ "playlist": "播放列表", "server": "伺服器" }, + "media_browsing_error": "媒體瀏覽錯誤", "media_not_supported": "瀏覽器媒體播放器不支援此類型媒體", "media_player": "媒體播放器", "media-player-browser": "媒體播放器瀏覽器", From 30c47a65f4f28273f4a133cbcc9957894349546d Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 19:47:25 -0500 Subject: [PATCH 040/146] fix more info content --- src/dialogs/more-info/ha-more-info-dialog.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 086040ddef..7738ee7349 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -274,6 +274,7 @@ export class MoreInfoDialog extends LitElement { --mdc-theme-on-primary: var(--primary-text-color); --mdc-theme-primary: var(--mdc-theme-surface); flex-shrink: 0; + display: block; } @media all and (max-width: 450px), all and (max-height: 500px) { From 7edc9064d9ddfedce2ab384ccb96818c094b104e Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 20:49:53 -0500 Subject: [PATCH 041/146] Fix light extra attributes start fix for history --- src/dialogs/more-info/controls/more-info-light.ts | 8 ++++---- src/dialogs/more-info/ha-more-info-tab-history.ts | 13 ++++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 6b0bba5572..a87316e874 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -105,12 +105,12 @@ class MoreInfoLight extends LitElement { ? html`
`; diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts index 18624a11ef..eeb00cef33 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -85,7 +85,18 @@ export class MoreInfoTabHistoryDialog extends LitElement { clearInterval(this._historyRefreshInterval); } - if (changedProps.has("entityId")) { + if (!this.hass) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant; + const newStateObj = this.hass.states[this.entityId]; + const oldStateObj = oldHass?.states[this.entityId]; + + if ( + changedProps.has("entityId") || + (oldHass && newStateObj !== oldStateObj) + ) { this._stateHistory = undefined; this._entries = undefined; From 8b1801f378dc5174186dad7db8843a1c9312d9d8 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 8 Sep 2020 02:14:34 -0500 Subject: [PATCH 042/146] Fix header on media browser in safari (#6838) --- src/components/media-player/ha-media-player-browse.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 8f54e1204c..0d9cde1946 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -172,6 +172,7 @@ export class HaMediaPlayerBrowse extends LitElement {
@@ -543,6 +544,7 @@ export class HaMediaPlayerBrowse extends LitElement { .header { background-color: var(--card-background-color); position: sticky; + position: -webkit-sticky; top: 0; z-index: 5; padding: 20px 24px 10px; @@ -740,6 +742,10 @@ export class HaMediaPlayerBrowse extends LitElement { padding: 0; } + :host([narrow]) .header.no-dialog { + display: block; + } + :host([narrow]) .header_button { position: absolute; top: 14px; From d7448ecb952afef1509d33c15e7061312c508120 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 8 Sep 2020 02:17:49 -0500 Subject: [PATCH 043/146] Fix Calendar Card in Add Card dialog (#6833) --- src/panels/lovelace/cards/hui-calendar-card.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-calendar-card.ts b/src/panels/lovelace/cards/hui-calendar-card.ts index 2351454829..ee94fb807c 100644 --- a/src/panels/lovelace/cards/hui-calendar-card.ts +++ b/src/panels/lovelace/cards/hui-calendar-card.ts @@ -76,11 +76,11 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard { private _resizeObserver?: ResizeObserver; public setConfig(config: CalendarCardConfig): void { - if (!config.entities) { + if (!config.entities?.length) { throw new Error("Entities must be defined"); } - if (config.entities && !Array.isArray(config.entities)) { + if (!Array.isArray(config.entities)) { throw new Error("Entities need to be an array"); } From 100ba8edfaa6e37c73e69c4603e58988ca1f897a Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 8 Sep 2020 04:37:49 -0500 Subject: [PATCH 044/146] Add allowed options to entities struct so UI editor can still be used (#6823) --- src/panels/lovelace/editor/types.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts index 04a2755a4b..5d9a335a10 100644 --- a/src/panels/lovelace/editor/types.ts +++ b/src/panels/lovelace/editor/types.ts @@ -5,7 +5,7 @@ import { ShowViewConfig, } from "../../../data/lovelace"; import { EntityConfig } from "../entity-rows/types"; -import { optional, string, object, union } from "superstruct"; +import { optional, string, object, union, boolean } from "superstruct"; import { EntityId } from "../common/structs/is-entity-id"; import { Icon } from "../common/structs/is-icon"; @@ -81,6 +81,10 @@ export const entitiesConfigStruct = union([ entity: EntityId, name: optional(string()), icon: optional(Icon), + image: optional(string()), + secondary_info: optional(string()), + format: optional(string()), + state_color: optional(boolean()), }), EntityId, ]); From 6e0e169b6e89b2b85a217843f04c7fbe729e3420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 8 Sep 2020 13:27:59 +0200 Subject: [PATCH 045/146] Add reload for platforms with reload service (#6851) --- .../server_control/ha-config-server-control.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/panels/config/server_control/ha-config-server-control.ts b/src/panels/config/server_control/ha-config-server-control.ts index 7cd8e63160..04fbc5b237 100644 --- a/src/panels/config/server_control/ha-config-server-control.ts +++ b/src/panels/config/server_control/ha-config-server-control.ts @@ -23,6 +23,8 @@ import { HomeAssistant, Route } from "../../../types"; import "../ha-config-section"; import { configSections } from "../ha-panel-config"; +const platformsWithReload = ["template"]; + @customElement("ha-config-server-control") export class HaConfigServerControl extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -49,11 +51,13 @@ export class HaConfigServerControl extends LitElement { changedProperties.has("hass") && (!oldHass || oldHass.config.components !== this.hass.config.components) ) { - this._reloadableDomains = this.hass.config.components.filter( - (component) => - !component.includes(".") && - isServiceLoaded(this.hass, component, "reload") - ); + this._reloadableDomains = this.hass.config.components + .concat(platformsWithReload) + .filter( + (component) => + !component.includes(".") && + isServiceLoaded(this.hass, component, "reload") + ); } } From 67b46881684a9e061029c2a6448a3602dfeb2062 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Sep 2020 13:28:29 +0200 Subject: [PATCH 046/146] Make time wider in logbook (#6854) Fixes #6842 --- src/panels/logbook/ha-logbook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 1270db9619..cf49dd24ba 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -216,7 +216,7 @@ class HaLogbook extends LitElement { display: flex; justify-content: center; flex-direction: column; - width: 70px; + width: 75px; flex-shrink: 0; font-size: 12px; color: var(--secondary-text-color); From 82e91783209f8b96c7184b99c34b9adac8f9e491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 8 Sep 2020 13:56:51 +0200 Subject: [PATCH 047/146] Add warning class to delete (#6852) * Add error class to delete * Apply suggestions from code review Co-authored-by: Bram Kragten * Add missing haStyle Co-authored-by: Bram Kragten --- .../action/ha-automation-action-row.ts | 2 +- .../condition/ha-automation-condition-row.ts | 34 +++++++++++-------- .../trigger/ha-automation-trigger-row.ts | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index 420d339e28..d9cf65ce8c 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -173,7 +173,7 @@ export default class HaAutomationActionRow extends LitElement { "ui.panel.config.automation.editor.actions.duplicate" )} - + ${this.hass.localize( "ui.panel.config.automation.editor.actions.delete" )} diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts index 1f51b47f13..89bbfcab54 100644 --- a/src/panels/config/automation/condition/ha-automation-condition-row.ts +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -19,6 +19,7 @@ import { Condition } from "../../../../data/automation"; import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import { HomeAssistant } from "../../../../types"; import "./ha-automation-condition-editor"; +import { haStyle } from "../../../../resources/styles"; export interface ConditionElement extends LitElement { condition: Condition; @@ -86,7 +87,7 @@ export default class HaAutomationConditionRow extends LitElement { "ui.panel.config.automation.editor.actions.duplicate" )} - + ${this.hass.localize( "ui.panel.config.automation.editor.actions.delete" )} @@ -134,20 +135,23 @@ export default class HaAutomationConditionRow extends LitElement { this._yamlMode = !this._yamlMode; } - static get styles(): CSSResult { - return css` - .card-menu { - float: right; - z-index: 3; - --mdc-theme-text-primary-on-background: var(--primary-text-color); - } - .rtl .card-menu { - float: left; - } - mwc-list-item[disabled] { - --mdc-theme-text-primary-on-background: var(--disabled-text-color); - } - `; + static get styles(): CSSResult[] { + return [ + haStyle, + css` + .card-menu { + float: right; + z-index: 3; + --mdc-theme-text-primary-on-background: var(--primary-text-color); + } + .rtl .card-menu { + float: left; + } + mwc-list-item[disabled] { + --mdc-theme-text-primary-on-background: var(--disabled-text-color); + } + `, + ]; } } diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index feeda91773..aa80ff1a96 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -118,7 +118,7 @@ export default class HaAutomationTriggerRow extends LitElement { "ui.panel.config.automation.editor.actions.duplicate" )} - + ${this.hass.localize( "ui.panel.config.automation.editor.actions.delete" )} From 4631994f2041b48385dce589bd94fa0e87c5d662 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Sep 2020 14:10:34 +0200 Subject: [PATCH 048/146] Fix sidebar issues (#6853) * Fix sidebar issues * fix navigate in demo --- src/components/ha-sidebar.ts | 26 ++++++++++++++----- .../directives/action-handler-directive.ts | 9 ++++--- src/resources/ha-sortable-style.ts | 2 -- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 1226fffbc4..d02c7bbd60 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -322,7 +322,7 @@ class HaSidebar extends LitElement { )} href="#external-app-configuration" tabindex="-1" - @click=${this._handleExternalAppConfiguration} + @panel-tap=${this._handleExternalAppConfiguration} @mouseenter=${this._itemMouseEnter} @mouseleave=${this._itemMouseLeave} > @@ -479,7 +479,8 @@ class HaSidebar extends LitElement { } private async _handleAction(ev: CustomEvent) { - if (ev.detail.action !== "hold") { + if (ev.detail.action === "tap") { + fireEvent(ev.target as HTMLElement, "panel-tap"); return; } @@ -497,6 +498,10 @@ class HaSidebar extends LitElement { } this._editMode = true; + if (!this.expanded) { + fireEvent(this, "hass-toggle-menu"); + } + await this.updateComplete; this._createSortable(); @@ -655,8 +660,11 @@ class HaSidebar extends LitElement { ); } - private _handlePanelTap(ev: Event) { - navigate(this, (ev.currentTarget as HTMLAnchorElement).href); + private async _handlePanelTap(ev: Event) { + const path = __DEMO__ + ? (ev.currentTarget as HTMLAnchorElement).getAttribute("href")! + : (ev.currentTarget as HTMLAnchorElement).href; + navigate(this, path); } private _renderPanel( @@ -668,10 +676,10 @@ class HaSidebar extends LitElement { return html` @@ -980,4 +988,8 @@ declare global { interface HTMLElementTagNameMap { "ha-sidebar": HaSidebar; } + + interface HASSDomEvents { + "panel-tap": undefined; + } } diff --git a/src/panels/lovelace/common/directives/action-handler-directive.ts b/src/panels/lovelace/common/directives/action-handler-directive.ts index 0c245a4764..3b0ea378c8 100644 --- a/src/panels/lovelace/common/directives/action-handler-directive.ts +++ b/src/panels/lovelace/common/directives/action-handler-directive.ts @@ -154,6 +154,7 @@ class ActionHandler extends HTMLElement implements ActionHandler { if (["touchend", "touchcancel"].includes(ev.type) && this.cancelled) { return; } + const target = ev.target as HTMLElement; // Prevent mouse event if touch event if (ev.cancelable) { ev.preventDefault(); @@ -164,7 +165,7 @@ class ActionHandler extends HTMLElement implements ActionHandler { this.timer = undefined; } if (options.hasHold && this.held) { - fireEvent(element, "action", { action: "hold" }); + fireEvent(target, "action", { action: "hold" }); } else if (options.hasDoubleClick) { if ( (ev.type === "click" && (ev as MouseEvent).detail < 2) || @@ -172,15 +173,15 @@ class ActionHandler extends HTMLElement implements ActionHandler { ) { this.dblClickTimeout = window.setTimeout(() => { this.dblClickTimeout = undefined; - fireEvent(element, "action", { action: "tap" }); + fireEvent(target, "action", { action: "tap" }); }, 250); } else { clearTimeout(this.dblClickTimeout); this.dblClickTimeout = undefined; - fireEvent(element, "action", { action: "double_tap" }); + fireEvent(target, "action", { action: "double_tap" }); } } else { - fireEvent(element, "action", { action: "tap" }); + fireEvent(target, "action", { action: "tap" }); } }; diff --git a/src/resources/ha-sortable-style.ts b/src/resources/ha-sortable-style.ts index aa5ffe7789..5751f54819 100644 --- a/src/resources/ha-sortable-style.ts +++ b/src/resources/ha-sortable-style.ts @@ -20,8 +20,6 @@ export const sortableStyles = css` #sortable { outline: none; - display: flex; - flex-direction: column; } .sortable-ghost { From 9149bb9333b3ec7b191671bf4128138282664cca Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 8 Sep 2020 15:41:17 +0200 Subject: [PATCH 049/146] Remove deprecated HTML support (#6858) --- src/entrypoints/app.ts | 2 - src/html/index.html.template | 4 - src/panels/lovelace/common/load-resources.ts | 6 - src/resources/html-import/import-href.js | 101 ----- src/resources/html-import/polyfill.js | 451 ------------------- src/util/custom-panel/load-custom-panel.ts | 11 - 6 files changed, 575 deletions(-) delete mode 100644 src/resources/html-import/import-href.js delete mode 100644 src/resources/html-import/polyfill.js diff --git a/src/entrypoints/app.ts b/src/entrypoints/app.ts index bb4b54b6e7..a4dd8580d4 100644 --- a/src/entrypoints/app.ts +++ b/src/entrypoints/app.ts @@ -7,5 +7,3 @@ import "../util/legacy-support"; setPassiveTouchGestures(true); (window as any).frontendVersion = __VERSION__; - -import("../resources/html-import/polyfill"); diff --git a/src/html/index.html.template b/src/html/index.html.template index fe4271c0fb..f5d8be97d1 100644 --- a/src/html/index.html.template +++ b/src/html/index.html.template @@ -100,9 +100,5 @@ {% endfor -%} } - - {% for extra_url in extra_urls -%} - - {% endfor -%} diff --git a/src/panels/lovelace/common/load-resources.ts b/src/panels/lovelace/common/load-resources.ts index c996f25562..5b9fb6d316 100644 --- a/src/panels/lovelace/common/load-resources.ts +++ b/src/panels/lovelace/common/load-resources.ts @@ -30,12 +30,6 @@ export const loadLovelaceResources = ( loadModule(normalizedUrl); break; - case "html": - import( - /* webpackChunkName: "import-href-polyfill" */ "../../../resources/html-import/import-href" - ).then(({ importHref }) => importHref(normalizedUrl)); - break; - default: // eslint-disable-next-line console.warn(`Unknown resource type specified: ${resource.type}`); diff --git a/src/resources/html-import/import-href.js b/src/resources/html-import/import-href.js deleted file mode 100644 index 32083338c3..0000000000 --- a/src/resources/html-import/import-href.js +++ /dev/null @@ -1,101 +0,0 @@ -/* eslint-disable */ -import "./polyfill"; -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -// run a callback when HTMLImports are ready or immediately if -// this api is not available. -function whenImportsReady(cb) { - if (window.HTMLImports) { - HTMLImports.whenReady(cb); - } else { - cb(); - } -} - -/** - * Convenience method for importing an HTML document imperatively. - * - * This method creates a new `` element with - * the provided URL and appends it to the document to start loading. - * In the `onload` callback, the `import` property of the `link` - * element will contain the imported document contents. - * - * @param {string} href URL to document to load. - * @param {?function(!Event):void=} onload Callback to notify when an import successfully - * loaded. - * @param {?function(!ErrorEvent):void=} onerror Callback to notify when an import - * unsuccessfully loaded. - * @param {boolean=} optAsync True if the import should be loaded `async`. - * Defaults to `false`. - * @return {!HTMLLinkElement} The link element for the URL to be loaded. - */ -export const importHref = function (href, onload, onerror, optAsync) { - let link /** @type {HTMLLinkElement} */ = document.head.querySelector( - 'link[href="' + href + '"][import-href]' - ); - if (!link) { - link = /** @type {HTMLLinkElement} */ (document.createElement("link")); - link.rel = "import"; - link.href = href; - link.setAttribute("import-href", ""); - } - // always ensure link has `async` attribute if user specified one, - // even if it was previously not async. This is considered less confusing. - if (optAsync) { - link.setAttribute("async", ""); - } - // NOTE: the link may now be in 3 states: (1) pending insertion, - // (2) inflight, (3) already loaded. In each case, we need to add - // event listeners to process callbacks. - const cleanup = function () { - link.removeEventListener("load", loadListener); - link.removeEventListener("error", errorListener); - }; - let loadListener = function (event) { - cleanup(); - // In case of a successful load, cache the load event on the link so - // that it can be used to short-circuit this method in the future when - // it is called with the same href param. - link.__dynamicImportLoaded = true; - if (onload) { - whenImportsReady(() => { - onload(event); - }); - } - }; - let errorListener = function (event) { - cleanup(); - // In case of an error, remove the link from the document so that it - // will be automatically created again the next time `importHref` is - // called. - if (link.parentNode) { - link.parentNode.removeChild(link); - } - if (onerror) { - whenImportsReady(() => { - onerror(event); - }); - } - }; - link.addEventListener("load", loadListener); - link.addEventListener("error", errorListener); - if (link.parentNode == null) { - document.head.appendChild(link); - // if the link already loaded, dispatch a fake load event - // so that listeners are called and get a proper event argument. - } else if (link.__dynamicImportLoaded) { - link.dispatchEvent(new Event("load")); - } - return link; -}; - -export const importHrefPromise = (href) => - new Promise((resolve, reject) => importHref(href, resolve, reject)); diff --git a/src/resources/html-import/polyfill.js b/src/resources/html-import/polyfill.js deleted file mode 100644 index 51844ed272..0000000000 --- a/src/resources/html-import/polyfill.js +++ /dev/null @@ -1,451 +0,0 @@ -/* eslint-disable */ -/* - Copyright (c) 2016 The Polymer Project Authors. All rights reserved. - This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - Code distributed by Google as part of the polymer project is also - subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -(function (q) { - function y(a, b) { - if ("function" === typeof window.CustomEvent) return new CustomEvent(a, b); - var c = document.createEvent("CustomEvent"); - c.initCustomEvent(a, !!b.bubbles, !!b.cancelable, b.detail); - return c; - } - function m(a) { - if (u) return a.ownerDocument !== document ? a.ownerDocument : null; - var b = a.__importDoc; - if (!b && a.parentNode) { - b = a.parentNode; - if ("function" === typeof b.closest) b = b.closest("link[rel=import]"); - else for (; !r(b) && (b = b.parentNode); ); - a.__importDoc = b; - } - return b; - } - function D(a) { - var b = k(document, "link[rel=import]:not([import-dependency])"), - c = b.length; - c - ? g(b, function (b) { - return t(b, function () { - 0 === --c && a(); - }); - }) - : a(); - } - function z(a) { - function b() { - "loading" !== document.readyState && - document.body && - (document.removeEventListener("readystatechange", b), a()); - } - document.addEventListener("readystatechange", b); - b(); - } - function A(a) { - z(function () { - return D(function () { - return a && a(); - }); - }); - } - function t(a, b) { - if (a.__loaded) b && b(); - else if ( - ("script" === a.localName && !a.src) || - ("style" === a.localName && !a.firstChild) - ) - (a.__loaded = !0), b && b(); - else { - var c = function (d) { - a.removeEventListener(d.type, c); - a.__loaded = !0; - b && b(); - }; - a.addEventListener("load", c); - (v && "style" === a.localName) || a.addEventListener("error", c); - } - } - function r(a) { - return ( - a.nodeType === Node.ELEMENT_NODE && - "link" === a.localName && - "import" === a.rel - ); - } - function h() { - var a = this; - this.a = {}; - this.b = 0; - this.g = new MutationObserver(function (b) { - return a.w(b); - }); - this.g.observe(document.head, { childList: !0, subtree: !0 }); - this.loadImports(document); - } - function B(a) { - g(k(a, "template"), function (a) { - g( - k( - a.content, - 'script:not([type]),script[type="application/javascript"],script[type="text/javascript"]' - ), - function (a) { - var b = document.createElement("script"); - g(a.attributes, function (a) { - return b.setAttribute(a.name, a.value); - }); - b.textContent = a.textContent; - a.parentNode.replaceChild(b, a); - } - ); - B(a.content); - }); - } - function k(a, b) { - return a.childNodes.length ? a.querySelectorAll(b) : E; - } - function g(a, b, c) { - var d = a ? a.length : 0, - f = c ? -1 : 1; - for (c = c ? d - 1 : 0; c < d && 0 <= c; c += f) b(a[c], c); - } - var n = document.createElement("link"), - u = "import" in n, - E = n.querySelectorAll("*"), - w = null; - !1 === "currentScript" in document && - Object.defineProperty(document, "currentScript", { - get: function () { - return ( - w || - ("complete" !== document.readyState - ? document.scripts[document.scripts.length - 1] - : null) - ); - }, - configurable: !0, - }); - var F = /(url\()([^)]*)(\))/g, - G = /(@import[\s]+(?!url\())([^;]*)(;)/g, - H = /(]*)(rel=['|"]?stylesheet['|"]?[^>]*>)/g, - e = { - u: function (a, b) { - a.href && a.setAttribute("href", e.c(a.getAttribute("href"), b)); - a.src && a.setAttribute("src", e.c(a.getAttribute("src"), b)); - if ("style" === a.localName) { - var c = e.o(a.textContent, b, F); - a.textContent = e.o(c, b, G); - } - }, - o: function (a, b, c) { - return a.replace(c, function (a, c, l, g) { - a = l.replace(/["']/g, ""); - b && (a = e.c(a, b)); - return c + "'" + a + "'" + g; - }); - }, - c: function (a, b) { - if (void 0 === e.f) { - e.f = !1; - try { - var c = new URL("b", "http://a"); - c.pathname = "c%20d"; - e.f = "http://a/c%20d" === c.href; - } catch (d) {} - } - if (e.f) return new URL(a, b).href; - c = e.s; - c || - ((c = document.implementation.createHTMLDocument("temp")), - (e.s = c), - (c.i = c.createElement("base")), - c.head.appendChild(c.i), - (c.h = c.createElement("a"))); - c.i.href = b; - c.h.href = a; - return c.h.href || a; - }, - }, - C = { - async: !0, - load: function (a, b, c) { - if (a) - if (a.match(/^data:/)) { - a = a.split(","); - var d = a[1]; - d = -1 < a[0].indexOf(";base64") ? atob(d) : decodeURIComponent(d); - b(d); - } else { - var f = new XMLHttpRequest(); - f.open("GET", a, C.async); - f.onload = function () { - var a = f.responseURL || f.getResponseHeader("Location"); - a && - 0 === a.indexOf("/") && - (a = - (location.origin || - location.protocol + "//" + location.host) + a); - var d = f.response || f.responseText; - 304 === f.status || - 0 === f.status || - (200 <= f.status && 300 > f.status) - ? b(d, a) - : c(d); - }; - f.send(); - } - else c("error: href must be specified"); - }, - }, - v = - /Trident/.test(navigator.userAgent) || - /Edge\/\d./i.test(navigator.userAgent); - h.prototype.loadImports = function (a) { - var b = this; - g(k(a, "link[rel=import]"), function (a) { - return b.l(a); - }); - }; - h.prototype.l = function (a) { - var b = this, - c = a.href; - if (void 0 !== this.a[c]) { - var d = this.a[c]; - d && d.__loaded && ((a.__import = d), this.j(a)); - } else - this.b++, - (this.a[c] = "pending"), - C.load( - c, - function (a, d) { - a = b.A(a, d || c); - b.a[c] = a; - b.b--; - b.loadImports(a); - b.m(); - }, - function () { - b.a[c] = null; - b.b--; - b.m(); - } - ); - }; - h.prototype.A = function (a, b) { - if (!a) return document.createDocumentFragment(); - v && - (a = a.replace(H, function (a, b, c) { - return -1 === a.indexOf("type=") ? b + " type=import-disable " + c : a; - })); - var c = document.createElement("template"); - c.innerHTML = a; - if (c.content) (a = c.content), B(a); - else - for (a = document.createDocumentFragment(); c.firstChild; ) - a.appendChild(c.firstChild); - if ((c = a.querySelector("base"))) - (b = e.c(c.getAttribute("href"), b)), c.removeAttribute("href"); - var d = 0; - g( - k( - a, - 'link[rel=import],link[rel=stylesheet][href][type=import-disable],style:not([type]),link[rel=stylesheet][href]:not([type]),script:not([type]),script[type="application/javascript"],script[type="text/javascript"]' - ), - function (a) { - t(a); - e.u(a, b); - a.setAttribute("import-dependency", ""); - "script" === a.localName && - !a.src && - a.textContent && - (a.setAttribute( - "src", - "data:text/javascript;charset=utf-8," + - encodeURIComponent( - a.textContent + - ("\n//# sourceURL=" + b + (d ? "-" + d : "") + ".js\n") - ) - ), - (a.textContent = ""), - d++); - } - ); - return a; - }; - h.prototype.m = function () { - var a = this; - if (!this.b) { - this.g.disconnect(); - this.flatten(document); - var b = !1, - c = !1, - d = function () { - c && - b && - (a.loadImports(document), - a.b || - (a.g.observe(document.head, { childList: !0, subtree: !0 }), - a.v())); - }; - this.C(function () { - c = !0; - d(); - }); - this.B(function () { - b = !0; - d(); - }); - } - }; - h.prototype.flatten = function (a) { - var b = this; - g(k(a, "link[rel=import]"), function (a) { - var c = b.a[a.href]; - (a.__import = c) && - c.nodeType === Node.DOCUMENT_FRAGMENT_NODE && - ((b.a[a.href] = a), - (a.readyState = "loading"), - (a.__import = a), - b.flatten(c), - a.appendChild(c)); - }); - }; - h.prototype.B = function (a) { - function b(f) { - if (f < d) { - var l = c[f], - e = document.createElement("script"); - l.removeAttribute("import-dependency"); - g(l.attributes, function (a) { - return e.setAttribute(a.name, a.value); - }); - w = e; - l.parentNode.replaceChild(e, l); - t(e, function () { - w = null; - b(f + 1); - }); - } else a(); - } - var c = k(document, "script[import-dependency]"), - d = c.length; - b(0); - }; - h.prototype.C = function (a) { - var b = k( - document, - "style[import-dependency],link[rel=stylesheet][import-dependency]" - ), - c = b.length; - if (c) { - var d = - v && - !!document.querySelector( - "link[rel=stylesheet][href][type=import-disable]" - ); - g(b, function (b) { - t(b, function () { - b.removeAttribute("import-dependency"); - 0 === --c && a(); - }); - if (d && b.parentNode !== document.head) { - var e = document.createElement(b.localName); - e.__appliedElement = b; - e.setAttribute("type", "import-placeholder"); - b.parentNode.insertBefore(e, b.nextSibling); - for (e = m(b); e && m(e); ) e = m(e); - e.parentNode !== document.head && (e = null); - document.head.insertBefore(b, e); - b.removeAttribute("type"); - } - }); - } else a(); - }; - h.prototype.v = function () { - var a = this; - g( - k(document, "link[rel=import]"), - function (b) { - return a.j(b); - }, - !0 - ); - }; - h.prototype.j = function (a) { - a.__loaded || - ((a.__loaded = !0), - a.import && (a.import.readyState = "complete"), - a.dispatchEvent( - y(a.import ? "load" : "error", { - bubbles: !1, - cancelable: !1, - detail: void 0, - }) - )); - }; - h.prototype.w = function (a) { - var b = this; - g(a, function (a) { - return g(a.addedNodes, function (a) { - a && - a.nodeType === Node.ELEMENT_NODE && - (r(a) ? b.l(a) : b.loadImports(a)); - }); - }); - }; - var x = null; - if (u) - g(k(document, "link[rel=import]"), function (a) { - (a.import && "loading" === a.import.readyState) || (a.__loaded = !0); - }), - (n = function (a) { - a = a.target; - r(a) && (a.__loaded = !0); - }), - document.addEventListener("load", n, !0), - document.addEventListener("error", n, !0); - else { - var p = Object.getOwnPropertyDescriptor(Node.prototype, "baseURI"); - Object.defineProperty( - (!p || p.configurable ? Node : Element).prototype, - "baseURI", - { - get: function () { - var a = r(this) ? this : m(this); - return a - ? a.href - : p && p.get - ? p.get.call(this) - : (document.querySelector("base") || window.location).href; - }, - configurable: !0, - enumerable: !0, - } - ); - Object.defineProperty(HTMLLinkElement.prototype, "import", { - get: function () { - return this.__import || null; - }, - configurable: !0, - enumerable: !0, - }); - z(function () { - x = new h(); - }); - } - A(function () { - return document.dispatchEvent( - y("HTMLImportsLoaded", { cancelable: !0, bubbles: !0, detail: void 0 }) - ); - }); - q.useNative = u; - q.whenReady = A; - q.importForElement = m; - q.loadImports = function (a) { - x && x.loadImports(a); - }; -})((window.HTMLImports = window.HTMLImports || {})); diff --git a/src/util/custom-panel/load-custom-panel.ts b/src/util/custom-panel/load-custom-panel.ts index c674e0f4ed..1286b6588f 100644 --- a/src/util/custom-panel/load-custom-panel.ts +++ b/src/util/custom-panel/load-custom-panel.ts @@ -47,17 +47,6 @@ export const loadCustomPanel = ( ): Promise => { const panelSource = getUrl(panelConfig); - if (panelSource.type === "html") { - const toLoad = [ - import( - /* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href" - ), - ]; - - return Promise.all(toLoad).then(([{ importHrefPromise }]) => - importHrefPromise(panelSource.url) - ); - } if (panelSource.type === "js") { if (!(panelSource.url in JS_CACHE)) { JS_CACHE[panelSource.url] = loadJS(panelSource.url); From 4e676b1dba0854c5fc0303c735ca618af78a8c5c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Sep 2020 16:17:01 +0200 Subject: [PATCH 050/146] Fix light more info (#6855) --- src/components/ha-color-picker.js | 11 +++++++++++ src/dialogs/more-info/controls/more-info-light.ts | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/ha-color-picker.js b/src/components/ha-color-picker.js index 49fce6c7f5..a50601c0ea 100644 --- a/src/components/ha-color-picker.js +++ b/src/components/ha-color-picker.js @@ -176,6 +176,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) { this.drawColorWheel(); this.drawMarker(); + if (this.desiredHsColor) { + this.setMarkerOnColor(this.desiredHsColor); + this.applyColorToCanvas(this.desiredHsColor); + } + this.interactionLayer.addEventListener("mousedown", (ev) => this.onMouseDown(ev) ); @@ -319,6 +324,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) { // set marker position to the given color setMarkerOnColor(hs) { + if (!this.marker || !this.tooltip) { + return; + } const dist = hs.s * this.radius; const theta = ((hs.h - 180) / 180) * Math.PI; const markerdX = -dist * Math.cos(theta); @@ -330,6 +338,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) { // apply given color to interface elements applyColorToCanvas(hs) { + if (!this.interactionLayer) { + return; + } // we're not really converting hs to hsl here, but we keep it cheap // setting the color on the interactionLayer, the svg elements can inherit this.interactionLayer.style.color = `hsl(${hs.h}, 100%, ${ diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 6b0bba5572..772d0ea29d 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -134,7 +134,7 @@ class MoreInfoLight extends LitElement { attr-for-selected="item-name" >${this.stateObj.attributes.effect_list.map( (effect: string) => html` - ${effect} ` @@ -170,7 +170,7 @@ class MoreInfoLight extends LitElement { } private _effectChanged(ev: CustomEvent) { - const newVal = ev.detail.value; + const newVal = ev.detail.item.itemName; if (!newVal || this.stateObj!.attributes.effect === newVal) { return; From 909cff215836cf69cd10f2fd1dfff62d38150882 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Sep 2020 17:01:04 +0200 Subject: [PATCH 051/146] Fix timer entity display (#6849) --- src/common/entity/timer_time_remaining.ts | 7 ++++++- src/dialogs/more-info/controls/more-info-timer.ts | 13 +++++-------- .../lovelace/entity-rows/hui-timer-entity-row.ts | 6 ++++-- src/state-summary/state-card-timer.js | 8 +++++--- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/common/entity/timer_time_remaining.ts b/src/common/entity/timer_time_remaining.ts index cd7c90ef8f..5b2f54654d 100644 --- a/src/common/entity/timer_time_remaining.ts +++ b/src/common/entity/timer_time_remaining.ts @@ -1,7 +1,12 @@ import { HassEntity } from "home-assistant-js-websocket"; import durationToSeconds from "../datetime/duration_to_seconds"; -export const timerTimeRemaining = (stateObj: HassEntity) => { +export const timerTimeRemaining = ( + stateObj: HassEntity +): undefined | number => { + if (!stateObj.attributes.remaining) { + return undefined; + } let timeRemaining = durationToSeconds(stateObj.attributes.remaining); if (stateObj.state === "active") { diff --git a/src/dialogs/more-info/controls/more-info-timer.ts b/src/dialogs/more-info/controls/more-info-timer.ts index 809d697c70..c06f549896 100644 --- a/src/dialogs/more-info/controls/more-info-timer.ts +++ b/src/dialogs/more-info/controls/more-info-timer.ts @@ -26,15 +26,12 @@ class MoreInfoTimer extends LitElement { return html`
${this.stateObj.state === "idle" || this.stateObj.state === "paused" ? html` - + ${this.hass!.localize("ui.card.timer.actions.start")} ` @@ -42,7 +39,7 @@ class MoreInfoTimer extends LitElement { ${this.stateObj.state === "active" ? html` ${this.hass!.localize("ui.card.timer.actions.pause")} @@ -52,13 +49,13 @@ class MoreInfoTimer extends LitElement { ${this.stateObj.state === "active" || this.stateObj.state === "paused" ? html` ${this.hass!.localize("ui.card.timer.actions.cancel")} ${this.hass!.localize("ui.card.timer.actions.finish")} diff --git a/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts b/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts index e744313f26..b77225c28d 100644 --- a/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-timer-entity-row.ts @@ -2,9 +2,9 @@ import { HassEntity } from "home-assistant-js-websocket"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -125,7 +125,9 @@ class HuiTimerEntityRow extends LitElement { } if (stateObj.state === "idle" || this._timeRemaining === 0) { - return this.hass!.localize("state.timer." + stateObj.state); + return ( + this.hass!.localize(`state.timer.${stateObj.state}`) || stateObj.state + ); } let display = secondsToDuration(this._timeRemaining || 0); diff --git a/src/state-summary/state-card-timer.js b/src/state-summary/state-card-timer.js index 96586e8804..17d39fde5c 100644 --- a/src/state-summary/state-card-timer.js +++ b/src/state-summary/state-card-timer.js @@ -23,7 +23,7 @@ class StateCardTimer extends PolymerElement {
${this.stateInfoTemplate} -
[[_secondsToDuration(timeRemaining)]]
+
[[_displayState(timeRemaining, stateObj)]]
`; } @@ -90,8 +90,10 @@ class StateCardTimer extends PolymerElement { this.timeRemaining = timerTimeRemaining(stateObj); } - _secondsToDuration(time) { - return secondsToDuration(time); + _displayState(time, stateObj) { + return time + ? secondsToDuration(time) + : this.hass.localize(`state.timer.${stateObj.state}`) || stateObj.state; } } customElements.define("state-card-timer", StateCardTimer); From 4d0d1ed2a1d8705abb9f9d936a996ca6e3d0ecef Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 8 Sep 2020 13:52:21 -0500 Subject: [PATCH 052/146] Undo my commit into dev (#6864) --- src/dialogs/more-info/controls/more-info-light.ts | 6 +++--- src/dialogs/more-info/ha-more-info-tab-history.ts | 13 +------------ 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 605e3210f5..772d0ea29d 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -105,12 +105,12 @@ class MoreInfoLight extends LitElement { ? html`
`; diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts index eeb00cef33..18624a11ef 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -85,18 +85,7 @@ export class MoreInfoTabHistoryDialog extends LitElement { clearInterval(this._historyRefreshInterval); } - if (!this.hass) { - return; - } - - const oldHass = changedProps.get("hass") as HomeAssistant; - const newStateObj = this.hass.states[this.entityId]; - const oldStateObj = oldHass?.states[this.entityId]; - - if ( - changedProps.has("entityId") || - (oldHass && newStateObj !== oldStateObj) - ) { + if (changedProps.has("entityId")) { this._stateHistory = undefined; this._entries = undefined; From 869b7c85cafddefa81da4ba47702de1dd4a25bc9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 8 Sep 2020 13:56:45 -0500 Subject: [PATCH 053/146] Ensure we pickup all the reloadable domains (#6861) --- src/common/config/components_with_service.ts | 9 ++++ .../ha-config-server-control.ts | 50 ++++++++----------- 2 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 src/common/config/components_with_service.ts diff --git a/src/common/config/components_with_service.ts b/src/common/config/components_with_service.ts new file mode 100644 index 0000000000..9775b8c6f6 --- /dev/null +++ b/src/common/config/components_with_service.ts @@ -0,0 +1,9 @@ +import { HomeAssistant } from "../../types"; + +/** Return an array of domains with the service. */ +export const componentsWithService = ( + hass: HomeAssistant, + service: string +): Array => + hass && + Object.keys(hass.services).filter((key) => service in hass.services[key]); diff --git a/src/panels/config/server_control/ha-config-server-control.ts b/src/panels/config/server_control/ha-config-server-control.ts index 04fbc5b237..62622baeab 100644 --- a/src/panels/config/server_control/ha-config-server-control.ts +++ b/src/panels/config/server_control/ha-config-server-control.ts @@ -12,7 +12,7 @@ import { property, TemplateResult, } from "lit-element"; -import { isServiceLoaded } from "../../../common/config/is_service_loaded"; +import { componentsWithService } from "../../../common/config/components_with_service"; import "../../../components/buttons/ha-call-service-button"; import "../../../components/ha-card"; import { checkCoreConfig } from "../../../data/core"; @@ -23,8 +23,6 @@ import { HomeAssistant, Route } from "../../../types"; import "../ha-config-section"; import { configSections } from "../ha-panel-config"; -const platformsWithReload = ["template"]; - @customElement("ha-config-server-control") export class HaConfigServerControl extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -51,13 +49,10 @@ export class HaConfigServerControl extends LitElement { changedProperties.has("hass") && (!oldHass || oldHass.config.components !== this.hass.config.components) ) { - this._reloadableDomains = this.hass.config.components - .concat(platformsWithReload) - .filter( - (component) => - !component.includes(".") && - isServiceLoaded(this.hass, component, "reload") - ); + this._reloadableDomains = componentsWithService( + this.hass, + "reload" + ).sort(); } } @@ -207,24 +202,23 @@ export class HaConfigServerControl extends LitElement { )}
- ${this._reloadableDomains.map((domain) => - isServiceLoaded(this.hass, domain, "reload") - ? html`
- ${this.hass.localize( - `ui.panel.config.server_control.section.reloading.${domain}` - ) || - this.hass.localize( - "ui.panel.config.server_control.section.reloading.reload", - "domain", - domainToName(this.hass.localize, domain) - )} - -
` - : "" + ${this._reloadableDomains.map( + (domain) => + html`
+ ${this.hass.localize( + `ui.panel.config.server_control.section.reloading.${domain}` + ) || + this.hass.localize( + "ui.panel.config.server_control.section.reloading.reload", + "domain", + domainToName(this.hass.localize, domain) + )} + +
` )} ` From b30ee884a72fe39f87e75fbe80aceeba7714c2ac Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 8 Sep 2020 13:57:17 -0500 Subject: [PATCH 054/146] Fix for Camera streams that don't support stream (#6863) --- src/components/ha-camera-stream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index a8d34c8a18..51794b3ad3 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -66,7 +66,7 @@ class HaCameraStream extends LitElement { } protected updated(changedProps: PropertyValues): void { - if (changedProps.has("stateObj")) { + if (changedProps.has("stateObj") && !this._shouldRenderMJPEG) { this._forceMJPEG = undefined; this._getStreamUrl(); } From 0e021e7d7def0c84f557f1dfec4562bbaf9d4d7d Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 8 Sep 2020 20:58:47 +0200 Subject: [PATCH 055/146] Bumped version to 20200908.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b1a1ef7a73..8bec01eb50 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200907.0", + version="20200908.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From 7c5a78a1cf692eeac3757ddbae2080ae10bf7401 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Tue, 8 Sep 2020 23:36:44 +0200 Subject: [PATCH 056/146] Media player visual improvements (#6817) Co-authored-by: Bram Kragten --- .../media-player/ha-media-player-browse.ts | 12 +++++++++++- src/panels/media-browser/ha-panel-media-browser.ts | 14 ++++++-------- .../hui-dialog-select-media-player.ts | 2 -- .../hui-dialog-web-browser-play-media.ts | 2 -- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 0d9cde1946..ad80b0d524 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -335,6 +335,7 @@ export class HaMediaPlayerBrowse extends LitElement { .item=${child} graphic="avatar" hasMeta + dir=${computeRTLDirection(this.hass)} >
- ${child.title} + ${child.title}
  • ` @@ -620,6 +621,7 @@ export class HaMediaPlayerBrowse extends LitElement { mwc-list { --mdc-list-vertical-padding: 0; + --mdc-list-item-graphic-margin: 0; --mdc-theme-text-icon-on-background: var(--secondary-text-color); margin-top: 10px; } @@ -728,6 +730,14 @@ export class HaMediaPlayerBrowse extends LitElement { background-color: transparent; } + mwc-list-item .title { + margin-left: 16px; + } + mwc-list-item[dir="rtl"] .title { + margin-right: 16px; + margin-left: 0; + } + /* ============= Narrow ============= */ :host([narrow]) { diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index 33cd43dd0c..a58243857e 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -64,14 +64,12 @@ class PanelMediaBrowser extends LitElement { "ui.components.media-browser.media-player-browser" )}
    - - - + + + ${this.hass.localize( + "ui.components.media-browser.choose_player" + )} +
    diff --git a/src/panels/media-browser/hui-dialog-select-media-player.ts b/src/panels/media-browser/hui-dialog-select-media-player.ts index 0c45026e81..d19cebe0ee 100644 --- a/src/panels/media-browser/hui-dialog-select-media-player.ts +++ b/src/panels/media-browser/hui-dialog-select-media-player.ts @@ -40,8 +40,6 @@ export class HuiDialogSelectMediaPlayer extends LitElement { return html` Date: Wed, 9 Sep 2020 00:32:22 +0000 Subject: [PATCH 057/146] [ci skip] Translation update --- translations/frontend/ca.json | 23 +++++++++- translations/frontend/cs.json | 23 +++++++++- translations/frontend/es.json | 23 +++++++++- translations/frontend/fr.json | 16 ++++++- translations/frontend/it.json | 68 +++++++++++++++++++++++++++--- translations/frontend/nb.json | 23 +++++++++- translations/frontend/nl.json | 34 ++++++++++++++- translations/frontend/ru.json | 36 +++++++++++++--- translations/frontend/zh-Hans.json | 28 ++++++++++-- 9 files changed, 248 insertions(+), 26 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 9e2888ab08..61e6b7e55d 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -553,6 +553,10 @@ "toggle": "Commuta" }, "entity": { + "entity-attribute-picker": { + "attribute": "Atribut", + "show_attributes": "Mostra els atributs" + }, "entity-picker": { "clear": "Esborra", "entity": "Entitat", @@ -929,7 +933,13 @@ "label": "Crida servei", "service_data": "Dades de servei" }, + "wait_for_trigger": { + "continue_timeout": "Continua amb temps d'espera", + "label": "Espera disparador", + "timeout": "Temps màxim d'espera (opcional)" + }, "wait_template": { + "continue_timeout": "Continua amb temps d'espera", "label": "Espera", "timeout": "Temps màxim d'espera (opcional)", "wait_template": "Plantilla d'espera" @@ -993,7 +1003,9 @@ "time": { "after": "Després", "before": "Abans", - "label": "Temporal" + "label": "Temporal", + "type_input": "Valor d'un ajudant de data/hora", + "type_value": "Temps fix" }, "zone": { "entity": "Entitat amb ubicació", @@ -1081,6 +1093,7 @@ "value_template": "Plantilla de valor (opcional)" }, "state": { + "attribute": "Atribut (opcional)", "for": "Durant", "from": "Des de", "label": "Estat", @@ -1108,7 +1121,9 @@ }, "time": { "at": "A les", - "label": "Temporal" + "label": "Temporal", + "type_input": "Valor d'un ajudant de data/hora", + "type_value": "Temps fix" }, "webhook": { "label": "Webhook", @@ -1131,6 +1146,8 @@ "add_automation": "Afegeix automatització", "delete_automation": "Elimina l'automatització", "delete_confirm": "Estàs segur que vols eliminar aquesta automatització?", + "duplicate": "Duplica", + "duplicate_automation": "Duplica l'automatització", "edit_automation": "Edita automatització", "header": "Editor d'automatitzacions", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "Afegeix integració", + "attention": "Cal atenció", "caption": "Integracions", "config_entry": { "area": "A {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "Ajusta els paràmetres de cerca.", "note_about_integrations": "Encara no es poden configurar totes les integracions a través de la UI.", "note_about_website_reference": "N'hi ha més disponibles al ", + "reconfigure": "Reconfigura", "rename_dialog": "Edita el nom de l'entrada de configuració", "rename_input_label": "Nom de l'entrada", "search": "Cerca integracions" diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index e8048e50a0..2c11c2fd88 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -553,6 +553,10 @@ "toggle": "Přepnout" }, "entity": { + "entity-attribute-picker": { + "attribute": "Atribut", + "show_attributes": "Zobrazit atributy" + }, "entity-picker": { "clear": "Zrušit", "entity": "Entita", @@ -929,7 +933,13 @@ "label": "Zavolat službu", "service_data": "Data služby" }, + "wait_for_trigger": { + "continue_timeout": "Pokračovat po časovém limitu", + "label": "Počkat na spouštěč", + "timeout": "Časový limit (volitelný)" + }, "wait_template": { + "continue_timeout": "Pokračovat po časovém limitu", "label": "Čekání", "timeout": "Časový limit (volitelné)", "wait_template": "Šablona pro čekání" @@ -993,7 +1003,9 @@ "time": { "after": "Po", "before": "Před", - "label": "Čas" + "label": "Čas", + "type_input": "Hodnota pomocníka \"Datum a/nebo čas\"", + "type_value": "Pevný čas" }, "zone": { "entity": "Entita s umístěním", @@ -1081,6 +1093,7 @@ "value_template": "Šablona hodnoty (volitelné)" }, "state": { + "attribute": "Atribut (volitelný)", "for": "Po dobu", "from": "Z", "label": "Stav", @@ -1108,7 +1121,9 @@ }, "time": { "at": "V", - "label": "Čas" + "label": "Čas", + "type_input": "Hodnota pomocníka \"Datum a/nebo čas\"", + "type_value": "Pevný čas" }, "webhook": { "label": "Webhook", @@ -1131,6 +1146,8 @@ "add_automation": "Přidat automatizaci", "delete_automation": "Odstranit automatizaci", "delete_confirm": "Opravdu chcete odstranit tuto automatizaci?", + "duplicate": "Duplikovat", + "duplicate_automation": "Duplikovat automatizaci", "edit_automation": "Upravit automatizaci", "header": "Editor automatizací", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "Přidat integraci", + "attention": "Vyžadována pozornost", "caption": "Integrace", "config_entry": { "area": "V {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "Upravte kritéria vyhledávání.", "note_about_integrations": "Ne všechny integrace lze prozatím konfigurovat prostřednictvím uživatelského rozhraní.", "note_about_website_reference": "Další jsou k dispozici na ", + "reconfigure": "Překonfigurovat", "rename_dialog": "Upravit název této položky nastavení", "rename_input_label": "Název položky", "search": "Hledat integraci" diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 9b22dc0cde..93eaa86477 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -553,6 +553,10 @@ "toggle": "Interruptor" }, "entity": { + "entity-attribute-picker": { + "attribute": "Atributo", + "show_attributes": "Mostrar atributos" + }, "entity-picker": { "clear": "Limpiar", "entity": "Entidad", @@ -929,7 +933,13 @@ "label": "Llamar servicio", "service_data": "Datos de servicio" }, + "wait_for_trigger": { + "continue_timeout": "Continuar tras el límite de tiempo", + "label": "Esperar al disparador", + "timeout": "Límite de tiempo (opcional)" + }, "wait_template": { + "continue_timeout": "Continuar tras el límite de tiempo", "label": "Esperar", "timeout": "Límite de tiempo (opcional)", "wait_template": "Plantilla de espera" @@ -993,7 +1003,9 @@ "time": { "after": "Después de", "before": "Antes de", - "label": "Hora" + "label": "Hora", + "type_input": "Valor de un ayudante de fecha/hora", + "type_value": "Horario fijo" }, "zone": { "entity": "Entidad con la ubicación", @@ -1081,6 +1093,7 @@ "value_template": "Valor de la plantilla (opcional)" }, "state": { + "attribute": "Atributo (Opcional)", "for": "Durante", "from": "De", "label": "Estado", @@ -1108,7 +1121,9 @@ }, "time": { "at": "A las", - "label": "Hora" + "label": "Hora", + "type_input": "Valor de un ayudante de fecha/hora", + "type_value": "Horario fijo" }, "webhook": { "label": "Webhook", @@ -1131,6 +1146,8 @@ "add_automation": "Añadir automatización", "delete_automation": "Eliminar la automatización", "delete_confirm": "¿Estás seguro de que quieres eliminar esta automatización?", + "duplicate": "Duplicar", + "duplicate_automation": "Duplicar la automatización", "edit_automation": "Editar automatización", "header": "Editor de automatización", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "Añadir integración", + "attention": "Atención requerida", "caption": "Integraciones", "config_entry": { "area": "En {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "Ajusta tus criterios de búsqueda.", "note_about_integrations": "Todavía no se pueden configurar todas las integraciones a través de la interfaz de usuario.", "note_about_website_reference": "Hay más disponibles en el ", + "reconfigure": "Reconfigurar", "rename_dialog": "Edita el nombre de esta entrada de configuración", "rename_input_label": "Nombre de la entrada", "search": "Buscar integraciones" diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 8af22d4ab6..714fc4320a 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -553,6 +553,10 @@ "toggle": "Permuter" }, "entity": { + "entity-attribute-picker": { + "attribute": "Attribut", + "show_attributes": "Afficher les attributs" + }, "entity-picker": { "clear": "Effacer", "entity": "Entité", @@ -929,6 +933,9 @@ "label": "Appeler un service", "service_data": "Données du service" }, + "wait_for_trigger": { + "label": "Attendre le déclencheur" + }, "wait_template": { "label": "Attendre", "timeout": "Délai d'expiration (optionnel)", @@ -1081,6 +1088,7 @@ "value_template": "Contenu du template (optionnel)" }, "state": { + "attribute": "Attribut (facultatif)", "for": "Pendant", "from": "De", "label": "État", @@ -1108,7 +1116,9 @@ }, "time": { "at": "À", - "label": "Heure" + "label": "Heure", + "type_input": "Valeur d'une aide de date/heure", + "type_value": "Temps fixe" }, "webhook": { "label": "Webhook", @@ -1131,6 +1141,8 @@ "add_automation": "Ajouter une automatisation", "delete_automation": "Supprimer l'automatisation", "delete_confirm": "Voulez-vous vraiment supprimer cette automatisation ?", + "duplicate": "Dupliquer", + "duplicate_automation": "Dupliquer l'automatisation", "edit_automation": "Modifier l'automatisation", "header": "Éditeur d'automatisation", "headers": { @@ -1531,6 +1543,7 @@ }, "integrations": { "add_integration": "Ajouter l'intégration", + "attention": "Attention requise", "caption": "Intégrations", "config_entry": { "area": "Dans {area}", @@ -1600,6 +1613,7 @@ "none_found_detail": "Ajustez vos critères de recherche.", "note_about_integrations": "Toutes les intégrations ne peuvent pas encore être configurées via l'interface utilisateur.", "note_about_website_reference": "D'autres sont disponibles sur le ", + "reconfigure": "Reconfigurer", "rename_dialog": "Modifier le nom de cette entrée de configuration", "rename_input_label": "Nom de l'entrée", "search": "Chercher les intégrations" diff --git a/translations/frontend/it.json b/translations/frontend/it.json index 13c8d8aeaf..fa2f1fb908 100644 --- a/translations/frontend/it.json +++ b/translations/frontend/it.json @@ -19,6 +19,7 @@ "logbook": "Registro", "mailbox": "Posta", "map": "Mappa", + "media_browser": "Browser multimediale", "profile": "Profilo", "shopping_list": "Lista della spesa", "states": "Panoramica" @@ -505,6 +506,7 @@ "back": "Indietro", "cancel": "Annulla", "close": "Chiudi", + "continue": "Continua", "delete": "Elimina", "error_required": "Necessario", "loading": "Caricamento", @@ -551,6 +553,10 @@ "toggle": "Azionare" }, "entity": { + "entity-attribute-picker": { + "attribute": "Attributo", + "show_attributes": "Mostra attributi" + }, "entity-picker": { "clear": "Cancella", "entity": "Entità", @@ -562,6 +568,8 @@ "no_history_found": "Nessuno storico trovato." }, "media-browser": { + "audio_not_supported": "Il tuo browser non supporta l'elemento audio.", + "choose_player": "Scegli il lettore", "choose-source": "Scegli origine", "content-type": { "album": "Album", @@ -570,12 +578,17 @@ "playlist": "Elenco di riproduzione", "server": "Server" }, + "media_browsing_error": "Errore di navigazione multimediale", + "media_not_supported": "Il Browser Media Player non supporta questo tipo di file multimediali", + "media_player": "Lettore multimediale", "media-player-browser": "Lettore multimediale", "no_items": "Nessun elemento", "pick": "Scegli", "pick-media": "Seleziona file multimediali", "play": "Riproduci", - "play-media": "Riproduci file multimediali" + "play-media": "Riproduci file multimediali", + "video_not_supported": "Il tuo browser non supporta l'elemento video.", + "web-browser": "Web Browser" }, "picture-upload": { "label": "Immagine", @@ -689,8 +702,10 @@ "crop": "Ritaglia" }, "more_info_control": { + "controls": "Controlli", "dismiss": "Chiudi finestra di dialogo", "edit": "Modifica entità", + "history": "Storico", "person": { "create_zone": "Crea zona dalla posizione corrente" }, @@ -918,7 +933,13 @@ "label": "Chiama servizio", "service_data": "Dati del servizio" }, + "wait_for_trigger": { + "continue_timeout": "Continua al timeout", + "label": "In attesa dell'attivazione", + "timeout": "Timeout (opzionale)" + }, "wait_template": { + "continue_timeout": "Continua al timeout", "label": "Attendere", "timeout": "Timeout (opzionale)", "wait_template": "Modello di attesa" @@ -982,7 +1003,9 @@ "time": { "after": "Dopo", "before": "Prima", - "label": "Tempo" + "label": "Tempo", + "type_input": "Valore di un aiutante data/ora", + "type_value": "Tempo fisso" }, "zone": { "entity": "Entità con posizione", @@ -1070,6 +1093,7 @@ "value_template": "Valore modello (opzionale)" }, "state": { + "attribute": "Attributo (opzionale)", "for": "Per", "from": "Da", "label": "Stato", @@ -1096,8 +1120,10 @@ "seconds": "Secondi" }, "time": { - "at": "Alle", - "label": "Ora" + "at": "Al tempo", + "label": "Ora", + "type_input": "Valore di un aiutante data/ora", + "type_value": "Tempo fisso" }, "webhook": { "label": "Webhook", @@ -1120,6 +1146,8 @@ "add_automation": "Aggiungi Automazione", "delete_automation": "Cancellare l'automazione", "delete_confirm": "Sei sicuro di voler eliminare questa automazione?", + "duplicate": "Duplica", + "duplicate_automation": "Duplica automazione", "edit_automation": "Modifica automazione", "header": "Editor di Automazione", "headers": { @@ -1520,6 +1548,7 @@ }, "integrations": { "add_integration": "Aggiungi integrazione", + "attention": "Attenzione richiesta", "caption": "Integrazioni", "config_entry": { "area": "In {area}", @@ -1543,6 +1572,7 @@ "reload_restart_confirm": "Riavvia Home Assistant per completare il ricaricamento di questa integrazione", "rename": "Rinomina", "restart_confirm": "Riavvia Home Assistant per completare la rimozione di questa integrazione", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Modificare le impostazioni per {integration}.", "system_options": "Opzioni di sistema", "system_options_button": "Opzioni di sistema per {integration}", @@ -1589,6 +1619,7 @@ "none_found_detail": "Modifica i criteri di ricerca.", "note_about_integrations": "Non tutte le integrazioni possono ancora essere configurate tramite l'interfaccia utente.", "note_about_website_reference": "Ulteriori informazioni sono disponibili su ", + "reconfigure": "Riconfigurare", "rename_dialog": "Modifica il nome di questa voce di configurazione", "rename_input_label": "Nome della voce", "search": "Cerca integrazioni" @@ -1762,8 +1793,21 @@ "versions": "Recupero di informazioni sulle versioni del firmware e della classe di comando", "wakeup": "Configurazione del supporto per code di riattivazione e messaggi " }, + "node": { + "button": "Dettagli del nodo", + "not_found": "Nodo non trovato" + }, + "nodes_table": { + "failed": "Fallito", + "id": "ID", + "manufacturer": "Produttore", + "model": "Modello", + "query_stage": "Fase di richiesta", + "zwave_plus": "Z-Wave Plus" + }, "refresh_node": { "battery_note": "Se il nodo è alimentato a batteria, assicurarsi di riattivarlo prima di procedere", + "button": "Aggiorna nodo", "complete": "Aggiornamento del nodo completato", "description": "In questo modo si dirà a OpenZWave di re-interrogare un nodo e aggiornare le classi di comando, le funzionalità e i valori del nodo.", "node_status": "Stato del nodo", @@ -1912,7 +1956,7 @@ "filter": "Ricaricare le entità filtro", "generic": "Ricaricare le entità delle generiche telecamere IP", "generic_thermostat": "Ricaricare le entità termostati generiche", - "group": "Ricarica i Gruppi", + "group": "Ricarica gruppi, entità di gruppo e servizi di notifica", "heading": "Ricarica Configurazione YAML", "history_stats": "Ricarica le entità delle statistiche della cronologia", "homekit": "Ricarica HomeKit", @@ -1923,12 +1967,17 @@ "input_text": "Ricarica input testuali", "introduction": "Alcune parti di Home Assistant possono essere ricaricate senza richiedere un riavvio. Premendo su Ricarica si rimuoverà la loro Configurazione YAML attuale e si caricherà la versione aggiornata.", "min_max": "Ricaricare le entità min/max", + "mqtt": "Ricarica le entità mqtt", "person": "Ricarica le persone", "ping": "Ricarica le entità del sensore binario ping", - "rest": "Ricarica le entità della piattaforma rest", + "reload": "Ricarica {domain}", + "rest": "Ricarica le entità di rest e i servizi di notifica", + "rpi_gpio": "Ricarica le entità GPIO di Raspberry Pi ", "scene": "Ricarica le Scene", "script": "Ricarica gli Script", + "smtp": "Ricarica i servizi di notifica smtp", "statistics": "Ricaricare le entità statistiche", + "telegram": "Ricarica i servizi di notifica telegram", "template": "Ricaricare le entità modello", "trend": "Ricaricare le entità di tendenza", "universal": "Ricarica le entità del lettore multimediale universale", @@ -2017,7 +2066,7 @@ "system": "Sistema" } }, - "users_privileges_note": "Il gruppo di utenti è in fase di elaborazione. L'utente non sarà in grado di amministrare l'istanza tramite l'interfaccia utente. Stiamo ancora verificando tutti gli endpoint dell'API di gestione per garantire che limitino correttamente l'accesso agli amministratori." + "users_privileges_note": "La funzionalità del gruppo di utenti è in fase di elaborazione. L'utente non sarà in grado di amministrare l'istanza tramite l'Interfaccia Utente. Stiamo ancora verificando tutti gli endpoint dell'API di gestione per garantire che limitino correttamente l'accesso solo agli amministratori." }, "zha": { "add_device_page": { @@ -2549,7 +2598,11 @@ } }, "cardpicker": { + "by_card": "Per Scheda", + "by_entity": "Per Entità", "custom_card": "Personalizzato", + "domain": "Dominio", + "entity": "Entità", "no_description": "Nessuna descrizione disponibile." }, "edit_card": { @@ -2563,6 +2616,7 @@ "options": "Altre opzioni", "pick_card": "Quale scheda vorresti aggiungere?", "pick_card_view_title": "Quale scheda vorresti aggiungere alla tua vista {name}?", + "search_cards": "Schede di ricerca", "show_code_editor": "Mostra Editor di Codice", "show_visual_editor": "Mostra Editor Visivo", "toggle_editor": "Attiva / disattiva l'editor", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index 523a9a2718..f650fd7020 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -553,6 +553,10 @@ "toggle": "Veksle" }, "entity": { + "entity-attribute-picker": { + "attribute": "Attributt", + "show_attributes": "Vis attributter" + }, "entity-picker": { "clear": "Tøm", "entity": "Entitet", @@ -929,7 +933,13 @@ "label": "Tilkall tjeneste", "service_data": "Tjenestedata" }, + "wait_for_trigger": { + "continue_timeout": "Fortsett ved tidsavbrudd", + "label": "Vent på utløser", + "timeout": "Tidsavbrudd" + }, "wait_template": { + "continue_timeout": "Fortsett ved tidsavbrudd", "label": "Vent", "timeout": "Tidsavbrudd (valgfritt)", "wait_template": "Ventemal" @@ -993,7 +1003,9 @@ "time": { "after": "Etter", "before": "Før", - "label": "Tid" + "label": "Tid", + "type_input": "Verdien til en tid/dato hjelper", + "type_value": "Bestemt tid" }, "zone": { "entity": "Entitet med posisjon", @@ -1081,6 +1093,7 @@ "value_template": "Verdi fra mal (valgfritt)" }, "state": { + "attribute": "Attributt (valgfritt)", "for": "", "from": "Fra", "label": "Tilstand", @@ -1108,7 +1121,9 @@ }, "time": { "at": "Klokken", - "label": "Tid" + "label": "Tid", + "type_input": "Verdien til en tid/dato hjelper", + "type_value": "Bestemt tid" }, "webhook": { "label": "", @@ -1131,6 +1146,8 @@ "add_automation": "Legg til automasjon", "delete_automation": "Slett automasjon", "delete_confirm": "Er du sikker på at du vil slette denne automasjonen?", + "duplicate": "Dupliser", + "duplicate_automation": "Dupliser automasjon", "edit_automation": "Rediger automasjon", "header": "Automasjonsredigering", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "Legg til integrasjon", + "attention": "Oppmerksomhet kreves", "caption": "Integrasjoner", "config_entry": { "area": "I {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "Juster dine søkekriterier", "note_about_integrations": "Ikke alle integrasjoner kan konfigureres via brukergrensesnittet ennå.", "note_about_website_reference": "Flere er tilgjengelige på", + "reconfigure": "Konfigurer på nytt", "rename_dialog": "Redigere navnet på denne config-oppføringen", "rename_input_label": "Navn på oppføring", "search": "Søk i integrasjoner" diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 8e5aaece5d..20dec55c5e 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -505,6 +505,7 @@ "back": "Terug", "cancel": "Annuleren", "close": "Sluiten", + "continue": "Ga verder", "delete": "Verwijderen", "error_required": "Verplicht", "loading": "Bezig met laden", @@ -562,6 +563,7 @@ "no_history_found": "Geen geschiedenis gevonden" }, "media-browser": { + "choose_player": "Kies speler", "choose-source": "Kies bron", "content-type": { "album": "Album", @@ -570,12 +572,16 @@ "playlist": "Afspeellijst", "server": "Server" }, + "media_not_supported": "De Browser Media Player ondersteunt dit type media niet", + "media_player": "Mediaspeler", "media-player-browser": "Mediaspeler-browser", "no_items": "Niets gevonden", "pick": "Kies", "pick-media": "Kies Media", "play": "Speel", - "play-media": "Media afspelen" + "play-media": "Media afspelen", + "video_not_supported": "Uw browser ondersteunt het video-element niet.", + "web-browser": "Webbrowser" }, "picture-upload": { "label": "Afbeelding", @@ -617,6 +623,7 @@ "update": "Bijwerken" }, "domain_toggler": { + "reset_entities": "Entiteiten opnieuw instellen", "title": "Domeinen in- en uitschakelen" }, "entity_registry": { @@ -688,8 +695,10 @@ "crop": "Snijd bij" }, "more_info_control": { + "controls": "Besturing", "dismiss": "Dialoogvenster sluiten", "edit": "Entiteit bewerken", + "history": "Geschiedenis", "person": { "create_zone": "Creëer zone van huidige locatie" }, @@ -917,7 +926,11 @@ "label": "Service aanroepen", "service_data": "Service data" }, + "wait_for_trigger": { + "label": "Wacht op trigger" + }, "wait_template": { + "continue_timeout": "Ga verder op time-out", "label": "Wacht", "timeout": "Timeout (optioneel)", "wait_template": "Wachtsjabloon" @@ -1200,8 +1213,12 @@ }, "alexa": { "banner": "Het bewerken van de entiteiten die via deze gebruikersinterface worden weergegeven is uitgeschakeld, omdat je entiteitenfilters hebt geconfigureerd in configuration.yaml.", + "dont_expose_entity": "Entiteit niet beschikbaar maken", "expose": "Blootstellen aan Alexa", + "expose_entity": "Enititeit beschikbaar maken", "exposed_entities": "Blootgestelde entiteiten", + "follow_domain": "Domein volgen", + "manage_domains": "Beheer domeinen", "not_exposed_entities": "Niet blootgestelde entiteiten", "title": "Alexa" }, @@ -1239,8 +1256,11 @@ "google": { "banner": "Het bewerken van de entiteiten die via deze gebruikersinterface worden weergegeven is uitgeschakeld, omdat je entiteitenfilters hebt geconfigureerd in configuration.yaml.", "disable_2FA": "Schakel tweestapsverificatie uit", + "dont_expose_entity": "Entiteit niet beschikbaar maken", "expose": "Blootstellen aan Google Assistant", + "expose_entity": "Enititeit beschikbaar maken", "exposed_entities": "Blootgestelde entiteiten", + "manage_domains": "Beheer domeinen", "not_exposed_entities": "Niet blootgestelde entiteiten", "sync_to_google": "Wijzigingen synchroniseren met Google.", "title": "Google Assistant" @@ -1507,6 +1527,7 @@ }, "integrations": { "add_integration": "Integratie toevoegen", + "attention": "Aandacht vereist", "caption": "Integraties", "config_entry": { "area": "In {area}", @@ -1576,6 +1597,7 @@ "none_found_detail": "Pas uw zoekcriteria aan.", "note_about_integrations": "Nog niet alle integraties kunnen via de UI worden geconfigureerd.", "note_about_website_reference": "Meer zijn beschikbaar op de ", + "reconfigure": "Herconfigureer", "rename_dialog": "Bewerk de naam van dit configuratie item", "rename_input_label": "Invoernaam", "search": "Zoek integraties" @@ -1749,6 +1771,9 @@ "versions": "Het verkrijgen van informatie over firmware en commandoklasseversies", "wakeup": "Ondersteuning instellen voor waakwachtrijen en berichten" }, + "nodes_table": { + "failed": "Mislukt" + }, "refresh_node": { "battery_note": "Als het knooppunt op batterijen werkt, moet u het uit de sluimerstand halen voordat u verder gaat", "complete": "Knooppunt vernieuwen voltooid", @@ -1910,12 +1935,16 @@ "input_text": "Herlaad input texts", "introduction": "Sommige delen van Home Assistant kunnen opnieuw worden geladen zonder dat een herstart vereist is. Als je herladen gebruikt, wordt de huidige configuratie leeggemaakt en wordt de nieuwe geladen.", "min_max": "Herlaad min/max entiteiten", + "mqtt": "Herlaad mqtt entiteiten", "person": "Herlaad personen", "ping": "Herlaad ping binaire sensor entiteiten", "rest": "Herlaad rust-entiteiten", + "rpi_gpio": "Herlaad Raspberry Pi GPIO-entiteiten", "scene": "Herlaad scenes", "script": "Herlaad scripts", + "smtp": "Herlaad telegram notify services", "statistics": "Herlaad statistische entiteiten", + "telegram": "Herlaad telegram notify services", "template": "Herlaad sjabloon-entiteiten", "trend": "Herlaad trend-entiteiten", "universal": "Herlaad universele mediaspeler entiteiten", @@ -2537,6 +2566,8 @@ }, "cardpicker": { "custom_card": "Aangepaste", + "domain": "Domein", + "entity": "Entiteit", "no_description": "Er is geen beschrijving beschikbaar" }, "edit_card": { @@ -2550,6 +2581,7 @@ "options": "Meer opties", "pick_card": "Welke kaart wil je toevoegen?", "pick_card_view_title": "Welke kaart wil je toevoegen aan je {name} weergave?", + "search_cards": "Zoek kaarten", "show_code_editor": "Code-editor weergeven", "show_visual_editor": "Visual Editor weergeven", "toggle_editor": "Toggle Editor", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 8bc907ffb4..0ff93c88b1 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -19,6 +19,7 @@ "logbook": "Журнал событий", "mailbox": "Почта", "map": "Карта", + "media_browser": "Браузер мультимедиа", "profile": "Профиль", "shopping_list": "Список покупок", "states": "Обзор" @@ -552,6 +553,10 @@ "toggle": "Переключить" }, "entity": { + "entity-attribute-picker": { + "attribute": "Атрибут", + "show_attributes": "Показать атрибуты" + }, "entity-picker": { "clear": "Очистить", "entity": "Объект", @@ -563,6 +568,7 @@ "no_history_found": "История не найдена." }, "media-browser": { + "audio_not_supported": "Ваш браузер не поддерживает аудио.", "choose_player": "Выберите медиаплеер", "choose-source": "Выбрать источник", "content-type": { @@ -572,6 +578,8 @@ "playlist": "Плейлист", "server": "Сервер" }, + "media_browsing_error": "Ошибка просмотра мультимедиа", + "media_not_supported": "Браузер медиаплеера не поддерживает этот тип мультимедиа.", "media_player": "Медиаплеер", "media-player-browser": "Браузер медиаплеера", "no_items": "Нет элементов", @@ -579,6 +587,7 @@ "pick-media": "Выбрать Медиа", "play": "Воспроизведение", "play-media": "Воспроизведение Медиа", + "video_not_supported": "Ваш браузер не поддерживает видео.", "web-browser": "Веб-браузер" }, "picture-upload": { @@ -864,7 +873,7 @@ "add": "Добавить действие", "delete": "Удалить", "delete_confirm": "Вы уверены, что хотите удалить?", - "duplicate": "Дублировать", + "duplicate": "Копировать", "header": "Действия", "introduction": "Действия — это то, что сделает Home Assistant, когда правило автоматизации сработает.", "learn_more": "Узнайте больше о действиях", @@ -924,7 +933,13 @@ "label": "Вызов службы", "service_data": "Данные" }, + "wait_for_trigger": { + "continue_timeout": "Продолжить по истечении времени", + "label": "Ожидать триггера", + "timeout": "Тайм-аут (необязательно)" + }, "wait_template": { + "continue_timeout": "Продолжить по истечении времени", "label": "Ожидание", "timeout": "Тайм-аут (необязательно)", "wait_template": "Шаблон ожидания" @@ -937,7 +952,7 @@ "add": "Добавить условие", "delete": "Удалить", "delete_confirm": "Вы уверены, что хотите удалить?", - "duplicate": "Дублировать", + "duplicate": "Копировать", "header": "Условия", "introduction": "Условия — это необязательная часть правила автоматизации. Действие автоматизации не будет выполнено, пока не будут удовлетворены все условия.", "learn_more": "Узнайте больше об условиях", @@ -988,7 +1003,9 @@ "time": { "after": "После", "before": "До", - "label": "Время" + "label": "Время", + "type_input": "Вспомогательный элемент даты и времени", + "type_value": "Фиксированное время" }, "zone": { "entity": "Объект с местоположением", @@ -1029,7 +1046,7 @@ "add": "Добавить триггер", "delete": "Удалить", "delete_confirm": "Вы уверены, что хотите удалить?", - "duplicate": "Дублировать", + "duplicate": "Копировать", "header": "Триггеры", "introduction": "Триггеры — это то, что запускает процесс автоматизации. Можно указать несколько триггеров на одно и то же правило. Когда триггер сработает, Home Assistant будет проверять условия (если таковые имеются), и выполнять действия.", "learn_more": "Узнайте больше о триггерах", @@ -1076,6 +1093,7 @@ "value_template": "Шаблон значения (необязательно)" }, "state": { + "attribute": "Атрибут (необязательно)", "for": "В течение", "from": "С", "label": "Состояние", @@ -1102,8 +1120,10 @@ "seconds": "Секунд" }, "time": { - "at": "В", - "label": "Время" + "at": "Время", + "label": "Время", + "type_input": "Вспомогательный элемент даты и времени", + "type_value": "Фиксированное время" }, "webhook": { "label": "Webhook", @@ -1126,6 +1146,8 @@ "add_automation": "Добавить автоматизацию", "delete_automation": "Удалить автоматизацию", "delete_confirm": "Вы уверены, что хотите удалить эту автоматизацию?", + "duplicate": "Копировать", + "duplicate_automation": "Копировать автоматизацию", "edit_automation": "Редактировать автоматизацию", "header": "Редактор автоматизаций", "headers": { @@ -1526,6 +1548,7 @@ }, "integrations": { "add_integration": "Добавить интеграцию", + "attention": "Требуется внимание", "caption": "Интеграции", "config_entry": { "area": "Помещение: {area}", @@ -1596,6 +1619,7 @@ "none_found_detail": "Измените критерии поиска", "note_about_integrations": "Пока что не все интеграции могут быть настроены через пользовательский интерфейс.", "note_about_website_reference": "Все доступные интеграции Вы можете найти на ", + "reconfigure": "Перенастроить", "rename_dialog": "Название интеграции", "rename_input_label": "Название", "search": "Поиск интеграций" diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index 525efe5654..fef4a36abf 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -19,6 +19,7 @@ "logbook": "日志", "mailbox": "邮箱", "map": "地图", + "media_browser": "媒体浏览器", "profile": "用户资料", "shopping_list": "购物清单", "states": "概览" @@ -552,6 +553,10 @@ "toggle": "切换" }, "entity": { + "entity-attribute-picker": { + "attribute": "属性", + "show_attributes": "显示属性" + }, "entity-picker": { "clear": "清除", "entity": "实体", @@ -573,6 +578,7 @@ "playlist": "播放列表", "server": "服务器" }, + "media_browsing_error": "媒体浏览错误", "media_not_supported": "浏览器媒体播放器不支持此类型的媒体", "media_player": "媒体播放器", "media-player-browser": "媒体播放浏览器", @@ -927,7 +933,13 @@ "label": "调用服务", "service_data": "服务数据" }, + "wait_for_trigger": { + "continue_timeout": "超时继续", + "label": "等待触发", + "timeout": "超时(可选)" + }, "wait_template": { + "continue_timeout": "超时继续", "label": "等待", "timeout": "超时(可选)", "wait_template": "等待模板" @@ -991,7 +1003,9 @@ "time": { "after": "晚于", "before": "早于", - "label": "时间" + "label": "时间", + "type_input": "“日期/时间”辅助元素的值", + "type_value": "固定时间" }, "zone": { "entity": "位置追踪设备", @@ -1079,6 +1093,7 @@ "value_template": "自定义值(可选)" }, "state": { + "attribute": "属性(可选)", "for": "持续", "from": "从", "label": "状态", @@ -1105,8 +1120,10 @@ "seconds": "秒" }, "time": { - "at": "当", - "label": "时间" + "at": "时间为", + "label": "时间", + "type_input": "“日期/时间”辅助元素的值", + "type_value": "固定时间" }, "webhook": { "label": "Webhook", @@ -1129,6 +1146,8 @@ "add_automation": "添加自动化", "delete_automation": "删除自动化", "delete_confirm": "您确定要删除此自动化吗?", + "duplicate": "制作副本", + "duplicate_automation": "复制自动化", "edit_automation": "编辑自动化", "header": "自动化编辑器", "headers": { @@ -1529,6 +1548,7 @@ }, "integrations": { "add_integration": "添加集成", + "attention": "需要注意", "caption": "集成", "config_entry": { "area": "位于:{area}", @@ -1552,6 +1572,7 @@ "reload_restart_confirm": "重启 Home Assistant 以完成此集成的重载", "rename": "重命名", "restart_confirm": "重启 Home Assistant 以完成此集成的删除", + "services": "{count} {count, plural,\n one {个服务}\n other {个服务}\n}", "settings_button": "编辑{integration}设置", "system_options": "系统选项", "system_options_button": "{integration}系统选项", @@ -1598,6 +1619,7 @@ "none_found_detail": "请调整搜索条件。", "note_about_integrations": "并非所有集成都可以通过 UI 进行配置。", "note_about_website_reference": "更多可用信息,尽在 ", + "reconfigure": "重新配置", "rename_dialog": "编辑此配置项的名称", "rename_input_label": "条目名称", "search": "搜索集成" From b3a763a48d1c14412e9e54444bed7b4541d5a903 Mon Sep 17 00:00:00 2001 From: Ludeeus Date: Wed, 9 Sep 2020 10:16:54 +0000 Subject: [PATCH 058/146] Add fallback for renderExternalStepHeader --- src/dialogs/config-flow/show-dialog-config-flow.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dialogs/config-flow/show-dialog-config-flow.ts b/src/dialogs/config-flow/show-dialog-config-flow.ts index 1ac015ecde..ae3c755631 100644 --- a/src/dialogs/config-flow/show-dialog-config-flow.ts +++ b/src/dialogs/config-flow/show-dialog-config-flow.ts @@ -97,8 +97,13 @@ export const showConfigFlowDialog = ( }, renderExternalStepHeader(hass, step) { - return hass.localize( - `component.${step.handler}.config.step.${step.step_id}.title` + return ( + hass.localize( + `component.${step.handler}.config.${step.step_id}.title` + ) || + hass.localize( + "ui.panel.config.integrations.config_flow.external_step.open_site" + ) ); }, From 5b8f97e0f60f1ccb02695199c966eab3db6f9cec Mon Sep 17 00:00:00 2001 From: Ludeeus Date: Wed, 9 Sep 2020 10:17:57 +0000 Subject: [PATCH 059/146] fix missing step --- src/dialogs/config-flow/show-dialog-config-flow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/config-flow/show-dialog-config-flow.ts b/src/dialogs/config-flow/show-dialog-config-flow.ts index ae3c755631..92bbb21597 100644 --- a/src/dialogs/config-flow/show-dialog-config-flow.ts +++ b/src/dialogs/config-flow/show-dialog-config-flow.ts @@ -99,7 +99,7 @@ export const showConfigFlowDialog = ( renderExternalStepHeader(hass, step) { return ( hass.localize( - `component.${step.handler}.config.${step.step_id}.title` + `component.${step.handler}.config.step.${step.step_id}.title` ) || hass.localize( "ui.panel.config.integrations.config_flow.external_step.open_site" From 2dffe7ba9e0c0ec60058ed2c2e6fefb0f492f7d8 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 9 Sep 2020 13:27:54 +0200 Subject: [PATCH 060/146] Add binary sensor icon for DEVICE_CLASS_BATTERY_CHARGING (#6876) * Add binary sensor icon for DEVICE_CLASS_BATTERY_CHARGING * Update icons for DEVICE_CLASS_BATTERY_CHARGING --- src/common/entity/binary_sensor_icon.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/entity/binary_sensor_icon.ts b/src/common/entity/binary_sensor_icon.ts index c1eb694afe..845e096403 100644 --- a/src/common/entity/binary_sensor_icon.ts +++ b/src/common/entity/binary_sensor_icon.ts @@ -7,6 +7,8 @@ export const binarySensorIcon = (state: HassEntity) => { switch (state.attributes.device_class) { case "battery": return activated ? "hass:battery" : "hass:battery-outline"; + case "battery_charging": + return activated ? "hass:battery" : "hass:battery-charging"; case "cold": return activated ? "hass:thermometer" : "hass:snowflake"; case "connectivity": From f7ab52fe9a4c8567e57f8d20aedd816936e433c7 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Wed, 9 Sep 2020 10:22:34 -0500 Subject: [PATCH 061/146] Remove sort from frontend for now (#6886) --- src/components/media-player/ha-media-player-browse.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index ad80b0d524..b2468fbbd3 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -21,7 +21,6 @@ import { ifDefined } from "lit-html/directives/if-defined"; import { styleMap } from "lit-html/directives/style-map"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; -import { compare } from "../../common/string/compare"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; import { @@ -478,13 +477,6 @@ export class HaMediaPlayerBrowse extends LitElement { mediaContentType ) : await browseLocalMediaPlayer(this.hass, mediaContentId); - itemData.children = itemData.children?.sort((first, second) => - !first.can_expand && second.can_expand - ? 1 - : first.can_expand && !second.can_expand - ? -1 - : compare(first.title, second.title) - ); return itemData; } From 432e3ba6363d9b711797ec11396e741b74a78bce Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 17:23:03 +0200 Subject: [PATCH 062/146] Fix entity drag (#6884) --- .../lovelace/components/hui-entity-editor.ts | 44 ++++++++++++------- .../editor/card-editor/hui-card-editor.ts | 17 ++++--- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index b22bc775fd..90e4b5eeb4 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -34,7 +34,9 @@ export class HuiEntityEditor extends LitElement { @internalProperty() private _attached = false; - private _sortable?; + @internalProperty() private _renderEmptySortable = false; + + private _sortable?: Sortable; public connectedCallback() { super.connectedCallback(); @@ -60,21 +62,23 @@ export class HuiEntityEditor extends LitElement { ")"}
    - ${guard([this.entities], () => - this.entities!.map((entityConf, index) => { - return html` -
    - - -
    - `; - }) + ${guard([this.entities, this._renderEmptySortable], () => + this._renderEmptySortable + ? "" + : this.entities!.map((entityConf, index) => { + return html` +
    + + +
    + `; + }) )}
    entity.entity)); + this._handleEntitiesChanged(); } } + private async _handleEntitiesChanged() { + this._renderEmptySortable = true; + await this.updateComplete; + this._renderEmptySortable = false; + } + private _createSortable() { this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), { animation: 150, diff --git a/src/panels/lovelace/editor/card-editor/hui-card-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-editor.ts index f081cc65ae..5c786ad71e 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-editor.ts @@ -5,14 +5,16 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, - TemplateResult, query, + TemplateResult, } from "lit-element"; import { fireEvent } from "../../../../common/dom/fire_event"; import { computeRTL } from "../../../../common/util/compute_rtl"; +import { deepEqual } from "../../../../common/util/deep-equal"; +import "../../../../components/ha-circular-progress"; import "../../../../components/ha-code-editor"; import type { HaCodeEditor } from "../../../../components/ha-code-editor"; import type { @@ -20,14 +22,12 @@ import type { LovelaceConfig, } from "../../../../data/lovelace"; import type { HomeAssistant } from "../../../../types"; +import { handleStructError } from "../../common/structs/handle-errors"; import { getCardElementClass } from "../../create-element/create-card-element"; import type { EntityConfig } from "../../entity-rows/types"; import type { LovelaceCardEditor } from "../../types"; -import type { GUIModeChangedEvent } from "../types"; -import "../../../../components/ha-circular-progress"; -import { deepEqual } from "../../../../common/util/deep-equal"; -import { handleStructError } from "../../common/structs/handle-errors"; import { GUISupportError } from "../gui-support-error"; +import type { GUIModeChangedEvent } from "../types"; export interface ConfigChangedEvent { config: LovelaceCardConfig; @@ -78,6 +78,9 @@ export class HuiCardEditor extends LitElement { @query("ha-code-editor") _yamlEditor?: HaCodeEditor; public get yaml(): string { + if (!this._yaml) { + this._yaml = safeDump(this._config); + } return this._yaml || ""; } @@ -101,7 +104,7 @@ export class HuiCardEditor extends LitElement { return; } this._config = config; - this._yaml = safeDump(config); + this._yaml = undefined; this._error = undefined; this._setConfig(); } From 5ff52ea1137c6d8640d6e6979686441940031966 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 9 Sep 2020 17:24:13 +0200 Subject: [PATCH 063/146] Update constant name to make it clearer (#6881) --- src/common/entity/binary_sensor_icon.ts | 42 ++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/common/entity/binary_sensor_icon.ts b/src/common/entity/binary_sensor_icon.ts index 845e096403..5442e432ff 100644 --- a/src/common/entity/binary_sensor_icon.ts +++ b/src/common/entity/binary_sensor_icon.ts @@ -3,51 +3,51 @@ import { HassEntity } from "home-assistant-js-websocket"; /** Return an icon representing a binary sensor state. */ export const binarySensorIcon = (state: HassEntity) => { - const activated = state.state && state.state === "off"; + const is_off = state.state && state.state === "off"; switch (state.attributes.device_class) { case "battery": - return activated ? "hass:battery" : "hass:battery-outline"; + return is_off ? "hass:battery" : "hass:battery-outline"; case "battery_charging": - return activated ? "hass:battery" : "hass:battery-charging"; + return is_off ? "hass:battery" : "hass:battery-charging"; case "cold": - return activated ? "hass:thermometer" : "hass:snowflake"; + return is_off ? "hass:thermometer" : "hass:snowflake"; case "connectivity": - return activated ? "hass:server-network-off" : "hass:server-network"; + return is_off ? "hass:server-network-off" : "hass:server-network"; case "door": - return activated ? "hass:door-closed" : "hass:door-open"; + return is_off ? "hass:door-closed" : "hass:door-open"; case "garage_door": - return activated ? "hass:garage" : "hass:garage-open"; + return is_off ? "hass:garage" : "hass:garage-open"; case "gas": case "power": case "problem": case "safety": case "smoke": - return activated ? "hass:shield-check" : "hass:alert"; + return is_off ? "hass:shield-check" : "hass:alert"; case "heat": - return activated ? "hass:thermometer" : "hass:fire"; + return is_off ? "hass:thermometer" : "hass:fire"; case "light": - return activated ? "hass:brightness-5" : "hass:brightness-7"; + return is_off ? "hass:brightness-5" : "hass:brightness-7"; case "lock": - return activated ? "hass:lock" : "hass:lock-open"; + return is_off ? "hass:lock" : "hass:lock-open"; case "moisture": - return activated ? "hass:water-off" : "hass:water"; + return is_off ? "hass:water-off" : "hass:water"; case "motion": - return activated ? "hass:walk" : "hass:run"; + return is_off ? "hass:walk" : "hass:run"; case "occupancy": - return activated ? "hass:home-outline" : "hass:home"; + return is_off ? "hass:home-outline" : "hass:home"; case "opening": - return activated ? "hass:square" : "hass:square-outline"; + return is_off ? "hass:square" : "hass:square-outline"; case "plug": - return activated ? "hass:power-plug-off" : "hass:power-plug"; + return is_off ? "hass:power-plug-off" : "hass:power-plug"; case "presence": - return activated ? "hass:home-outline" : "hass:home"; + return is_off ? "hass:home-outline" : "hass:home"; case "sound": - return activated ? "hass:music-note-off" : "hass:music-note"; + return is_off ? "hass:music-note-off" : "hass:music-note"; case "vibration": - return activated ? "hass:crop-portrait" : "hass:vibrate"; + return is_off ? "hass:crop-portrait" : "hass:vibrate"; case "window": - return activated ? "hass:window-closed" : "hass:window-open"; + return is_off ? "hass:window-closed" : "hass:window-open"; default: - return activated ? "hass:radiobox-blank" : "hass:checkbox-marked-circle"; + return is_off ? "hass:radiobox-blank" : "hass:checkbox-marked-circle"; } }; From 42bf350034b7a53f0c6ba76791ea9d2a65bf6d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 9 Sep 2020 17:26:22 +0200 Subject: [PATCH 064/146] Add ha-user-badge to view visibility editor (#6885) --- src/components/user/ha-person-badge.ts | 3 +++ src/components/user/ha-user-badge.ts | 3 +++ .../view-editor/hui-view-visibility-editor.ts | 14 ++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/user/ha-person-badge.ts b/src/components/user/ha-person-badge.ts index 70c47d6023..eead0784c2 100644 --- a/src/components/user/ha-person-badge.ts +++ b/src/components/user/ha-person-badge.ts @@ -39,6 +39,9 @@ class PersonBadge extends LitElement { static get styles(): CSSResult { return css` + :host { + display: contents; + } .picture { width: 40px; height: 40px; diff --git a/src/components/user/ha-user-badge.ts b/src/components/user/ha-user-badge.ts index 09972237fc..cb978dd004 100644 --- a/src/components/user/ha-user-badge.ts +++ b/src/components/user/ha-user-badge.ts @@ -104,6 +104,9 @@ class UserBadge extends LitElement { static get styles(): CSSResult { return css` + :host { + display: contents; + } .picture { width: 40px; height: 40px; diff --git a/src/panels/lovelace/editor/view-editor/hui-view-visibility-editor.ts b/src/panels/lovelace/editor/view-editor/hui-view-visibility-editor.ts index 8bb8f62f07..f5ed39cf9b 100644 --- a/src/panels/lovelace/editor/view-editor/hui-view-visibility-editor.ts +++ b/src/panels/lovelace/editor/view-editor/hui-view-visibility-editor.ts @@ -1,13 +1,13 @@ -import "@polymer/paper-item/paper-item"; +import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-item-body"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -15,6 +15,7 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; import { compare } from "../../../../common/string/compare"; import { HaSwitch } from "../../../../components/ha-switch"; +import "../../../../components/user/ha-user-badge"; import { LovelaceViewConfig, ShowViewConfig } from "../../../../data/lovelace"; import { fetchUsers, User } from "../../../../data/user"; import { HomeAssistant } from "../../../../types"; @@ -69,14 +70,19 @@ export class HuiViewVisibilityEditor extends LitElement {

    ${this._sortedUsers(this._users).map( (user) => html` - + + ${user.name} - + ` )} `; From 1890aab1e60020f03390d679bfab29c6e011f058 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 9 Sep 2020 20:48:51 +0200 Subject: [PATCH 065/146] Color all deletion options consistenly red (#6891) * Color all deletion options consistenly red * CSS cleanup * Color the "Remove Selected" entity config button * Make eslint happy * Getting rid of a wayward bracket --- .../action/ha-automation-action-row.ts | 1 - .../config/automation/ha-automation-editor.ts | 1 + .../config/entities/ha-config-entities.ts | 219 +++++++++--------- src/panels/config/scene/ha-scene-editor.ts | 1 + src/panels/config/script/ha-script-editor.ts | 1 + 5 files changed, 115 insertions(+), 108 deletions(-) diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index d9cf65ce8c..f9f82bbd5b 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -336,7 +336,6 @@ export default class HaAutomationActionRow extends LitElement { --mdc-theme-text-primary-on-background: var(--disabled-text-color); } .warning { - color: var(--warning-color); margin-bottom: 8px; } .warning ul { diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 1c6f0c125c..f76f44ac67 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -104,6 +104,7 @@ export class HaAutomationEditor extends LitElement { - ${this.hass.localize( "ui.panel.config.entities.picker.remove_selected.button" )} mwc-button, - .header-btns > ha-icon-button { - margin: 8px; - } - .active-filters { - color: var(--primary-text-color); - position: relative; - display: flex; - align-items: center; - padding: 2px 2px 2px 8px; - margin-left: 4px; - font-size: 14px; - } - .active-filters ha-icon { - color: var(--primary-color); - } - .active-filters mwc-button { - margin-left: 8px; - } - .active-filters::before { - background-color: var(--primary-color); - opacity: 0.12; - border-radius: 4px; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - content: ""; - } - `; + static get styles(): CSSResult[] { + return [ + haStyle, + css` + hass-loading-screen { + --app-header-background-color: var(--sidebar-background-color); + --app-header-text-color: var(--sidebar-text-color); + } + a { + color: var(--primary-color); + } + h2 { + margin-top: 0; + font-family: var(--paper-font-headline_-_font-family); + -webkit-font-smoothing: var( + --paper-font-headline_-_-webkit-font-smoothing + ); + font-size: var(--paper-font-headline_-_font-size); + font-weight: var(--paper-font-headline_-_font-weight); + letter-spacing: var(--paper-font-headline_-_letter-spacing); + line-height: var(--paper-font-headline_-_line-height); + opacity: var(--dark-primary-opacity); + } + p { + font-family: var(--paper-font-subhead_-_font-family); + -webkit-font-smoothing: var( + --paper-font-subhead_-_-webkit-font-smoothing + ); + font-weight: var(--paper-font-subhead_-_font-weight); + line-height: var(--paper-font-subhead_-_line-height); + } + ha-data-table { + width: 100%; + --data-table-border-width: 0; + } + :host(:not([narrow])) ha-data-table { + height: calc(100vh - 65px); + display: block; + } + ha-button-menu { + margin-right: 8px; + } + .table-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12); + } + search-input { + margin-left: 16px; + flex-grow: 1; + position: relative; + top: 2px; + } + .search-toolbar search-input { + margin-left: 8px; + top: 1px; + } + .search-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + color: var(--secondary-text-color); + } + .search-toolbar ha-button-menu { + position: static; + } + .selected-txt { + font-weight: bold; + padding-left: 16px; + } + .table-header .selected-txt { + margin-top: 20px; + } + .search-toolbar .selected-txt { + font-size: 16px; + } + .header-btns > mwc-button, + .header-btns > ha-icon-button { + margin: 8px; + } + .active-filters { + color: var(--primary-text-color); + position: relative; + display: flex; + align-items: center; + padding: 2px 2px 2px 8px; + margin-left: 4px; + font-size: 14px; + } + .active-filters ha-icon { + color: var(--primary-color); + } + .active-filters mwc-button { + margin-left: 8px; + } + .active-filters::before { + background-color: var(--primary-color); + opacity: 0.12; + border-radius: 4px; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + } + `, + ]; } } diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index 77bec1b7db..73f0e29ce3 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -198,6 +198,7 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) { ? "" : html` Date: Wed, 9 Sep 2020 20:49:56 +0200 Subject: [PATCH 066/146] Disable "Execute" if automation is unavailable (#6866) --- src/dialogs/more-info/controls/more-info-automation.ts | 5 ++++- src/panels/config/automation/ha-automation-picker.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index 8a25e93fe4..a63a10d819 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -34,7 +34,10 @@ class MoreInfoAutomation extends LitElement {
    - + ${this.hass.localize("ui.card.automation.trigger")}
    diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index e56b62fe2b..221f7f1e14 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -91,10 +91,11 @@ class HaAutomationPicker extends LitElement { if (!narrow) { columns.execute = { title: "", - template: (_info, automation) => html` + template: (_info, automation: any) => html` this._execute(ev)} + .disabled=${automation.state === "unavailable"} > ${this.hass.localize("ui.card.automation.trigger")} From d99d092784fbf28b59bf9a6e7027525cbf9a6259 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 21:02:14 +0200 Subject: [PATCH 067/146] Enlarge touch target delete button (#6893) --- src/components/ha-sidebar.ts | 9 +++++---- src/resources/ha-sortable-style.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index d02c7bbd60..41d460fc9f 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -526,7 +526,7 @@ class HaSidebar extends LitElement { private async _hidePanel(ev: Event) { ev.preventDefault(); - const panel = (ev.target as any).panel; + const panel = (ev.currentTarget as any).panel; if (this._hiddenPanels.includes(panel)) { return; } @@ -692,12 +692,13 @@ class HaSidebar extends LitElement { : html``} ${title} ${this._editMode - ? html`` + > + + ` : ""}
    diff --git a/src/resources/ha-sortable-style.ts b/src/resources/ha-sortable-style.ts index 5751f54819..b6e3d377c5 100644 --- a/src/resources/ha-sortable-style.ts +++ b/src/resources/ha-sortable-style.ts @@ -57,7 +57,7 @@ export const sortableStyles = css` .hide-panel { display: none; position: absolute; - right: 8px; + right: 0; } :host([expanded]) .hide-panel { From 1130007d14106bf15ae2a806194f3c6d01a7b24a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 22:09:56 +0200 Subject: [PATCH 068/146] Fix mjpeg player (#6896) --- src/components/ha-camera-stream.ts | 10 ++++++---- src/dialogs/more-info/controls/more-info-camera.ts | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index 51794b3ad3..b2db579890 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -35,7 +35,7 @@ class HaCameraStream extends LitElement { @internalProperty() private _url?: string; protected render(): TemplateResult { - if (!this.stateObj || (!this._forceMJPEG && !this._url)) { + if (!this.stateObj) { return html``; } @@ -52,16 +52,18 @@ class HaCameraStream extends LitElement { )} camera.`} /> ` - : html` + : this._url + ? html` - `} + ` + : ""} `; } diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index d990cf25a0..4bc02b6a79 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -4,9 +4,9 @@ import { css, CSSResult, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -47,7 +47,7 @@ class MoreInfoCamera extends LitElement { return html` ${this._cameraPrefs From e0bdef98a65777846a97b4678f2e2cc5557a1293 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 22:10:23 +0200 Subject: [PATCH 069/146] Only show history tabs for certain domains (#6895) Co-authored-by: Zack Barett --- .../controls/more-info-automation.ts | 2 +- src/dialogs/more-info/ha-more-info-dialog.ts | 30 ++++++++++++++----- ...tab-history.ts => ha-more-info-history.ts} | 23 +++++++------- src/panels/logbook/ha-logbook.ts | 2 +- src/translations/en.json | 6 ++-- 5 files changed, 41 insertions(+), 22 deletions(-) rename src/dialogs/more-info/{ha-more-info-tab-history.ts => ha-more-info-history.ts} (89%) diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index a63a10d819..8b4ff441c2 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -55,7 +55,7 @@ class MoreInfoAutomation extends LitElement { justify-content: space-between; } .actions { - margin: 36px 0 8px 0; + margin: 8px 0; text-align: right; } `; diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 7738ee7349..63381e3251 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -29,9 +29,19 @@ import { haStyleDialog } from "../../resources/styles"; import "../../state-summary/state-card-content"; import { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; +import "./ha-more-info-history"; import "./more-info-content"; const DOMAINS_NO_INFO = ["camera", "configurator"]; +const CONTROL_DOMAINS = [ + "light", + "media_player", + "vacuum", + "alarm_control_panel", + "climate", + "humidifier", + "weather", +]; const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"]; const EDITABLE_DOMAINS = ["script"]; @@ -127,7 +137,8 @@ export class MoreInfoDialog extends LitElement { ` : ""} - ${this._computeShowHistoryComponent(entityId) + ${CONTROL_DOMAINS.includes(domain) && + this._computeShowHistoryComponent(entityId) ? html` + ${!CONTROL_DOMAINS.includes(domain) || + !this._computeShowHistoryComponent(entityId) + ? "" + : html``} ${stateObj.attributes.restored ? html`

    @@ -188,10 +206,10 @@ export class MoreInfoDialog extends LitElement { : ""} ` : html` - + > ` )}

    @@ -199,10 +217,6 @@ export class MoreInfoDialog extends LitElement { `; } - protected firstUpdated(): void { - import("./ha-more-info-tab-history"); - } - private _enlarge() { this.large = !this.large; } diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-history.ts similarity index 89% rename from src/dialogs/more-info/ha-more-info-tab-history.ts rename to src/dialogs/more-info/ha-more-info-history.ts index 18624a11ef..4c57bbd2c3 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -16,11 +16,11 @@ import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; import { getLogbookData, LogbookEntry } from "../../data/logbook"; import "../../panels/logbook/ha-logbook"; -import { haStyleDialog } from "../../resources/styles"; +import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; -@customElement("ha-more-info-tab-history") -export class MoreInfoTabHistoryDialog extends LitElement { +@customElement("ha-more-info-history") +export class MoreInfoHistory extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public entityId!: string; @@ -43,8 +43,7 @@ export class MoreInfoTabHistoryDialog extends LitElement { return html``; } - return html` - ` - : ""} - `; + : html`
    + ${this.hass.localize("ui.components.logbook.entries_not_found")} +
    `}`; } protected firstUpdated(): void { @@ -139,13 +139,16 @@ export class MoreInfoTabHistoryDialog extends LitElement { static get styles() { return [ - haStyleDialog, + haStyle, css` state-history-charts { display: block; margin-bottom: 16px; } - + .no-entries { + text-align: center; + padding: 16px; + } ha-logbook { max-height: 360px; } @@ -161,6 +164,6 @@ export class MoreInfoTabHistoryDialog extends LitElement { declare global { interface HTMLElementTagNameMap { - "ha-more-info-tab-history": MoreInfoTabHistoryDialog; + "ha-more-info-history": MoreInfoHistory; } } diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index cf49dd24ba..2eb994cae1 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -67,7 +67,7 @@ class HaLogbook extends LitElement { if (!this.entries?.length) { return html`
    - ${this.hass.localize("ui.panel.logbook.entries_not_found")} + ${this.hass.localize("ui.components.logbook.entries_not_found")}
    `; } diff --git a/src/translations/en.json b/src/translations/en.json index 219471c422..24f05139cc 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -281,6 +281,9 @@ "error_required": "Required" }, "components": { + "logbook": { + "entries_not_found": "No logbook entries found." + }, "entity": { "entity-picker": { "entity": "Entity", @@ -401,7 +404,7 @@ "dismiss": "Dismiss dialog", "settings": "Entity settings", "edit": "Edit entity", - "controls": "Controls", + "details": "Details", "history": "History", "script": { "last_action": "Last Action", @@ -2045,7 +2048,6 @@ } }, "logbook": { - "entries_not_found": "No logbook entries found.", "ranges": { "today": "Today", "yesterday": "Yesterday", From eb96dd480373159a17c65f31ab6b3f5779e32b95 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 22:55:43 +0200 Subject: [PATCH 070/146] Handle not defined entities (#6898) --- src/panels/config/cloud/alexa/cloud-alexa.ts | 8 ++++---- .../cloud/google-assistant/cloud-google-assistant.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/panels/config/cloud/alexa/cloud-alexa.ts b/src/panels/config/cloud/alexa/cloud-alexa.ts index 5614318886..948d542680 100644 --- a/src/panels/config/cloud/alexa/cloud-alexa.ts +++ b/src/panels/config/cloud/alexa/cloud-alexa.ts @@ -103,7 +103,9 @@ class CloudAlexa extends LitElement { this._entities.forEach((entity) => { const stateObj = this.hass.states[entity.entity_id]; - const config = this._entityConfigs[entity.entity_id] || {}; + const config = this._entityConfigs[entity.entity_id] || { + should_expose: null, + }; const isExposed = emptyFilter ? this._configIsExposed(entity.entity_id, config) : filterFunc(entity.entity_id); @@ -319,9 +321,7 @@ class CloudAlexa extends LitElement { } private _configIsExposed(entityId: string, config: AlexaEntityConfig) { - return config.should_expose === null - ? this._configIsDomainExposed(entityId) - : config.should_expose; + return config.should_expose ?? this._configIsDomainExposed(entityId); } private async _exposeChanged(ev: CustomEvent) { diff --git a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts index c29e22eaa0..49b56ea32c 100644 --- a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts +++ b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts @@ -109,7 +109,9 @@ class CloudGoogleAssistant extends LitElement { this._entities.forEach((entity) => { const stateObj = this.hass.states[entity.entity_id]; - const config = this._entityConfigs[entity.entity_id] || {}; + const config = this._entityConfigs[entity.entity_id] || { + should_expose: null, + }; const isExposed = emptyFilter ? this._configIsExposed(entity.entity_id, config) : filterFunc(entity.entity_id); @@ -324,9 +326,7 @@ class CloudGoogleAssistant extends LitElement { } private _configIsExposed(entityId: string, config: GoogleEntityConfig) { - return config.should_expose === null - ? this._configIsDomainExposed(entityId) - : config.should_expose; + return config.should_expose ?? this._configIsDomainExposed(entityId); } private async _fetchData() { From e955cc437852f77b6aab70e49cd4e7118d5578f6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 22:57:44 +0200 Subject: [PATCH 071/146] Check for hass when setting themes (#6897) --- src/state/themes-mixin.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/state/themes-mixin.ts b/src/state/themes-mixin.ts index 0815f4391a..847f1102f5 100644 --- a/src/state/themes-mixin.ts +++ b/src/state/themes-mixin.ts @@ -45,25 +45,28 @@ export default >(superClass: T) => } private _applyTheme(dark: boolean) { + if (!this.hass) { + return; + } const themeName = - this.hass!.selectedTheme?.theme || - (dark && this.hass!.themes.default_dark_theme - ? this.hass!.themes.default_dark_theme! - : this.hass!.themes.default_theme); + this.hass.selectedTheme?.theme || + (dark && this.hass.themes.default_dark_theme + ? this.hass.themes.default_dark_theme! + : this.hass.themes.default_theme); let options: Partial = this.hass! .selectedTheme; if (themeName === "default" && options?.dark === undefined) { options = { - ...this.hass!.selectedTheme!, + ...this.hass.selectedTheme!, dark, }; } applyThemesOnElement( document.documentElement, - this.hass!.themes, + this.hass.themes, themeName, options ); @@ -71,11 +74,11 @@ export default >(superClass: T) => const darkMode = themeName === "default" ? !!options?.dark - : !!(dark && this.hass!.themes.default_dark_theme); + : !!(dark && this.hass.themes.default_dark_theme); - if (darkMode !== this.hass!.themes.darkMode) { + if (darkMode !== this.hass.themes.darkMode) { this._updateHass({ - themes: { ...this.hass!.themes, darkMode }, + themes: { ...this.hass.themes, darkMode }, }); const schemeMeta = document.querySelector("meta[name=color-scheme]"); From 6c2b8c2abb08f0dc5b6fccb66eddbc308a4c422e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 23:00:48 +0200 Subject: [PATCH 072/146] Bumped version to 20200909.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8bec01eb50..f666c7161d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200908.0", + version="20200909.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From 498882d01475ec390b24e2de4ac4c7dc335e7a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 9 Sep 2020 23:01:50 +0200 Subject: [PATCH 073/146] Remove mobile_app from generated Lovelace (#6873) * Hide mobile_app from generated Lovelace * simplify * Move to computeDefaultViewStates * removed -> hidden * Update src/panels/lovelace/common/generate-lovelace-config.ts Co-authored-by: Bram Kragten * Adjust for Set * Review comments Co-authored-by: Bram Kragten --- .../common/generate-lovelace-config.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index b5301d37f9..bf49d2116a 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -63,6 +63,8 @@ const HIDE_DOMAIN = new Set([ "zone", ]); +const HIDE_PLATFORM = new Set(["mobile_app"]); + let subscribedRegistries = false; interface SplittedByAreas { @@ -206,11 +208,23 @@ export const computeCards = ( return cards; }; -const computeDefaultViewStates = (entities: HassEntities): HassEntities => { +const computeDefaultViewStates = ( + entities: HassEntities, + entityEntries: EntityRegistryEntry[] +): HassEntities => { const states = {}; + const hiddenEntities = new Set( + entityEntries + .filter((entry) => HIDE_PLATFORM.has(entry.platform)) + .map((entry) => entry.entity_id) + ); + Object.keys(entities).forEach((entityId) => { const stateObj = entities[entityId]; - if (!HIDE_DOMAIN.has(computeStateDomain(stateObj))) { + if ( + !HIDE_DOMAIN.has(computeStateDomain(stateObj)) && + !hiddenEntities.has(stateObj.entity_id) + ) { states[entityId] = entities[entityId]; } }); @@ -317,7 +331,7 @@ export const generateDefaultViewConfig = ( entities: HassEntities, localize: LocalizeFunc ): LovelaceViewConfig => { - const states = computeDefaultViewStates(entities); + const states = computeDefaultViewStates(entities, entityEntries); const path = "default_view"; const title = "Home"; const icon = undefined; From 3039c678a5645b31d9b8f44ddd48f1242f3f4b38 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 9 Sep 2020 23:08:16 +0200 Subject: [PATCH 074/146] Fix check --- src/dialogs/more-info/ha-more-info-dialog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 63381e3251..15d71e694f 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -175,7 +175,7 @@ export class MoreInfoDialog extends LitElement { .stateObj=${stateObj} .hass=${this.hass} > - ${!CONTROL_DOMAINS.includes(domain) || + ${CONTROL_DOMAINS.includes(domain) || !this._computeShowHistoryComponent(entityId) ? "" : html` Date: Wed, 9 Sep 2020 17:03:11 -0500 Subject: [PATCH 075/146] Fix media browse item width (#6870) --- src/components/media-player/ha-media-player-browse.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index b2468fbbd3..741a85b238 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -630,13 +630,20 @@ export class HaMediaPlayerBrowse extends LitElement { display: grid; grid-template-columns: repeat( auto-fit, - minmax(var(--media-browse-item-size, 175px), 0.33fr) + minmax(var(--media-browse-item-size, 175px), 0.1fr) ); grid-gap: 16px; margin: 8px 0px; padding: 0px 24px; } + :host([dialog]) .children { + grid-template-columns: repeat( + auto-fit, + minmax(var(--media-browse-item-size, 175px), 0.33fr) + ); + } + .child { display: flex; flex-direction: column; @@ -689,6 +696,7 @@ export class HaMediaPlayerBrowse extends LitElement { .child .title { font-size: 16px; padding-top: 8px; + padding-left: 2px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; From cdf7558a8e61df7aa85910b6671dccacf9f1203e Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Thu, 10 Sep 2020 00:32:03 +0000 Subject: [PATCH 076/146] [ci skip] Translation update --- translations/frontend/cs.json | 6 ++-- translations/frontend/en.json | 4 +++ translations/frontend/nb.json | 50 +++++++++++++++--------------- translations/frontend/uk.json | 9 ++++-- translations/frontend/zh-Hant.json | 25 +++++++++++++-- 5 files changed, 61 insertions(+), 33 deletions(-) diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 2c11c2fd88..dec6bdf715 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -1497,7 +1497,7 @@ "header": "Konfigurace Home Assistant", "helpers": { "caption": "Pomocníci", - "description": "Prvky, které mohou pomoci při vytváření automatizací.", + "description": "Správa prvků, které mohou pomoci při vytváření automatizací", "dialog": { "add_helper": "Přidat pomocníka", "add_platform": "Přidat {platform}", @@ -1628,7 +1628,7 @@ "logs": { "caption": "Logy", "clear": "Zrušit", - "description": "Zobrazit log Home Assistant", + "description": "Zobrazení logů Home Assistant", "details": "Detaily protokolu ({level})", "load_full_log": "Načíst úplný protokol Home Assistanta", "loading_log": "Načítání protokolu chyb...", @@ -2179,7 +2179,7 @@ "configured_in_yaml": "Zóny konfigurované pomocí configuration.yaml nelze upravovat pomocí UI.", "confirm_delete": "Opravdu chcete tuto zónu smazat?", "create_zone": "Vytvořit zónu", - "description": "Spravujte zóny, ve kterých chcete sledovat osoby.", + "description": "Spravá zón, ve kterých chcete sledovat osoby", "detail": { "create": "Vytvořit", "delete": "Smazat", diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 250666624b..180e20eb4c 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -567,6 +567,9 @@ "loading_history": "Loading state history...", "no_history_found": "No state history found." }, + "logbook": { + "entries_not_found": "No logbook entries found." + }, "media-browser": { "audio_not_supported": "Your browser does not support the audio element.", "choose_player": "Choose Player", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Controls", + "details": "Details", "dismiss": "Dismiss dialog", "edit": "Edit entity", "history": "History", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index f650fd7020..c01cf5a5a5 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -625,12 +625,12 @@ "dialogs": { "config_entry_system_options": { "enable_new_entities_description": "Hvis den er deaktivert, blir ikke nyoppdagede entiteter for {integration} automatisk lagt til i Home Assistant.", - "enable_new_entities_label": "Aktiver enheter som nylig er lagt til.", + "enable_new_entities_label": "Aktiver entiteter som nylig er lagt til.", "title": "Systemalternativer for {integration}", "update": "Oppdater" }, "domain_toggler": { - "reset_entities": "Tilbakestill enheter", + "reset_entities": "Tilbakestill entiteter", "title": "Veksle domener" }, "entity_registry": { @@ -744,7 +744,7 @@ }, "mqtt_device_debug_info": { "deserialize": "Forsøk å analysere MQTT-meldinger som JSON", - "entities": "Enheter", + "entities": "Entiteter", "no_entities": "Ingen entiteter", "no_triggers": "Ingen utløsere", "payload_display": "Nyttelastvisning", @@ -1120,7 +1120,7 @@ "seconds": "Sekunder" }, "time": { - "at": "Klokken", + "at": "På tidspunktet", "label": "Tid", "type_input": "Verdien til en tid/dato hjelper", "type_value": "Bestemt tid" @@ -1193,7 +1193,7 @@ "manage_entities": "Håndtér entiteter", "security_devices": "Sikkerhetsenheter", "sync_entities": "Synkronisér entiteter til Google", - "sync_entities_404_message": "Kunne ikke synkronisere enhetene dine med Google, be Google 'Hei Google, synkroniser enhetene mine' for å synkronisere enhetene dine.", + "sync_entities_404_message": "Kunne ikke synkronisere entitetene dine med Google, be Google 'Hei Google, synkroniser enhetene mine' for å synkronisere entitetene dine.", "title": "" }, "integrations": "Integrasjoner", @@ -1229,15 +1229,15 @@ }, "alexa": { "banner": "Redigere hvilke entiteter som vises via dette grensesnittet er deaktivert fordi du har konfigurert entitetsfilter i configuration.yaml.", - "dont_expose_entity": "Ikke eksponer enheten", + "dont_expose_entity": "Ikke eksponer entiteten", "expose": "Eksponer til Alexa", - "expose_entity": "Vis enhet", + "expose_entity": "Eksponer entitet", "exposed": "{selected} eksponert", "exposed_entities": "Eksponerte entiteter", "follow_domain": "Følg domenet", "manage_domains": "Administrer domener", "not_exposed": "{selected} ikke eksponert", - "not_exposed_entities": "Ikke eksponerte enheter", + "not_exposed_entities": "Ikke eksponerte entiteter", "title": "" }, "caption": "", @@ -1274,15 +1274,15 @@ "google": { "banner": "Redigere hvilke entiteter som vises via dette grensesnittet er deaktivert fordi du har konfigurert entitetsfilter i configuration.yaml.", "disable_2FA": "Deaktiver totrinnsbekreftelse", - "dont_expose_entity": "Ikke eksponer enheten", + "dont_expose_entity": "Ikke eksponer entiteten", "expose": "Eksponer til Google Assistant", - "expose_entity": "Vis enhet", + "expose_entity": "Eksponer entiteten", "exposed": "{selected} eksponert", "exposed_entities": "Eksponerte entiteter", "follow_domain": "Følg domenet", "manage_domains": "Administrer domener", "not_exposed": "{selected} ikke eksponert", - "not_exposed_entities": "Ikke eksponerte enheter", + "not_exposed_entities": "Ikke eksponerte entiteter", "sync_to_google": "Synkroniserer endringer til Google.", "title": "" }, @@ -1383,7 +1383,7 @@ } }, "devices": { - "add_prompt": "Ingen {navn} er lagt til ved hjelp av denne enheten ennå. Du kan legge til en ved å klikke på + knappen ovenfor.", + "add_prompt": "Ingen {name} er lagt til ved hjelp av denne enheten ennå. Du kan legge til en ved å klikke på + knappen ovenfor.", "automation": { "actions": { "caption": "Når noe er utløst..." @@ -1403,7 +1403,7 @@ "caption": "Enheter", "confirm_delete": "Er du sikker på at du vil slette denne enheten?", "confirm_rename_entity_ids": "Vil du også endre navn på entitets-ID-en for entitetene dine?", - "confirm_rename_entity_ids_warning": "Dette vil ikke endre noen konfigurasjon (som automatiseringer, skript, scener, Lovelace) som bruker disse enhetene, må du oppdatere dem selv.", + "confirm_rename_entity_ids_warning": "Dette vil ikke endre noen konfigurasjon (som automatiseringer, skript, scener, Lovelace) som bruker disse entitetene, du må oppdatere dem selv.", "data_table": { "area": "Område", "battery": "Batteri", @@ -1421,9 +1421,9 @@ "entities": { "add_entities_lovelace": "Legg til i Lovelace", "disabled_entities": "+{count} {count, plural,\n one {deaktivert entitet}\n other {deaktiverte entiteter}\n}", - "entities": "Enheter", + "entities": "Entiteter", "hide_disabled": "Skjul deaktiverte", - "none": "Denne enheten har ingen enheter" + "none": "Denne enheten har ingen entiteter" }, "name": "Navn", "no_devices": "Ingen enheter", @@ -1461,7 +1461,7 @@ "filter": "", "show_disabled": "Vis deaktiverte entiteter", "show_readonly": "Vis skrivebeskyttede enheter", - "show_unavailable": "Vis utilgjengelige enheter" + "show_unavailable": "Vis utilgjengelige entiteter" }, "header": "Entiteter", "headers": { @@ -1870,7 +1870,7 @@ "delete": "Slett entitet", "device_entities": "Hvis du legger til en entitet som tilhører en enhet, vil enheten også bli lagt til.", "header": "Entiteter", - "introduction": "Enheter som ikke tilhører en enhet kan angis her.", + "introduction": "Entiteter som ikke tilhører en enhet kan angis her.", "without_device": "Entiteter uten enhet" }, "icon": "Ikon", @@ -1967,19 +1967,19 @@ "input_text": "Last inn inndata tekst på nytt", "introduction": "Noen deler av Home Assistant kan laste inn uten å kreve omstart. Hvis du trykker last på nytt, vil du bytte den nåværende konfigurasjonen med den nye.", "min_max": "Last inn min/maks entiteter på nytt", - "mqtt": "Last inn mqtt-enheter på nytt", + "mqtt": "Last inn mqtt-entiteter på nytt", "person": "Last inn personer på nytt", "ping": "Last inn ping binære sensor entiteter på nytt", "reload": "Last inn {domain} på nytt", - "rest": "Last inn hvileenheter på nytt og varsle tjenester", - "rpi_gpio": "Last inn Raspberry Pi GPIO-enheter på nytt", + "rest": "Last inn REST entiteter og varslingstjenester på nytt", + "rpi_gpio": "Last inn Raspberry Pi GPIO-entiteter på nytt", "scene": "Last inn scener på nytt", "script": "Last inn skript på nytt", "smtp": "Last inn smtp-varslingstjenester på nytt", "statistics": "Last inn statistiske entiteter på nytt", "telegram": "Last inn telegram varslingstjenester på nytt", "template": "Laste inn mal entiteter på nytt", - "trend": "Laste inn trend entiteter på nytt", + "trend": "Last inn trend entiteter på nytt", "universal": "Laste inn universelle mediespiller entiteter på nytt", "zone": "Last inn soner på nytt" }, @@ -2599,7 +2599,7 @@ }, "cardpicker": { "by_card": "Med kort", - "by_entity": "Etter enhet", + "by_entity": "Etter entitet", "custom_card": "Tilpasset", "domain": "Domene", "entity": "Entitet", @@ -2716,14 +2716,14 @@ "refresh_header": "Vil du oppfriske?" }, "unused_entities": { - "available_entities": "Dette er enheter som du har tilgjengelig, men som ikke er i Lovelace brukergrensesnittet ennå.", + "available_entities": "Dette er entitene som du har tilgjengelig, men som ikke er i Lovelace brukergrensesnittet ennå.", "domain": "Domene", "entity": "Entitet", "entity_id": "Entitets-ID", "last_changed": "Sist endret", "no_data": "Ingen ubrukte entiteter funnet", - "search": "Søk etter enheter", - "select_to_add": "Velg enhetene du vil legge til et kort, og klikk deretter på knappen Legg til kort.", + "search": "Søk etter entiteter", + "select_to_add": "Velg entitene du vil legge til et kort, og klikk deretter på knappen Legg til kort.", "title": "Ubrukte entiteter" }, "views": { diff --git a/translations/frontend/uk.json b/translations/frontend/uk.json index 4757374de4..b3501dd8da 100644 --- a/translations/frontend/uk.json +++ b/translations/frontend/uk.json @@ -810,6 +810,9 @@ "label": "Викликати сервіс", "service_data": "Дані сервісу" }, + "wait_for_trigger": { + "timeout": "Таймаут (опціонально)" + }, "wait_template": { "label": "Чекати", "timeout": "Тайм-аут (необов'язково)", @@ -873,7 +876,8 @@ "time": { "after": "Після", "before": "До", - "label": "Час" + "label": "Час", + "type_value": "Фіксований час" }, "zone": { "entity": "Об'єкт з місцем розташування", @@ -982,7 +986,8 @@ }, "time": { "at": "У", - "label": "Час" + "label": "Час", + "type_value": "Фіксований час" }, "webhook": { "label": "Webhook", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 30f780c639..67203a2c85 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -553,6 +553,10 @@ "toggle": "觸發" }, "entity": { + "entity-attribute-picker": { + "attribute": "屬性", + "show_attributes": "顯示屬性" + }, "entity-picker": { "clear": "清除", "entity": "實體", @@ -929,7 +933,13 @@ "label": "執行服務", "service_data": "服務資料" }, + "wait_for_trigger": { + "continue_timeout": "繼續逾時計算", + "label": "等候觸發", + "timeout": "超時(選項)" + }, "wait_template": { + "continue_timeout": "繼續逾時計算", "label": "等待", "timeout": "超時(選項)", "wait_template": "等待模板" @@ -993,7 +1003,9 @@ "time": { "after": "在...之後", "before": "在...之前", - "label": "時間" + "label": "時間", + "type_input": "日期/時間協助數值", + "type_value": "固定時間" }, "zone": { "entity": "區域實體", @@ -1081,6 +1093,7 @@ "value_template": "數值模板(選項)" }, "state": { + "attribute": "屬性(選項)", "for": "持續", "from": "從...狀態", "label": "狀態", @@ -1107,8 +1120,10 @@ "seconds": "秒鐘" }, "time": { - "at": "在...時間", - "label": "時間" + "at": "時間於", + "label": "時間", + "type_input": "日期/時間協助數值", + "type_value": "固定時間" }, "webhook": { "label": "Webhook", @@ -1131,6 +1146,8 @@ "add_automation": "新增一個自動化", "delete_automation": "刪除自動化", "delete_confirm": "確定要刪除此自動化?", + "duplicate": "複製", + "duplicate_automation": "複製自動化", "edit_automation": "編輯自動化", "header": "自動化編輯器", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "新增整合", + "attention": "需要注意", "caption": "整合", "config_entry": { "area": "於 {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "調整搜尋條件。", "note_about_integrations": "目前並非所有整合皆可以透過 UI 進行設定。", "note_about_website_reference": "更多資訊請參閱", + "reconfigure": "重新設定", "rename_dialog": "編輯設定實體名稱", "rename_input_label": "實體名稱", "search": "搜尋整合" From af74f21af9867f65789d6b1b20d47924eb464e20 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 11:12:05 +0200 Subject: [PATCH 077/146] Dont virtualize logbook in more info (#6907) --- src/dialogs/more-info/ha-more-info-history.ts | 10 +- src/panels/logbook/ha-logbook.ts | 183 +++++++++--------- src/panels/logbook/ha-panel-logbook.ts | 37 ++-- 3 files changed, 116 insertions(+), 114 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 4c57bbd2c3..50390b31a2 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -8,7 +8,6 @@ import { PropertyValues, TemplateResult, } from "lit-element"; -import { styleMap } from "lit-html/directives/style-map"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import "../../components/ha-circular-progress"; import "../../components/state-history-charts"; @@ -16,7 +15,7 @@ import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; import { getLogbookData, LogbookEntry } from "../../data/logbook"; import "../../panels/logbook/ha-logbook"; -import { haStyle } from "../../resources/styles"; +import { haStyle, haStyleScrollbar } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @customElement("ha-more-info-history") @@ -59,12 +58,10 @@ export class MoreInfoHistory extends LitElement { : this._entries.length ? html` - ${scroll({ - items: this.entries, - renderItem: (item: LogbookEntry, index?: number) => - this._renderLogbookItem(item, index), - })} + ${this.virtualize + ? scroll({ + items: this.entries, + renderItem: (item: LogbookEntry, index?: number) => + this._renderLogbookItem(item, index), + }) + : this.entries.map((item, index) => + this._renderLogbookItem(item, index) + )}
    `; } @@ -185,106 +191,103 @@ class HaLogbook extends LitElement { }); } - static get styles(): CSSResult[] { - return [ - haStyleScrollbar, - css` - :host { - display: block; - height: 100%; - } + static get styles(): CSSResult { + return css` + :host { + display: block; + height: 100%; + } - .rtl { - direction: ltr; - } + .rtl { + direction: ltr; + } - .entry-container { - width: 100%; - } + .entry-container { + width: 100%; + } - .entry { - display: flex; - width: 100%; - line-height: 2em; - padding: 8px 16px; - box-sizing: border-box; - border-top: 1px solid - var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); - } + .entry { + display: flex; + width: 100%; + line-height: 2em; + padding: 8px 16px; + box-sizing: border-box; + border-top: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } - .time { - display: flex; - justify-content: center; - flex-direction: column; - width: 75px; - flex-shrink: 0; - font-size: 12px; - color: var(--secondary-text-color); - } + .time { + display: flex; + justify-content: center; + flex-direction: column; + width: 75px; + flex-shrink: 0; + font-size: 12px; + color: var(--secondary-text-color); + } - .date { - margin: 8px 0; - padding: 0 16px; - } + .date { + margin: 8px 0; + padding: 0 16px; + } - .narrow .date { - padding: 0 8px; - } + .narrow .date { + padding: 0 8px; + } - .rtl .date { - direction: rtl; - } + .rtl .date { + direction: rtl; + } - .icon-message { - display: flex; - align-items: center; - } + .icon-message { + display: flex; + align-items: center; + } - .no-entries { - text-align: center; - } + .no-entries { + text-align: center; + } - ha-icon { - margin: 0 8px 0 16px; - flex-shrink: 0; - color: var(--primary-text-color); - } + ha-icon { + margin: 0 8px 0 16px; + flex-shrink: 0; + color: var(--primary-text-color); + } - .message { - color: var(--primary-text-color); - } + .message { + color: var(--primary-text-color); + } - .no-name .item-message { - text-transform: capitalize; - } + .no-name .item-message { + text-transform: capitalize; + } - a { - color: var(--primary-color); - } + a { + color: var(--primary-color); + } - .uni-virtualizer-host { - display: block; - position: relative; - contain: strict; - height: 100%; - overflow: auto; - } + .uni-virtualizer-host { + display: block; + position: relative; + contain: strict; + height: 100%; + overflow: auto; + } - .uni-virtualizer-host > * { - box-sizing: border-box; - } + .uni-virtualizer-host > * { + box-sizing: border-box; + } - .narrow .entry { - flex-direction: column; - line-height: 1.5; - padding: 8px; - } + .narrow .entry { + flex-direction: column; + line-height: 1.5; + padding: 8px; + } - .narrow .icon-message ha-icon { - margin-left: 0; - } - `, - ]; + .narrow .icon-message ha-icon { + margin-left: 0; + } + `; } } diff --git a/src/panels/logbook/ha-panel-logbook.ts b/src/panels/logbook/ha-panel-logbook.ts index af57dbc7e6..806cb5ac6b 100644 --- a/src/panels/logbook/ha-panel-logbook.ts +++ b/src/panels/logbook/ha-panel-logbook.ts @@ -1,33 +1,33 @@ +import { mdiRefresh } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; -import "../../components/ha-icon-button"; -import "../../components/ha-circular-progress"; -import { computeRTL } from "../../common/util/compute_rtl"; -import "../../components/entity/ha-entity-picker"; -import "../../components/ha-menu-button"; -import "../../layouts/ha-app-layout"; -import "./ha-logbook"; import { - LitElement, - property, - internalProperty, + css, customElement, html, - css, + internalProperty, + LitElement, + property, PropertyValues, } from "lit-element"; -import { HomeAssistant } from "../../types"; -import { haStyle } from "../../resources/styles"; -import { fetchUsers } from "../../data/user"; -import { fetchPersons } from "../../data/person"; +import { computeRTL } from "../../common/util/compute_rtl"; +import "../../components/entity/ha-entity-picker"; +import "../../components/ha-circular-progress"; +import "../../components/ha-date-range-picker"; +import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; +import "../../components/ha-icon-button"; +import "../../components/ha-menu-button"; import { clearLogbookCache, getLogbookData, LogbookEntry, } from "../../data/logbook"; -import { mdiRefresh } from "@mdi/js"; -import "../../components/ha-date-range-picker"; -import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; +import { fetchPersons } from "../../data/person"; +import { fetchUsers } from "../../data/user"; +import "../../layouts/ha-app-layout"; +import { haStyle } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; +import "./ha-logbook"; @customElement("ha-panel-logbook") export class HaPanelLogbook extends LitElement { @@ -125,6 +125,7 @@ export class HaPanelLogbook extends LitElement { .hass=${this.hass} .entries=${this._entries} .userIdToName=${this._userIdToName} + virtualize >`} `; From 810d2a1ceb97605cebc33a6d594b40b26bd73645 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 13:11:27 +0200 Subject: [PATCH 078/146] Fix onboarding dark mode (#6910) Fixes #6882 --- src/components/map/ha-location-editor.ts | 1 + src/html/onboarding.html.template | 7 ++++++- src/onboarding/onboarding-create-user.ts | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/map/ha-location-editor.ts b/src/components/map/ha-location-editor.ts index 9bfcf5e50e..3b42fd38cc 100644 --- a/src/components/map/ha-location-editor.ts +++ b/src/components/map/ha-location-editor.ts @@ -279,6 +279,7 @@ class LocationEditor extends LitElement { } #map { height: 100%; + background: inherit; } .leaflet-edit-move { border-radius: 50%; diff --git a/src/html/onboarding.html.template b/src/html/onboarding.html.template index 0a605baba1..f4bc333ede 100644 --- a/src/html/onboarding.html.template +++ b/src/html/onboarding.html.template @@ -11,7 +11,12 @@ @media (prefers-color-scheme: dark) { html { background-color: #111111; - color: var(--primary-text-color, #e1e1e1); + color: #e1e1e1; + } + ha-onboarding { + --primary-text-color: #e1e1e1; + --secondary-text-color: #9b9b9b; + --disabled-text-color: #6f6f6f; } } .content { diff --git a/src/onboarding/onboarding-create-user.ts b/src/onboarding/onboarding-create-user.ts index d12fcd97ce..bb3f27ba2e 100644 --- a/src/onboarding/onboarding-create-user.ts +++ b/src/onboarding/onboarding-create-user.ts @@ -6,9 +6,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; From c2e6d403828095c0942f86bc3e81cd3bbfcdd663 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2020 15:16:03 +0200 Subject: [PATCH 079/146] Bump http-proxy from 1.17.0 to 1.18.1 (#6914) Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/http-party/node-http-proxy/releases) - [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md) - [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4a0450044e..1a6af52a72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4942,7 +4942,7 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@3.2.6, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@3.2.6, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5711,11 +5711,16 @@ event-target-shim@^5.0.1: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@3.1.0, eventemitter3@^3.0.0: +eventemitter3@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" @@ -6223,11 +6228,9 @@ fn-name@~2.0.1: integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc= follow-redirects@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" - integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== - dependencies: - debug "^3.2.6" + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" @@ -6977,11 +6980,11 @@ http-proxy-middleware@0.19.1: micromatch "^3.1.10" http-proxy@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== dependencies: - eventemitter3 "^3.0.0" + eventemitter3 "^4.0.0" follow-redirects "^1.0.0" requires-port "^1.0.0" From 96c5fdcbeb517f29243488a016af5c56262c887c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 15:17:32 +0200 Subject: [PATCH 080/146] Fix some lovelace editors (#6911) * Fix some lovelace editors * let -> const --- .../lovelace/components/hui-action-editor.ts | 35 ++++----- .../config-elements/hui-button-card-editor.ts | 77 ++++++++++--------- .../config-elements/hui-light-card-editor.ts | 28 +++---- .../hui-picture-card-editor.ts | 26 +++---- .../hui-picture-entity-card-editor.ts | 76 +++++++++--------- .../hui-picture-glance-card-editor.ts | 49 ++++-------- 6 files changed, 131 insertions(+), 160 deletions(-) diff --git a/src/panels/lovelace/components/hui-action-editor.ts b/src/panels/lovelace/components/hui-action-editor.ts index 5899360217..dea53bd0d3 100644 --- a/src/panels/lovelace/components/hui-action-editor.ts +++ b/src/panels/lovelace/components/hui-action-editor.ts @@ -1,4 +1,5 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-textarea"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; @@ -9,7 +10,7 @@ import { property, TemplateResult, } from "lit-element"; -import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; +import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-service-picker"; import { ActionConfig, @@ -20,17 +21,6 @@ import { import { HomeAssistant } from "../../../types"; import { EditorTarget } from "../editor/types"; -declare global { - // for fire event - interface HASSDomEvents { - "action-changed": undefined; - } - // for add event listener - interface HTMLElementEventMap { - "action-changed": HASSDomEvent; - } -} - @customElement("hui-action-editor") export class HuiActionEditor extends LitElement { @property() public config?: ActionConfig; @@ -42,21 +32,21 @@ export class HuiActionEditor extends LitElement { @property() protected hass?: HomeAssistant; get _action(): string { - return this.config!.action || ""; + return this.config?.action || ""; } get _navigation_path(): string { - const config = this.config! as NavigateActionConfig; + const config = this.config as NavigateActionConfig; return config.navigation_path || ""; } get _url_path(): string { - const config = this.config! as UrlActionConfig; + const config = this.config as UrlActionConfig; return config.url_path || ""; } get _service(): string { - const config = this.config! as CallServiceActionConfig; + const config = this.config as CallServiceActionConfig; return config.service || ""; } @@ -107,13 +97,14 @@ export class HuiActionEditor extends LitElement { .configValue="${"service"}" @value-changed="${this._valueChanged}" > -

    Toggle Editor to input Service Data

    + Service data can only be entered in the code editor ` : ""} `; } private _valueChanged(ev: Event): void { + ev.stopPropagation(); if (!this.hass) { return; } @@ -121,12 +112,12 @@ export class HuiActionEditor extends LitElement { if (this[`_${target.configValue}`] === target.value) { return; } - if (target.configValue === "action") { - this.config = { action: "none" }; - } if (target.configValue) { - this.config = { ...this.config!, [target.configValue!]: target.value }; - fireEvent(this, "action-changed"); + const newConfig = + target.configValue === "action" + ? { action: target.value } + : { ...this.config!, [target.configValue!]: target.value }; + fireEvent(this, "value-changed", { value: newConfig }); } } } diff --git a/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts index 36ce2aab96..a2285c087d 100644 --- a/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts @@ -2,14 +2,18 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, boolean, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { stateIcon } from "../../../../common/entity/state_icon"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; +import "../../../../components/ha-formfield"; import "../../../../components/ha-icon-input"; +import "../../../../components/ha-switch"; import { ActionConfig } from "../../../../data/lovelace"; import { HomeAssistant } from "../../../../types"; import { ButtonCardConfig } from "../../cards/types"; @@ -17,16 +21,8 @@ import "../../components/hui-action-editor"; import "../../components/hui-entity-editor"; import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; -import { - actionConfigStruct, - EditorTarget, - EntitiesEditorEvent, -} from "../types"; -import "../../../../components/ha-switch"; -import "../../../../components/ha-formfield"; +import { actionConfigStruct, EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; -import { assert, object, string, optional, boolean } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -63,11 +59,11 @@ export class HuiButtonCardEditor extends LitElement } get _show_name(): boolean { - return this._config!.show_name || true; + return this._config!.show_name ?? true; } get _show_state(): boolean { - return this._config!.show_state || false; + return this._config!.show_state ?? false; } get _icon(): string { @@ -75,7 +71,7 @@ export class HuiButtonCardEditor extends LitElement } get _show_icon(): boolean { - return this._config!.show_icon || true; + return this._config!.show_icon ?? true; } get _icon_height(): string { @@ -85,11 +81,11 @@ export class HuiButtonCardEditor extends LitElement } get _tap_action(): ActionConfig { - return this._config!.tap_action || { action: "more-info" }; + return this._config!.tap_action || { action: "toggle" }; } get _hold_action(): ActionConfig { - return this._config!.hold_action || { action: "none" }; + return this._config!.hold_action || { action: "more-info" }; } get _theme(): string { @@ -123,7 +119,7 @@ export class HuiButtonCardEditor extends LitElement .hass=${this.hass} .value="${this._entity}" .configValue=${"entity"} - @change="${this._valueChanged}" + @value-changed="${this._valueChanged}" allow-custom-entity >
    @@ -161,7 +157,7 @@ export class HuiButtonCardEditor extends LitElement
    @@ -175,7 +171,7 @@ export class HuiButtonCardEditor extends LitElement
    @@ -189,7 +185,7 @@ export class HuiButtonCardEditor extends LitElement
    @@ -225,7 +221,7 @@ export class HuiButtonCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" >
    `; } - private _valueChanged(ev: EntitiesEditorEvent): void { + private _change(ev: Event) { if (!this._config || !this.hass) { return; } const target = ev.target! as EditorTarget; + const value = target.checked; - if ( - this[`_${target.configValue}`] === target.value || - this[`_${target.configValue}`] === target.config - ) { + if (this[`_${target.configValue}`] === value) { + return; + } + + this._config = { + ...this._config, + [target.configValue!]: value, + }; + fireEvent(this, "config-changed", { config: this._config }); + } + + private _valueChanged(ev: CustomEvent): void { + if (!this._config || !this.hass) { + return; + } + const target = ev.target! as EditorTarget; + const value = ev.detail.value; + + if (this[`_${target.configValue}`] === value) { return; } if (target.configValue) { - if (target.value === "") { + if (value !== false && !value) { this._config = { ...this._config }; delete this._config[target.configValue!]; } else { @@ -266,18 +278,11 @@ export class HuiButtonCardEditor extends LitElement target.configValue === "icon_height" && !isNaN(Number(target.value)) ) { - newValue = `${String(target.value)}px`; + newValue = `${String(value)}px`; } this._config = { ...this._config, - [target.configValue!]: - target.checked !== undefined - ? target.checked - : newValue !== undefined - ? newValue - : target.value - ? target.value - : target.config, + [target.configValue!]: newValue !== undefined ? newValue : value, }; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts index 6e37bd0358..90b63d4276 100644 --- a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts @@ -2,11 +2,12 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { stateIcon } from "../../../../common/entity/state_icon"; import "../../../../components/ha-icon-input"; @@ -17,13 +18,8 @@ import "../../components/hui-action-editor"; import "../../components/hui-entity-editor"; import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; -import { - actionConfigStruct, - EditorTarget, - EntitiesEditorEvent, -} from "../types"; +import { actionConfigStruct, EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { string, object, optional, assert } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -100,7 +96,7 @@ export class HuiLightCardEditor extends LitElement .value=${this._entity} .configValue=${"entity"} .includeDomains=${includeDomains} - @change=${this._valueChanged} + @value-changed=${this._valueChanged} allow-custom-entity >
    @@ -145,7 +141,7 @@ export class HuiLightCardEditor extends LitElement .config=${this._hold_action} .actions=${actions} .configValue=${"hold_action"} - @action-changed=${this._valueChanged} + @value-changed=${this._valueChanged} >
    `; } - private _valueChanged(ev: EntitiesEditorEvent): void { + private _valueChanged(ev: CustomEvent): void { if (!this._config || !this.hass) { return; } const target = ev.target! as EditorTarget; + const value = ev.detail.value; - if ( - this[`_${target.configValue}`] === target.value || - this[`_${target.configValue}`] === target.config - ) { + if (this[`_${target.configValue}`] === value) { return; } if (target.configValue) { - if (target.value === "") { + if (value !== false && !value) { this._config = { ...this._config }; delete this._config[target.configValue!]; } else { this._config = { ...this._config, - [target.configValue!]: target.value ? target.value : target.config, + [target.configValue!]: value, }; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts index 98fbf6af9a..8858545325 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts @@ -2,11 +2,12 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { ActionConfig } from "../../../../data/lovelace"; import { HomeAssistant } from "../../../../types"; @@ -14,13 +15,8 @@ import { PictureCardConfig } from "../../cards/types"; import "../../components/hui-action-editor"; import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; -import { - actionConfigStruct, - EditorTarget, - EntitiesEditorEvent, -} from "../types"; +import { actionConfigStruct, EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { string, object, optional, assert } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -89,7 +85,7 @@ export class HuiPictureCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" > @@ -184,8 +180,7 @@ export class HuiPictureEntityCardEditor extends LitElement )} (${this.hass.localize( "ui.panel.lovelace.editor.card.config.optional" )})" - type="number" - .value="${Number(this._aspect_ratio.replace("%", ""))}" + .value="${this._aspect_ratio}" .configValue="${"aspect_ratio"}" @value-changed="${this._valueChanged}" > @@ -201,7 +196,7 @@ export class HuiPictureEntityCardEditor extends LitElement
    @@ -215,7 +210,7 @@ export class HuiPictureEntityCardEditor extends LitElement
    @@ -231,7 +226,7 @@ export class HuiPictureEntityCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" > @@ -180,8 +171,7 @@ export class HuiPictureGlanceCardEditor extends LitElement )} (${this.hass.localize( "ui.panel.lovelace.editor.card.config.optional" )})" - type="number" - .value="${Number(this._aspect_ratio.replace("%", ""))}" + .value="${this._aspect_ratio}" .configValue="${"aspect_ratio"}" @value-changed="${this._valueChanged}" > @@ -195,7 +185,7 @@ export class HuiPictureGlanceCardEditor extends LitElement .hass=${this.hass} .value="${this._entity}" .configValue=${"entity"} - @change="${this._valueChanged}" + @value-changed="${this._valueChanged}" allow-custom-entity >
    @@ -209,7 +199,7 @@ export class HuiPictureGlanceCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" >
    Date: Thu, 10 Sep 2020 16:59:12 +0200 Subject: [PATCH 081/146] Diable tts inputs when entities is unavailable (#6909) Fixes #6890 --- src/dialogs/more-info/controls/more-info-media_player.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts index 45dee3e224..2a54b7053c 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.ts +++ b/src/dialogs/more-info/controls/more-info-media_player.ts @@ -188,14 +188,17 @@ class MoreInfoMediaPlayer extends LitElement {
    - +
    ` From 7fa9f10c30a47f24dfa713754b915c5cba3838ef Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 17:24:01 +0200 Subject: [PATCH 082/146] Don't add space on the bottom when not showing tabs (#6913) --- src/layouts/hass-tabs-subpage.ts | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index 2f35492c5c..c110a02836 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -3,26 +3,26 @@ import { css, CSSResult, customElement, + eventOptions, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, - eventOptions, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../common/config/is_component_loaded"; -import { navigate } from "../common/navigate"; -import "../components/ha-menu-button"; -import "../components/ha-icon-button-arrow-prev"; -import { HomeAssistant, Route } from "../types"; -import "../components/ha-svg-icon"; -import "../components/ha-icon"; -import "../components/ha-tab"; import { restoreScroll } from "../common/decorators/restore-scroll"; +import { navigate } from "../common/navigate"; import { computeRTL } from "../common/util/compute_rtl"; +import "../components/ha-icon"; +import "../components/ha-icon-button-arrow-prev"; +import "../components/ha-menu-button"; +import "../components/ha-svg-icon"; +import "../components/ha-tab"; +import { HomeAssistant, Route } from "../types"; export interface PageNavigation { path: string; @@ -132,7 +132,7 @@ class HassTabsSubpage extends LitElement { this.hass.language, this.narrow ); - + const showTabs = tabs.length > 1 || !this.narrow; return html`
    ${this.mainPage @@ -152,7 +152,7 @@ class HassTabsSubpage extends LitElement { ${this.narrow ? html`
    ` : ""} - ${tabs.length > 1 || !this.narrow + ${showTabs ? html`
    ${tabs} @@ -163,10 +163,15 @@ class HassTabsSubpage extends LitElement {
    -
    +
    -
    +
    + +
    `; } @@ -274,12 +279,13 @@ class HassTabsSubpage extends LitElement { margin-left: env(safe-area-inset-left); margin-right: env(safe-area-inset-right); height: calc(100% - 65px); + height: calc(100% - 65px - env(safe-area-inset-bottom)); overflow-y: auto; overflow: auto; -webkit-overflow-scrolling: touch; } - :host([narrow]) .content { + :host([narrow]) .content.tabs { height: calc(100% - 128px); height: calc(100% - 128px - env(safe-area-inset-bottom)); } @@ -290,7 +296,7 @@ class HassTabsSubpage extends LitElement { bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 1; } - :host([narrow]) #fab { + :host([narrow]) #fab.tabs { bottom: calc(84px + env(safe-area-inset-bottom)); } #fab[is-wide] { From b9699f745f8acb057e91b8719d1529049edaf2d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 10 Sep 2020 13:50:24 -0500 Subject: [PATCH 083/146] Avoid watching all states in the default template (#6918) --- .../developer-tools/template/developer-tools-template.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index 0bb9d0f78e..fbc5e03b63 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -31,10 +31,9 @@ The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}. The sun will rise at {{ as_timestamp(strptime(state_attr("sun.sun", "next_rising"), "")) | timestamp_local }}. {%- endif %} -For loop example getting 3 entity values: +For loop example getting entity values in the weather domain: -{% for states in states | slice(3) -%} - {% set state = states | first %} +{% for state in states.weather -%} {%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%} {{ state.name | lower }} is {{state.state_with_unit}} {%- endfor %}.`; From 092a02a624e1c30308c67002df6dd26eecf2b93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 10 Sep 2020 20:51:23 +0200 Subject: [PATCH 084/146] Convert ha-long-lived-access-tokens-card (#6917) Co-authored-by: Zack Barett Co-authored-by: Bram Kragten --- src/data/refresh_token.ts | 17 ++ src/dialogs/generic/dialog-box.ts | 23 ++- .../ha-long-lived-access-tokens-card.js | 171 ---------------- .../ha-long-lived-access-tokens-card.ts | 193 ++++++++++++++++++ src/panels/profile/ha-panel-profile.ts | 11 +- src/translations/en.json | 7 +- 6 files changed, 238 insertions(+), 184 deletions(-) create mode 100644 src/data/refresh_token.ts delete mode 100644 src/panels/profile/ha-long-lived-access-tokens-card.js create mode 100644 src/panels/profile/ha-long-lived-access-tokens-card.ts diff --git a/src/data/refresh_token.ts b/src/data/refresh_token.ts new file mode 100644 index 0000000000..bc2752bdf0 --- /dev/null +++ b/src/data/refresh_token.ts @@ -0,0 +1,17 @@ +declare global { + interface HASSDomEvents { + "hass-refresh-tokens": undefined; + } +} + +export interface RefreshToken { + client_icon?: string; + client_id: string; + client_name?: string; + created_at: string; + id: string; + is_current: boolean; + last_used_at?: string; + last_used_ip?: string; + type: "normal" | "long_lived_access_token"; +} diff --git a/src/dialogs/generic/dialog-box.ts b/src/dialogs/generic/dialog-box.ts index 7d63d3dd53..5df7cb4f1e 100644 --- a/src/dialogs/generic/dialog-box.ts +++ b/src/dialogs/generic/dialog-box.ts @@ -57,7 +57,8 @@ class DialogBox extends LitElement { open ?scrimClickAction=${this._params.prompt} ?escapeKeyAction=${this._params.prompt} - @closed=${this._dismiss} + @closed=${this._dialogClosed} + defaultAction="ignore" .heading=${this._params.title ? this._params.title : this._params.confirmation && @@ -78,10 +79,10 @@ class DialogBox extends LitElement { ${this._params.prompt ? html` `} - + ${this._params.confirmText ? this._params.confirmText : this.hass.localize("ui.dialogs.generic.ok")} @@ -133,7 +138,17 @@ class DialogBox extends LitElement { this._close(); } + private _dialogClosed(ev) { + if (ev.detail.action === "ignore") { + return; + } + this.closeDialog(); + } + private _close(): void { + if (!this._params) { + return; + } this._params = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.js b/src/panels/profile/ha-long-lived-access-tokens-card.js deleted file mode 100644 index 5943164610..0000000000 --- a/src/panels/profile/ha-long-lived-access-tokens-card.js +++ /dev/null @@ -1,171 +0,0 @@ -import "@material/mwc-button"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatDateTime } from "../../common/datetime/format_date_time"; -import "../../components/ha-card"; -import "../../components/ha-icon-button"; -import { - showAlertDialog, - showPromptDialog, - showConfirmationDialog, -} from "../../dialogs/generic/show-dialog-box"; -import { EventsMixin } from "../../mixins/events-mixin"; -import LocalizeMixin from "../../mixins/localize-mixin"; -import "../../styles/polymer-ha-style"; -import "../../components/ha-settings-row"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -class HaLongLivedTokens extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - -
    -

    - [[localize('ui.panel.profile.long_lived_access_tokens.description')]] - - [[localize('ui.panel.profile.long_lived_access_tokens.learn_auth_requests')]] - -

    - -
    - -
    - - [[localize('ui.panel.profile.long_lived_access_tokens.create')]] - -
    -
    - `; - } - - static get properties() { - return { - hass: Object, - refreshTokens: Array, - _tokens: { - type: Array, - computed: "_computeTokens(refreshTokens)", - }, - }; - } - - _computeTokens(refreshTokens) { - return refreshTokens - .filter((tkn) => tkn.type === "long_lived_access_token") - .reverse(); - } - - _formatTitle(name) { - return this.localize( - "ui.panel.profile.long_lived_access_tokens.token_title", - "name", - name - ); - } - - _formatCreatedAt(created) { - return this.localize( - "ui.panel.profile.long_lived_access_tokens.created_at", - "date", - formatDateTime(new Date(created), this.hass.language) - ); - } - - async _handleCreate() { - const name = await showPromptDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.prompt_name" - ), - }); - if (!name) return; - try { - const token = await this.hass.callWS({ - type: "auth/long_lived_access_token", - lifespan: 3650, - client_name: name, - }); - await showPromptDialog(this, { - title: name, - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.prompt_copy_token" - ), - defaultValue: token, - }); - this.fire("hass-refresh-tokens"); - } catch (err) { - // eslint-disable-next-line - console.error(err); - showAlertDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.create_failed" - ), - }); - } - } - - async _handleDelete(ev) { - const token = ev.model.item; - if ( - !(await showConfirmationDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.confirm_delete", - "name", - token.client_name - ), - })) - ) { - return; - } - try { - await this.hass.callWS({ - type: "auth/delete_refresh_token", - refresh_token_id: token.id, - }); - this.fire("hass-refresh-tokens"); - } catch (err) { - // eslint-disable-next-line - console.error(err); - showAlertDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.delete_failed" - ), - }); - } - } -} - -customElements.define("ha-long-lived-access-tokens-card", HaLongLivedTokens); diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.ts b/src/panels/profile/ha-long-lived-access-tokens-card.ts new file mode 100644 index 0000000000..8c8eb36d96 --- /dev/null +++ b/src/panels/profile/ha-long-lived-access-tokens-card.ts @@ -0,0 +1,193 @@ +import "@material/mwc-button"; +import { + css, + CSSResultArray, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import relativeTime from "../../common/datetime/relative_time"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-card"; +import "../../components/ha-icon-button"; +import "../../components/ha-settings-row"; +import { RefreshToken } from "../../data/refresh_token"; +import { + showAlertDialog, + showConfirmationDialog, + showPromptDialog, +} from "../../dialogs/generic/show-dialog-box"; +import { haStyle } from "../../resources/styles"; +import "../../styles/polymer-ha-style"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-long-lived-access-tokens-card") +class HaLongLivedTokens extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public refreshTokens?: RefreshToken[]; + + private _accessTokens = memoizeOne( + (refreshTokens: RefreshToken[]): RefreshToken[] => + refreshTokens + ?.filter((token) => token.type === "long_lived_access_token") + .reverse() + ); + + protected render(): TemplateResult { + const accessTokens = this._accessTokens(this.refreshTokens!); + + return html` + +
    + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.description" + )} + + + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.learn_auth_requests" + )} + + ${!accessTokens?.length + ? html`

    + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.empty_state" + )} +

    ` + : accessTokens!.map( + (token) => html` + ${token.client_name} +
    + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.created", + "date", + relativeTime( + new Date(token.created_at), + this.hass.localize + ) + )} +
    + +
    ` + )} +
    + +
    + + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.create" + )} + +
    +
    + `; + } + + private async _createToken(): Promise { + const name = await showPromptDialog(this, { + text: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.prompt_name" + ), + inputLabel: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.name" + ), + }); + + if (!name) { + return; + } + + try { + const token = await this.hass.callWS({ + type: "auth/long_lived_access_token", + lifespan: 3650, + client_name: name, + }); + + showPromptDialog(this, { + title: name, + text: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.prompt_copy_token" + ), + defaultValue: token, + }); + + fireEvent(this, "hass-refresh-tokens"); + } catch (err) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.create_failed" + ), + text: err.message, + }); + } + } + + private async _deleteToken(ev: Event): Promise { + const token = (ev.currentTarget as any).token; + if ( + !(await showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.confirm_delete", + "name", + token.client_name + ), + })) + ) { + return; + } + try { + await this.hass.callWS({ + type: "auth/delete_refresh_token", + refresh_token_id: token.id, + }); + fireEvent(this, "hass-refresh-tokens"); + } catch (err) { + await showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.delete_failed" + ), + text: err.message, + }); + } + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + ha-settings-row { + padding: 0; + } + a { + color: var(--primary-color); + } + ha-icon-button { + color: var(--primary-text-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-long-lived-access-tokens-card": HaLongLivedTokens; + } +} diff --git a/src/panels/profile/ha-panel-profile.ts b/src/panels/profile/ha-panel-profile.ts index 3c429e2e31..b4aa386092 100644 --- a/src/panels/profile/ha-panel-profile.ts +++ b/src/panels/profile/ha-panel-profile.ts @@ -1,5 +1,4 @@ import "@material/mwc-button"; -import "../../layouts/ha-app-layout"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import "@polymer/paper-item/paper-item"; @@ -9,9 +8,9 @@ import { css, CSSResultArray, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import { fireEvent } from "../../common/dom/fire_event"; @@ -22,7 +21,9 @@ import { CoreFrontendUserData, getOptimisticFrontendUserDataCollection, } from "../../data/frontend"; +import { RefreshToken } from "../../data/refresh_token"; import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box"; +import "../../layouts/ha-app-layout"; import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; import "./ha-advanced-mode-row"; @@ -35,15 +36,15 @@ import "./ha-pick-language-row"; import "./ha-pick-theme-row"; import "./ha-push-notifications-row"; import "./ha-refresh-tokens-card"; -import "./ha-set-vibrate-row"; import "./ha-set-suspend-row"; +import "./ha-set-vibrate-row"; class HaPanelProfile extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public narrow!: boolean; + @property({ type: Boolean }) public narrow!: boolean; - @internalProperty() private _refreshTokens?: unknown[]; + @internalProperty() private _refreshTokens?: RefreshToken[]; @internalProperty() private _coreUserData?: CoreFrontendUserData | null; diff --git a/src/translations/en.json b/src/translations/en.json index 24f05139cc..29cc400560 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2512,14 +2512,13 @@ "header": "Long-Lived Access Tokens", "description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.", "learn_auth_requests": "Learn how to make authenticated requests.", - "created_at": "Created at {date}", - "last_used": "Last used at {date} from {location}", - "not_used": "Has never been used", + "created": "Created {date}", "confirm_delete": "Are you sure you want to delete the access token for {name}?", "delete_failed": "Failed to delete the access token.", "create": "Create Token", "create_failed": "Failed to create the access token.", - "prompt_name": "Name?", + "name": "Name", + "prompt_name": "Give the token a name", "prompt_copy_token": "Copy your access token. It will not be shown again.", "empty_state": "You have no long-lived access tokens yet." } From c06357a351377906b57bf6fd8641292d2646f8d6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 21:47:18 +0200 Subject: [PATCH 085/146] Only show what triggered a change if it wasn't a user (#6919) * Only show what triggered a change if it wasn't a user * Update ha-logbook.ts --- src/panels/logbook/ha-logbook.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 33269319b6..e94fd7c72d 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -149,20 +149,23 @@ class HaLogbook extends LitElement { > ` : ""} - ${item.message} - ${item_username ? ` (${item_username})` : ``} - ${!item.context_event_type + ${item.message} + ${item_username + ? ` by ${item_username}` + : !item.context_event_type ? "" : item.context_event_type === "call_service" ? // Service Call - html` by service + ` by service ${item.context_domain}.${item.context_service}` : item.context_entity_id === item.entity_id ? // HomeKit or something that self references - html` by - ${item.context_name - ? item.context_name - : item.context_event_type}` + ` by + ${ + item.context_name + ? item.context_name + : item.context_event_type + }` : // Another entity such as an automation or script html` by Date: Thu, 10 Sep 2020 22:04:16 +0200 Subject: [PATCH 086/146] Ignore disconnect codes for shutdown and reboot (#6901) Co-authored-by: Bram Kragten --- hassio/src/dashboard/hassio-update.ts | 3 ++- hassio/src/system/hassio-host-info.ts | 27 ++++++++++++++++++--------- src/data/hassio/common.ts | 2 ++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index 9bb6cc73ec..eec4097e54 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -16,6 +16,7 @@ import "../../../src/components/ha-svg-icon"; import { extractApiErrorMessage, HassioResponse, + ignoredStatusCodes, } from "../../../src/data/hassio/common"; import { HassioHassOSInfo } from "../../../src/data/hassio/host"; import { @@ -166,7 +167,7 @@ export class HassioUpdate extends LitElement { } catch (err) { // Only show an error if the status code was not expected (user behind proxy) // or no status at all(connection terminated) - if (err.status_code && ![502, 503, 504].includes(err.status_code)) { + if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { showAlertDialog(this, { title: "Update failed", text: extractApiErrorMessage(err), diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index bdd3e801b4..5f44703ac3 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -19,7 +19,10 @@ import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-card"; import "../../../src/components/ha-settings-row"; -import { extractApiErrorMessage } from "../../../src/data/hassio/common"; +import { + extractApiErrorMessage, + ignoredStatusCodes, +} from "../../../src/data/hassio/common"; import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware"; import { changeHostOptions, @@ -245,10 +248,13 @@ class HassioHostInfo extends LitElement { try { await rebootHost(this.hass); } catch (err) { - showAlertDialog(this, { - title: "Failed to reboot", - text: extractApiErrorMessage(err), - }); + // Ignore connection errors, these are all expected + if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { + showAlertDialog(this, { + title: "Failed to reboot", + text: extractApiErrorMessage(err), + }); + } } button.progress = false; } @@ -272,10 +278,13 @@ class HassioHostInfo extends LitElement { try { await shutdownHost(this.hass); } catch (err) { - showAlertDialog(this, { - title: "Failed to shutdown", - text: extractApiErrorMessage(err), - }); + // Ignore connection errors, these are all expected + if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { + showAlertDialog(this, { + title: "Failed to shutdown", + text: extractApiErrorMessage(err), + }); + } } button.progress = false; } diff --git a/src/data/hassio/common.ts b/src/data/hassio/common.ts index 4ab432b855..ad315096a3 100644 --- a/src/data/hassio/common.ts +++ b/src/data/hassio/common.ts @@ -13,3 +13,5 @@ export const extractApiErrorMessage = (error: any): string => { : error.body || "Unknown error, see logs" : error; }; + +export const ignoredStatusCodes = new Set([502, 503, 504]); From aa4bc2ce03dd0b16b1d4567d2afcb39d6cb2f142 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 22:40:54 +0200 Subject: [PATCH 087/146] Make logbook a bit smaller in more info (#6921) --- src/dialogs/more-info/ha-more-info-history.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 50390b31a2..c75181a1b4 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -148,7 +148,7 @@ export class MoreInfoHistory extends LitElement { padding: 16px; } ha-logbook { - max-height: 360px; + max-height: 250px; overflow: auto; } ha-circular-progress { From 2139a80a7aa40b830d24924989f26636a19977ac Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Thu, 10 Sep 2020 22:59:45 +0200 Subject: [PATCH 088/146] Use proper constants for "unavailable" checks (#6922) * Use proper constants for "unavailable" * Additional usage of constants --- src/common/entity/state_card_type.ts | 3 ++- src/components/entity/ha-state-label-badge.ts | 10 ++++++---- src/dialogs/more-info/controls/more-info-automation.ts | 5 +++-- src/panels/config/automation/ha-automation-picker.ts | 9 +++++---- src/panels/config/entities/ha-config-entities.ts | 3 ++- src/panels/lovelace/cards/hui-light-card.ts | 4 ++-- src/panels/lovelace/common/validate-condition.ts | 3 ++- src/panels/lovelace/components/hui-image.ts | 3 ++- .../entity-rows/hui-input-datetime-entity-row.ts | 6 +++--- .../lovelace/entity-rows/hui-sensor-entity-row.ts | 4 ++-- test-mocha/common/entity/compute_state_display.ts | 3 ++- 11 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/common/entity/state_card_type.ts b/src/common/entity/state_card_type.ts index a24994d5b3..2013ee2058 100644 --- a/src/common/entity/state_card_type.ts +++ b/src/common/entity/state_card_type.ts @@ -3,9 +3,10 @@ import { HomeAssistant } from "../../types"; import { DOMAINS_WITH_CARD } from "../const"; import { canToggleState } from "./can_toggle_state"; import { computeStateDomain } from "./compute_state_domain"; +import { UNAVAILABLE } from "../../data/entity"; export const stateCardType = (hass: HomeAssistant, stateObj: HassEntity) => { - if (stateObj.state === "unavailable") { + if (stateObj.state === UNAVAILABLE) { return "display"; } diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index b38009042f..3441979436 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -20,6 +20,7 @@ import { stateIcon } from "../../common/entity/state_icon"; import { timerTimeRemaining } from "../../common/entity/timer_time_remaining"; import { HomeAssistant } from "../../types"; import "../ha-label-badge"; +import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; @customElement("ha-state-label-badge") export class HaStateLabelBadge extends LitElement { @@ -81,7 +82,8 @@ export class HaStateLabelBadge extends LitElement { ? "" : this.image ? this.image - : state.attributes.entity_picture_local || state.attributes.entity_picture}" + : state.attributes.entity_picture_local || + state.attributes.entity_picture}" .label="${this._computeLabel(domain, state, this._timerTimeRemaining)}" .description="${this.name ? this.name : computeStateName(state)}" > @@ -108,7 +110,7 @@ export class HaStateLabelBadge extends LitElement { return null; case "sensor": default: - return state.state === "unknown" + return state.state === UNKNOWN ? "-" : state.attributes.unit_of_measurement ? state.state @@ -121,7 +123,7 @@ export class HaStateLabelBadge extends LitElement { } private _computeIcon(domain: string, state: HassEntity) { - if (state.state === "unavailable") { + if (state.state === UNAVAILABLE) { return null; } switch (domain) { @@ -166,7 +168,7 @@ export class HaStateLabelBadge extends LitElement { private _computeLabel(domain, state, _timerTimeRemaining) { if ( - state.state === "unavailable" || + state.state === UNAVAILABLE || ["device_tracker", "alarm_control_panel", "person"].includes(domain) ) { // Localize the state with a special state_badge namespace, which has variations of diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index 8b4ff441c2..6a1a789b99 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -12,12 +12,13 @@ import { import "../../../components/ha-relative-time"; import { triggerAutomation } from "../../../data/automation"; import { HomeAssistant } from "../../../types"; +import { UNAVAILABLE_STATES } from "../../../data/entity"; @customElement("more-info-automation") class MoreInfoAutomation extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public stateObj?: HassEntity; + @property({ attribute: false }) public stateObj?: HassEntity; protected render(): TemplateResult { if (!this.hass || !this.stateObj) { @@ -36,7 +37,7 @@ class MoreInfoAutomation extends LitElement {
    ${this.hass.localize("ui.card.automation.trigger")} diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 221f7f1e14..d95e80f079 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -25,6 +25,7 @@ import { showAutomationEditor, triggerAutomation, } from "../../../data/automation"; +import { UNAVAILABLE_STATES } from "../../../data/entity"; import "../../../layouts/hass-tabs-subpage-data-table"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; @@ -35,9 +36,9 @@ import { showThingtalkDialog } from "./show-dialog-thingtalk"; class HaAutomationPicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public isWide!: boolean; + @property({ type: Boolean }) public isWide!: boolean; - @property() public narrow!: boolean; + @property({ type: Boolean }) public narrow!: boolean; @property() public route!: Route; @@ -58,7 +59,7 @@ class HaAutomationPicker extends LitElement { toggle: { title: "", type: "icon", - template: (_toggle, automation) => + template: (_toggle, automation: any) => html` this._execute(ev)} - .disabled=${automation.state === "unavailable"} + .disabled=${UNAVAILABLE_STATES.includes(automation.state)} > ${this.hass.localize("ui.card.automation.trigger")} diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 8ac21ad076..366c852fa9 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -59,6 +59,7 @@ import { showEntityEditorDialog, } from "./show-dialog-entity-editor"; import { haStyle } from "../../../resources/styles"; +import { UNAVAILABLE } from "../../../data/entity"; export interface StateEntity extends EntityRegistryEntry { readonly?: boolean; @@ -281,7 +282,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { for (const entry of entities) { const entity = this.hass.states[entry.entity_id]; - const unavailable = entity?.state === "unavailable"; + const unavailable = entity?.state === UNAVAILABLE; const restored = entity?.attributes.restored; if (!showUnavailable && unavailable) { diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index bb65f89e1c..f05a1c4c1c 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -20,7 +20,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name"; import { stateIcon } from "../../../common/entity/state_icon"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-card"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { UNAVAILABLE_STATES, UNAVAILABLE } from "../../../data/entity"; import { SUPPORT_BRIGHTNESS } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant, LightEntity } from "../../../types"; @@ -133,7 +133,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { SUPPORT_BRIGHTNESS ), "state-on": stateObj.state === "on", - "state-unavailable": stateObj.state === "unavailable", + "state-unavailable": stateObj.state === UNAVAILABLE, })}" .icon=${this._config.icon || stateIcon(stateObj)} .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index e50707d871..0aca5f2e11 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -1,4 +1,5 @@ import { HomeAssistant } from "../../../types"; +import { UNAVAILABLE } from "../../../data/entity"; export interface Condition { entity: string; @@ -13,7 +14,7 @@ export function checkConditionsMet( return conditions.every((c) => { const state = hass.states[c.entity] ? hass!.states[c.entity].state - : "unavailable"; + : UNAVAILABLE; return c.state ? state === c.state : state !== c.state_not; }); diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 115e118f83..004bf705ff 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -17,6 +17,7 @@ import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import "../../../components/ha-camera-stream"; import { fetchThumbnailUrlWithCache } from "../../../data/camera"; import { CameraEntity, HomeAssistant } from "../../../types"; +import { UNAVAILABLE } from "../../../data/entity"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; @@ -73,7 +74,7 @@ export class HuiImage extends LitElement { } const ratio = this.aspectRatio ? parseAspectRatio(this.aspectRatio) : null; const stateObj = this.entity ? this.hass.states[this.entity] : undefined; - const state = stateObj ? stateObj.state : "unavailable"; + const state = stateObj ? stateObj.state : UNAVAILABLE; // Figure out image source to use let imageSrc: string | undefined; diff --git a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts index f878c33985..46e5a1e88c 100644 --- a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts @@ -11,7 +11,7 @@ import "../../../components/ha-date-input"; import type { HaDateInput } from "../../../components/ha-date-input"; import "../../../components/paper-time-input"; import type { PaperTimeInput } from "../../../components/paper-time-input"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { setInputDateTimeValue } from "../../../data/input_datetime"; import type { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -70,10 +70,10 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { ? html` ${stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - stateObj.state !== "unavailable" && - stateObj.state !== "unknown" + !UNAVAILABLE_STATES.includes(stateObj.state) ? html` { // Mock Localize function for testing @@ -72,7 +73,7 @@ describe("computeStateDisplay", () => { }; const stateObj: any = { entity_id: "sensor.test", - state: "unknown", + state: UNKNOWN, attributes: { unit_of_measurement: "m", }, From b0508f430ee026219f9e0cc4d0ee085cb5aa1ab4 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Fri, 11 Sep 2020 00:32:26 +0000 Subject: [PATCH 089/146] [ci skip] Translation update --- translations/frontend/ca.json | 4 +++ translations/frontend/cs.json | 4 +++ translations/frontend/en.json | 4 ++- translations/frontend/it.json | 4 +++ translations/frontend/nb.json | 10 ++++-- translations/frontend/nl.json | 57 +++++++++++++++++++++++++----- translations/frontend/ru.json | 3 ++ translations/frontend/zh-Hant.json | 4 +++ 8 files changed, 77 insertions(+), 13 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 61e6b7e55d..0e0d3b89ec 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -567,6 +567,9 @@ "loading_history": "Carregant historial d'estats...", "no_history_found": "No s'ha trobat cap historial d'estats." }, + "logbook": { + "entries_not_found": "No s'han trobat entrades al registre." + }, "media-browser": { "audio_not_supported": "El teu navegador no és compatible amb l'element d'àudio.", "choose_player": "Tria el reproductor", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Controls", + "details": "Detalls", "dismiss": "Desestimar el diàleg", "edit": "Edita entitat", "history": "Historial", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index dec6bdf715..17235e792e 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -567,6 +567,9 @@ "loading_history": "Historie stavu se načítá...", "no_history_found": "Historie stavu chybí." }, + "logbook": { + "entries_not_found": "Nenalezeny žádné záznamy." + }, "media-browser": { "audio_not_supported": "Váš prohlížeč nepodporuje element \"audio\".", "choose_player": "Vyberte přehrávač", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Ovládací prvky", + "details": "Podrobnosti", "dismiss": "Zavřít dialog", "edit": "Upravit entitu", "history": "Historie", diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 180e20eb4c..302930a05a 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2963,6 +2963,7 @@ "confirm_delete": "Are you sure you want to delete the access token for {name}?", "create": "Create Token", "create_failed": "Failed to create the access token.", + "created": "Created {date}", "created_at": "Created at {date}", "delete_failed": "Failed to delete the access token.", "description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.", @@ -2970,9 +2971,10 @@ "header": "Long-Lived Access Tokens", "last_used": "Last used at {date} from {location}", "learn_auth_requests": "Learn how to make authenticated requests.", + "name": "Name", "not_used": "Has never been used", "prompt_copy_token": "Copy your access token. It will not be shown again.", - "prompt_name": "Name?" + "prompt_name": "Give the token a name" }, "mfa_setup": { "close": "Close", diff --git a/translations/frontend/it.json b/translations/frontend/it.json index fa2f1fb908..c7976293f9 100644 --- a/translations/frontend/it.json +++ b/translations/frontend/it.json @@ -567,6 +567,9 @@ "loading_history": "Caricamento storico...", "no_history_found": "Nessuno storico trovato." }, + "logbook": { + "entries_not_found": "Non sono state trovate voci nel registro." + }, "media-browser": { "audio_not_supported": "Il tuo browser non supporta l'elemento audio.", "choose_player": "Scegli il lettore", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Controlli", + "details": "Dettagli", "dismiss": "Chiudi finestra di dialogo", "edit": "Modifica entità", "history": "Storico", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index c01cf5a5a5..ab4abc249b 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -567,6 +567,9 @@ "loading_history": "Laster statushistorikk...", "no_history_found": "Ingen statushistorikk funnet." }, + "logbook": { + "entries_not_found": "Ingen loggbokoppføringer funnet." + }, "media-browser": { "audio_not_supported": "Nettleseren din støtter ikke lydelementet.", "choose_player": "Velg spiller", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Kontroller", + "details": "Detaljer", "dismiss": "Avvis dialogboksen", "edit": "Redigér entitet", "history": "Historie", @@ -1788,7 +1792,7 @@ "nodeplusinfo": "Innhenting av Z-Wave + informasjon fra noden", "probe": "Kontrollere om noden er våken/levende", "protocolinfo": "Få grunnleggende Z-Wave-funksjoner i denne noden fra kontrolleren", - "session": "\nFå sjelden endrede verdier fra noden", + "session": "Henter verdier fra noden som sjeldent oppdateres", "static": "Innhenting av statiske verdier fra enheten", "versions": "Hente informasjon om fastvare- og kommandoklasseversjoner", "wakeup": "Sette opp støtte for vekkingskøer og meldinger" @@ -2463,7 +2467,7 @@ "name": "Entitetsfilter" }, "entity": { - "description": "Enhetskortet gir deg en rask oversikt over enhetens tilstand.", + "description": "Entitetskortet gir deg en rask oversikt over entitetens tilstand.", "name": "Entitet" }, "gauge": { @@ -2523,7 +2527,7 @@ "name": "Horisontal Stabel" }, "humidifier": { - "description": "Luftfukter kortet gir kontroll over luftfukter enheten din. Lar deg endre fuktigheten og modusen til enheten.", + "description": "Luftfukter kortet gir kontroll over luftfukter entiteten din. Lar deg endre fuktigheten og modusen til entiteten.", "name": "Luftfukter" }, "iframe": { diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 20dec55c5e..7e36879dae 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -19,6 +19,7 @@ "logbook": "Logboek", "mailbox": "Postvak", "map": "Kaart", + "media_browser": "Mediabrowser", "profile": "Profiel", "shopping_list": "Boodschappenlijst", "states": "Overzicht" @@ -552,6 +553,10 @@ "toggle": "Omschakelen" }, "entity": { + "entity-attribute-picker": { + "attribute": "Attribuut", + "show_attributes": "Toon attributen" + }, "entity-picker": { "clear": "Wis", "entity": "Entiteit", @@ -562,7 +567,11 @@ "loading_history": "Geschiedenis laden ...", "no_history_found": "Geen geschiedenis gevonden" }, + "logbook": { + "entries_not_found": "Geen logboekvermeldingen gevonden." + }, "media-browser": { + "audio_not_supported": "Uw browser ondersteunt het audio-element niet.", "choose_player": "Kies speler", "choose-source": "Kies bron", "content-type": { @@ -572,6 +581,7 @@ "playlist": "Afspeellijst", "server": "Server" }, + "media_browsing_error": "Fout bij bladeren door media", "media_not_supported": "De Browser Media Player ondersteunt dit type media niet", "media_player": "Mediaspeler", "media-player-browser": "Mediaspeler-browser", @@ -696,6 +706,7 @@ }, "more_info_control": { "controls": "Besturing", + "details": "Details", "dismiss": "Dialoogvenster sluiten", "edit": "Entiteit bewerken", "history": "Geschiedenis", @@ -927,7 +938,9 @@ "service_data": "Service data" }, "wait_for_trigger": { - "label": "Wacht op trigger" + "continue_timeout": "Ga verder op time-out", + "label": "Wacht op trigger", + "timeout": "Time-out (optioneel)" }, "wait_template": { "continue_timeout": "Ga verder op time-out", @@ -994,7 +1007,9 @@ "time": { "after": "Nadat", "before": "Voordat", - "label": "Tijd" + "label": "Tijd", + "type_input": "Waarde van een datum/tijdhelper", + "type_value": "Vaste tijd" }, "zone": { "entity": "Entiteit met locatie", @@ -1082,6 +1097,7 @@ "value_template": "Waardesjabloon (optioneel)" }, "state": { + "attribute": "Attribuut (Optioneel)", "for": "Voor", "from": "Van", "label": "Staat", @@ -1108,8 +1124,10 @@ "seconds": "Seconden" }, "time": { - "at": "Om", - "label": "Tijd" + "at": "Op tijd", + "label": "Tijd", + "type_input": "Waarde van een datum/tijdhelper", + "type_value": "Vaste tijd" }, "webhook": { "label": "Webhook", @@ -1132,6 +1150,8 @@ "add_automation": "Automatisering toevoegen", "delete_automation": "Verwijder automatisering", "delete_confirm": "Weet je zeker dat je deze automatisering wilt verwijderen?", + "duplicate": "Dupliceren", + "duplicate_automation": "Dupliceer automatisering", "edit_automation": "Bewerk automatisering", "header": "Automatiseringsbewerker", "headers": { @@ -1216,9 +1236,11 @@ "dont_expose_entity": "Entiteit niet beschikbaar maken", "expose": "Blootstellen aan Alexa", "expose_entity": "Enititeit beschikbaar maken", + "exposed": "{selected} blootgesteld", "exposed_entities": "Blootgestelde entiteiten", "follow_domain": "Domein volgen", "manage_domains": "Beheer domeinen", + "not_exposed": "{selected} niet blootgesteld", "not_exposed_entities": "Niet blootgestelde entiteiten", "title": "Alexa" }, @@ -1259,8 +1281,11 @@ "dont_expose_entity": "Entiteit niet beschikbaar maken", "expose": "Blootstellen aan Google Assistant", "expose_entity": "Enititeit beschikbaar maken", + "exposed": "{selected} blootgesteld", "exposed_entities": "Blootgestelde entiteiten", + "follow_domain": "Domein volgen", "manage_domains": "Beheer domeinen", + "not_exposed": "{selected} niet blootgesteld", "not_exposed_entities": "Niet blootgestelde entiteiten", "sync_to_google": "Wijzigingen synchroniseren met Google.", "title": "Google Assistant" @@ -1551,6 +1576,7 @@ "reload_restart_confirm": "Start Home Assistant opnieuw om het opnieuw laden van deze integratie te voltooien", "rename": "Naam wijzigen", "restart_confirm": "Herstart Home Assistant om het verwijderen van deze integratie te voltooien", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Instellingen bewerken voor {integration}", "system_options": "Systeeminstellingen", "system_options_button": "Systeeminstellingen voor {integration}", @@ -1771,11 +1797,21 @@ "versions": "Het verkrijgen van informatie over firmware en commandoklasseversies", "wakeup": "Ondersteuning instellen voor waakwachtrijen en berichten" }, + "node": { + "button": "Node details", + "not_found": "Node niet gevonden" + }, "nodes_table": { - "failed": "Mislukt" + "failed": "Mislukt", + "id": "ID", + "manufacturer": "Fabrikant", + "model": "Model", + "query_stage": "Vraagfase", + "zwave_plus": "Z-Wave Plus" }, "refresh_node": { "battery_note": "Als het knooppunt op batterijen werkt, moet u het uit de sluimerstand halen voordat u verder gaat", + "button": "Node verversen", "complete": "Knooppunt vernieuwen voltooid", "description": "Dit zal OpenZWave vertellen om een knooppunt opnieuw te ondervragen en de opdrachtklassen, mogelijkheden en waarden van het knooppunt bij te werken.", "node_status": "Knooppuntstatus", @@ -1924,7 +1960,7 @@ "filter": "Herlaad filter-entiteiten", "generic": "Herlaad generieke IP camera entiteiten", "generic_thermostat": "Herlaad generieke thermostaat entiteiten", - "group": "Herlaad groepen", + "group": "Herlaad groepen, groepsentiteiten en meld services", "heading": "Configuratie herladen", "history_stats": "Herlaad historische statistieken entiteiten", "homekit": "Herlaad HomeKit", @@ -1938,7 +1974,8 @@ "mqtt": "Herlaad mqtt entiteiten", "person": "Herlaad personen", "ping": "Herlaad ping binaire sensor entiteiten", - "rest": "Herlaad rust-entiteiten", + "reload": "Herlaad {domain}", + "rest": "Laad resterende entiteiten opnieuw en stel services op de hoogte", "rpi_gpio": "Herlaad Raspberry Pi GPIO-entiteiten", "scene": "Herlaad scenes", "script": "Herlaad scripts", @@ -2033,7 +2070,7 @@ "system": "Systeem" } }, - "users_privileges_note": "Gebruikersgroepen zijn nog in ontwikkeling. De gebruiker kan de instantie niet beheren via de interface. We zijn bezig met het controleren van de API-eindpunten toegang voor beheerders." + "users_privileges_note": "Gebruikersgroepen zijn nog in ontwikkeling. De gebruiker kan de instantie niet beheren via de interface. We controleren nog steeds alle beheer API eindpunten om ervoor te zorgen dat ze de toegang tot beheerders correct beperken." }, "zha": { "add_device_page": { @@ -2217,7 +2254,7 @@ "node_to_control": "Knooppunt om te besturen", "nodes": "Knooppunten", "nodes_hint": "Selecteer knooppunt om de opties per knooppunt te bekijken", - "nodes_in_group": "Andere knooppunten in deze groep:", + "nodes_in_group": "Andere nodes in deze groep:", "pooling_intensity": "Polling intensiteit", "protection": "Bescherming", "remove_broadcast": "Broadcast verwijderen", @@ -2565,6 +2602,8 @@ } }, "cardpicker": { + "by_card": "Per kaart", + "by_entity": "Per entiteit", "custom_card": "Aangepaste", "domain": "Domein", "entity": "Entiteit", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 0ff93c88b1..7122ac3008 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -567,6 +567,9 @@ "loading_history": "Загрузка истории...", "no_history_found": "История не найдена." }, + "logbook": { + "entries_not_found": "В журнале нет записей." + }, "media-browser": { "audio_not_supported": "Ваш браузер не поддерживает аудио.", "choose_player": "Выберите медиаплеер", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 67203a2c85..0fcc924359 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -567,6 +567,9 @@ "loading_history": "正在載入狀態歷史...", "no_history_found": "找不到狀態歷史。" }, + "logbook": { + "entries_not_found": "找不到實體日誌。" + }, "media-browser": { "audio_not_supported": "瀏覽器不支援音效元件。", "choose_player": "選擇播放器", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "控制", + "details": "詳細資訊", "dismiss": "忽略對話", "edit": "編輯實體", "history": "歷史", From ce8ee569c4ab304d3d42779abd1aa05f51e05fba Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 10:17:05 +0200 Subject: [PATCH 090/146] Check if history and logbook are loaded (#6908) --- src/dialogs/more-info/ha-more-info-dialog.ts | 3 ++- src/dialogs/more-info/ha-more-info-history.ts | 27 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 15d71e694f..f5beadea90 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -223,7 +223,8 @@ export class MoreInfoDialog extends LitElement { private _computeShowHistoryComponent(entityId) { return ( - isComponentLoaded(this.hass, "history") && + (isComponentLoaded(this.hass, "history") || + isComponentLoaded(this.hass, "logbook")) && !DOMAINS_MORE_INFO_NO_HISTORY.includes(computeDomain(entityId)) ); } diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index c75181a1b4..f436a9ddab 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -8,6 +8,7 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import "../../components/ha-circular-progress"; import "../../components/state-history-charts"; @@ -42,13 +43,16 @@ export class MoreInfoHistory extends LitElement { return html``; } - return html` - ${!this._entries + return html`${isComponentLoaded(this.hass, "history") + ? html`` + : ""} + ${isComponentLoaded(this.hass, "logbook") + ? !this._entries ? html` ${this.hass.localize("ui.components.logbook.entries_not_found")} -
    `}`; +
    ` + : ""} `; } protected firstUpdated(): void { @@ -97,6 +102,9 @@ export class MoreInfoHistory extends LitElement { } private async _getStateHistory(): Promise { + if (!isComponentLoaded(this.hass, "history")) { + return; + } this._stateHistory = await getRecentWithCache( this.hass!, this.entityId, @@ -111,6 +119,9 @@ export class MoreInfoHistory extends LitElement { } private async _getLogBookData() { + if (!isComponentLoaded(this.hass, "logbook")) { + return; + } const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); const now = new Date(); this._entries = await getLogbookData( From 5af4ce28abbabc7de4cf2a0ca8dce6883d136f2c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 14:42:39 +0200 Subject: [PATCH 091/146] Restrict long press to header of sidebar (#6933) --- src/components/ha-sidebar.ts | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 41d460fc9f..07508cfe3e 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -30,7 +30,6 @@ import memoizeOne from "memoize-one"; import { LocalStorage } from "../common/decorators/local-storage"; import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; -import { navigate } from "../common/navigate"; import { compare } from "../common/string/compare"; import { computeRTL } from "../common/util/compute_rtl"; import { ActionHandlerDetail } from "../data/lovelace"; @@ -235,7 +234,14 @@ class HaSidebar extends LitElement { ` : ""} - `; @@ -189,6 +240,11 @@ ${this._processed} { - this._processed = result; + this._templateResult = result; + this._error = undefined; }, { template: this._template, @@ -230,9 +287,10 @@ ${this._processed}; @@ -85,7 +88,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { class=${classMap({ "no-header": !this._config.title, })} - .content="${this._content}" + .content="${this._templateResult?.result}" > `; @@ -127,7 +130,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { this._unsubRenderTemplate = subscribeRenderTemplate( this.hass.connection, (result) => { - this._content = result; + this._templateResult = result; }, { template: this._config.content, @@ -139,7 +142,10 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { } ); } catch (_err) { - this._content = this._config!.content; + this._templateResult = { + result: this._config!.content, + listeners: { all: false, domains: [], entities: [] }, + }; this._unsubRenderTemplate = undefined; } } diff --git a/src/translations/en.json b/src/translations/en.json index 29cc400560..02cd088e92 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2729,7 +2729,12 @@ "reset": "Reset to demo template", "jinja_documentation": "Jinja2 template documentation", "template_extensions": "Home Assistant template extensions", - "unknown_error_template": "Unknown error rendering template" + "unknown_error_template": "Unknown error rendering template", + "all_listeners": "This template listens for all state changed events.", + "no_listeners": "This template does not listen for any state changed events and will not update automatically.", + "listeners": "This template listens for the following state changed events:", + "entity": "Entity", + "domain": "Domain" } } }, From dd1bf7b49d2ba6b9aa2635b6070063168e1cd009 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Fri, 11 Sep 2020 15:10:36 -0500 Subject: [PATCH 098/146] show first visible view on default (#6567) Co-authored-by: Bram Kragten --- src/panels/lovelace/hui-root.ts | 44 ++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index a720fde6bb..56815d7131 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -1,12 +1,13 @@ import "@material/mwc-button"; import "@material/mwc-list/mwc-list-item"; +import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; import { - mdiDotsVertical, - mdiMicrophone, - mdiPlus, mdiClose, - mdiPencil, + mdiDotsVertical, mdiHelpCircle, + mdiMicrophone, + mdiPencil, + mdiPlus, } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-scroll-effects/effects/waterfall"; @@ -17,9 +18,9 @@ import { css, CSSResult, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -28,16 +29,17 @@ import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; import scrollToTarget from "../../common/dom/scroll-to-target"; +import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; import { navigate } from "../../common/navigate"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; import { afterNextRender } from "../../common/util/render-status"; import "../../components/ha-button-menu"; import "../../components/ha-icon"; -import "../../components/ha-svg-icon"; import "../../components/ha-icon-button-arrow-next"; import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; +import "../../components/ha-svg-icon"; import type { LovelaceConfig, LovelacePanelConfig, @@ -58,8 +60,6 @@ import type { Lovelace } from "./types"; import "./views/hui-panel-view"; import type { HUIPanelView } from "./views/hui-panel-view"; import { HUIView } from "./views/hui-view"; -import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; -import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; class HUIRoot extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -382,6 +382,15 @@ class HUIRoot extends LitElement { `; } + private _isVisible = (view: LovelaceViewConfig) => + Boolean( + this._editMode || + view.visible === undefined || + view.visible === true || + (Array.isArray(view.visible) && + view.visible.some((show) => show.user === this.hass!.user?.id)) + ); + protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); @@ -407,9 +416,14 @@ class HUIRoot extends LitElement { if (changedProperties.has("route")) { const views = this.config.views; + if (!viewPath && views.length) { - navigate(this, `${this.route!.prefix}/${views[0].path || 0}`, true); - newSelectView = 0; + newSelectView = views.findIndex(this._isVisible); + navigate( + this, + `${this.route!.prefix}/${views[newSelectView].path || newSelectView}`, + true + ); } else if (viewPath === "hass-unused-entities") { newSelectView = "hass-unused-entities"; } else if (viewPath) { @@ -449,8 +463,14 @@ class HUIRoot extends LitElement { this.lovelace!.mode === "storage" && viewPath === "hass-unused-entities" ) { - navigate(this, `${this.route?.prefix}/${views[0]?.path || 0}`); - newSelectView = 0; + newSelectView = views.findIndex(this._isVisible); + navigate( + this, + `${this.route!.prefix}/${ + views[newSelectView].path || newSelectView + }`, + true + ); } } From da1de8db1d66f08a2acc57cb3b48171f707472bb Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 22:13:57 +0200 Subject: [PATCH 099/146] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..2dac784cb3 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,60 @@ +name: "CodeQL" + +on: + push: + branches: [dev, master] + pull_request: + # The branches below must be a subset of the branches above + branches: [dev] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['javascript', 'python'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 3fdd6a80f90d7a339f90bec9b1e6a3e2e8d8cfa9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 22:15:08 +0200 Subject: [PATCH 100/146] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2dac784cb3..f03f0dcbd0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -17,7 +17,7 @@ jobs: matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['javascript', 'python'] + language: ['javascript'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection From f01fe65be40aca9f372ecc224627aca9052e6f83 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 22:42:11 +0200 Subject: [PATCH 101/146] Show title and name for default panels (#6941) Fixes #6927 --- src/components/ha-sidebar.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 07508cfe3e..73e121c4f9 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -651,11 +651,13 @@ class HaSidebar extends LitElement { return panels.map((panel) => this._renderPanel( panel.url_path, - panel.url_path === "lovelace" - ? this.hass.localize("panel.states") + panel.url_path === this.hass.defaultPanel + ? panel.title || this.hass.localize("panel.states") : this.hass.localize(`panel.${panel.title}`) || panel.title, - panel.url_path === "lovelace" ? undefined : panel.icon, - panel.url_path === "lovelace" ? mdiViewDashboard : undefined + panel.icon, + panel.url_path === this.hass.defaultPanel && !panel.icon + ? mdiViewDashboard + : undefined ) ); } From 26fbc07cacf0a37719119df0f6567e6f48eaaced Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 00:04:25 +0200 Subject: [PATCH 102/146] Add edit sidebar button to profile (#6943) --- src/components/ha-settings-row.ts | 5 ++++- src/components/ha-sidebar.ts | 20 ++++++++++++++------ src/layouts/home-assistant-main.ts | 12 ++++++++++++ src/panels/profile/ha-panel-profile.ts | 21 +++++++++++++++++++++ src/state/sidebar-mixin.ts | 6 ++---- src/translations/en.json | 8 +++++++- 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/components/ha-settings-row.ts b/src/components/ha-settings-row.ts index 0d53c58d29..4fdae88f3e 100644 --- a/src/components/ha-settings-row.ts +++ b/src/components/ha-settings-row.ts @@ -1,3 +1,4 @@ +import "@polymer/paper-item/paper-item-body"; import { css, CSSResult, @@ -7,7 +8,6 @@ import { property, SVGTemplateResult, } from "lit-element"; -import "@polymer/paper-item/paper-item-body"; @customElement("ha-settings-row") export class HaSettingsRow extends LitElement { @@ -49,6 +49,9 @@ export class HaSettingsRow extends LitElement { border-top: 1px solid var(--divider-color); padding-bottom: 8px; } + ::slotted(ha-switch) { + padding: 16px 0; + } `; } } diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 73e121c4f9..49daf43104 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -165,7 +165,7 @@ let sortStyles: CSSResult; class HaSidebar extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public narrow!: boolean; + @property({ type: Boolean, reflect: true }) public narrow!: boolean; @property({ type: Boolean }) public alwaysExpand = false; @@ -259,7 +259,7 @@ class HaSidebar extends LitElement {
    ${this._editMode ? html` - DONE + ${hass.localize("ui.sidebar.done")} ` : "Home Assistant"}
    @@ -447,6 +447,9 @@ class HaSidebar extends LitElement { subscribeNotifications(this.hass.connection, (notifications) => { this._notifications = notifications; }); + window.addEventListener("hass-edit-sidebar", () => + this._activateEditMode() + ); } protected updated(changedProps) { @@ -479,11 +482,15 @@ class HaSidebar extends LitElement { return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement; } - private async _handleAction(ev: CustomEvent) { + private _handleAction(ev: CustomEvent) { if (ev.detail.action !== "hold") { return; } + this._activateEditMode(); + } + + private async _activateEditMode() { if (!Sortable) { const [sortableImport, sortStylesImport] = await Promise.all([ import("sortablejs/modular/sortable.core.esm"), @@ -498,9 +505,7 @@ class HaSidebar extends LitElement { } this._editMode = true; - if (!this.expanded) { - fireEvent(this, "hass-toggle-menu"); - } + fireEvent(this, "hass-open-menu"); await this.updateComplete; @@ -761,6 +766,9 @@ class HaSidebar extends LitElement { width: 100%; display: none; } + :host([narrow]) .title { + padding: 0 16px; + } :host([expanded]) .title { display: initial; } diff --git a/src/layouts/home-assistant-main.ts b/src/layouts/home-assistant-main.ts index 31609dbdc5..998fb04a3d 100644 --- a/src/layouts/home-assistant-main.ts +++ b/src/layouts/home-assistant-main.ts @@ -24,6 +24,7 @@ const NON_SWIPABLE_PANELS = ["map"]; declare global { // for fire event interface HASSDomEvents { + "hass-open-menu": undefined; "hass-toggle-menu": undefined; "hass-show-notifications": undefined; } @@ -92,6 +93,17 @@ class HomeAssistantMain extends LitElement { protected firstUpdated() { import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar"); + this.addEventListener("hass-open-menu", () => { + if (this._sidebarNarrow) { + this.drawer.open(); + } else { + fireEvent(this, "hass-dock-sidebar", { + dock: "docked", + }); + setTimeout(() => this.appLayout.resetLayout()); + } + }); + this.addEventListener("hass-toggle-menu", () => { if (this._sidebarNarrow) { if (this.drawer.opened) { diff --git a/src/panels/profile/ha-panel-profile.ts b/src/panels/profile/ha-panel-profile.ts index b4aa386092..458148335c 100644 --- a/src/panels/profile/ha-panel-profile.ts +++ b/src/panels/profile/ha-panel-profile.ts @@ -107,6 +107,23 @@ class HaPanelProfile extends LitElement { .narrow=${this.narrow} .hass=${this.hass} > + + + ${this.hass.localize( + "ui.panel.profile.customize_sidebar.header" + )} + + + ${this.hass.localize( + "ui.panel.profile.customize_sidebar.description" + )} + + + ${this.hass.localize( + "ui.panel.profile.customize_sidebar.button" + )} + + ${this.hass.dockedSidebar !== "auto" || !this.narrow ? html` ; - } - interface HTMLElementEventMap { "hass-default-panel": HASSDomEvent; + "hass-edit-sidebar": undefined; } } diff --git a/src/translations/en.json b/src/translations/en.json index 02cd088e92..e7d2ea366e 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -585,7 +585,8 @@ }, "sidebar": { "external_app_configuration": "App Configuration", - "sidebar_toggle": "Sidebar Toggle" + "sidebar_toggle": "Sidebar Toggle", + "done": "Done" }, "panel": { "calendar": { @@ -2431,6 +2432,11 @@ "header": "Always hide the sidebar", "description": "This will hide the sidebar by default, similar to the mobile experience." }, + "customize_sidebar": { + "header": "Change the order and hide items from the sidebar", + "description": "You can also press and hold the header of the sidebar to activate edit mode.", + "button": "Edit" + }, "vibrate": { "header": "Vibrate", "description": "Enable or disable vibration on this device when controlling devices." From 8ee29b1e43bd29a88392ce11edbb13b50e0d41d3 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Sat, 12 Sep 2020 00:32:19 +0000 Subject: [PATCH 103/146] [ci skip] Translation update --- translations/frontend/ar.json | 22 ----- translations/frontend/bs.json | 51 +----------- translations/frontend/ca.json | 4 +- translations/frontend/cs.json | 12 +-- translations/frontend/cy.json | 13 +-- translations/frontend/en.json | 11 +++ translations/frontend/eo.json | 52 +----------- translations/frontend/fa.json | 3 - translations/frontend/gsw.json | 46 +---------- translations/frontend/hi.json | 60 +------------- translations/frontend/hu.json | 82 ++++++++++++++----- translations/frontend/ja.json | 3 - translations/frontend/lt.json | 18 +---- translations/frontend/pl.json | 8 ++ translations/frontend/ru.json | 5 +- translations/frontend/sr-Latn.json | 124 ----------------------------- translations/frontend/sr.json | 120 +--------------------------- translations/frontend/ta.json | 49 +----------- translations/frontend/te.json | 25 +----- translations/frontend/tr.json | 6 +- translations/frontend/ur.json | 52 ------------ translations/frontend/zh-Hans.json | 8 +- 22 files changed, 115 insertions(+), 659 deletions(-) diff --git a/translations/frontend/ar.json b/translations/frontend/ar.json index bb67ab7519..15f0aca0ea 100644 --- a/translations/frontend/ar.json +++ b/translations/frontend/ar.json @@ -1469,34 +1469,12 @@ "next": "التالى", "providers": { "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "" - }, "step": { "init": { "data": { "password": "كلمة السر", "username": "اسم المستخدم" } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" } } }, diff --git a/translations/frontend/bs.json b/translations/frontend/bs.json index e85972507c..f3a2c84456 100644 --- a/translations/frontend/bs.json +++ b/translations/frontend/bs.json @@ -36,8 +36,7 @@ "unknown": "Nepoznat" }, "device_tracker": { - "home": "Kod kuće", - "not_home": "" + "home": "Kod kuće" }, "person": { "home": "Kod kuće" @@ -94,7 +93,6 @@ "on": "Otvoren" }, "presence": { - "off": "", "on": "Kod kuće" }, "problem": { @@ -161,7 +159,6 @@ "closing": "Zatvoreno", "home": "Kod kuće", "locked": "Zaključan", - "not_home": "", "off": "Isključen", "ok": "OK", "on": "Uključen", @@ -245,13 +242,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "type": { "mqtt": { @@ -303,45 +293,6 @@ "empty": "Nemate poruke", "playback_title": "Poruku preslušati" }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } - }, "shopping-list": { "add_item": "Dodajte objekat", "clear_completed": "Čišćenje završeno", diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 0e0d3b89ec..54945cf396 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -2963,6 +2963,7 @@ "confirm_delete": "Estàs segur que vols eliminar el token d'autenticació d'accés per {name}?", "create": "Crea un token d'autenticació", "create_failed": "No s'ha pogut crear el token d'autenticació d'accés.", + "created": "Creat el {date}", "created_at": "Creat el {date}", "delete_failed": "No s'ha pogut eliminar el token d'autenticació d'accés.", "description": "Crea tokens d'autenticació d'accés de llarga durada per permetre als teus programes/scripts interactuar amb la instància de Home Assistant. Cada token és vàlid durant deu anys després de la seva creació. Els següents tokens d'autenticació d'accés de llarga durada estan actius actualment.", @@ -2970,9 +2971,10 @@ "header": "Tokens d'autenticació d'accés de llarga durada", "last_used": "Darrer ús el {date} des de {location}", "learn_auth_requests": "Aprèn a fer sol·licituds autenticades.", + "name": "Nom", "not_used": "Mai no s'ha utilitzat", "prompt_copy_token": "Copia't el token d'autenticació d'accés. No es tornarà a mostrar més endavant.", - "prompt_name": "Nom?" + "prompt_name": "Posa un nom al token" }, "mfa_setup": { "close": "Tanca", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 17235e792e..8107413439 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -793,7 +793,7 @@ "services": { "reconfigure": "Překonfigurovat zařízení ZHA (opravit zařízení). Použijte, pokud se zařízením máte problémy. Je-li dotyčné zařízení napájené bateriemi, ujistěte se prosím, že je při používání této služby spuštěné a přijímá příkazy.", "remove": "Odebrat zařízení ze sítě Zigbee.", - "updateDeviceName": "Nastavte vlastní název tohoto zařízení v registru zařízení.", + "updateDeviceName": "Nastavte vlastní název tohoto zařízení v seznamu zařízení.", "zigbee_information": "Zobrazit Zigbee informace zařízení." }, "unknown": "Neznámý", @@ -1474,8 +1474,8 @@ "name": "Název", "status": "Stav" }, - "introduction": "Homa Assistant uchovává registr všech entit, které kdy viděl a mohou být jednoznačně identifikovány. Každá z těchto entit bude mít přiděleno ID, které bude rezervováno pouze pro tuto entitu.", - "introduction2": "Pomocí registru entit můžete přepsat název, změnit identifikátor entity nebo odebrat entitu.", + "introduction": "Homa Assistant uchovává záznam o každé entitě, kterou kdy viděl a která může být jednoznačně identifikována. Každá z těchto entit bude mít přiděleno ID, které bude rezervováno pouze pro tuto entitu.", + "introduction2": "Entitě můžete přepsat název, změnit její identifikátor nebo ji odebrat z Home Assistant.", "remove_selected": { "button": "Odstranit vybrané", "confirm_partly_text": "Můžete odebrat pouze {removable} z vybraných {selected} entit. Entity lze odebrat pouze v případě, že integrace již entity neposkytuje. Občas je třeba restartovat Home Assistant před tím, než je možné odstranit entity ze smazané integrace. Opravdu chcete odebrat odstranitelné entity?", @@ -1950,7 +1950,7 @@ }, "server_control": { "caption": "Ovládání serveru", - "description": "Restart a zastavení serveru Home Asistent", + "description": "Restartování a zastavení serveru Home Asistent", "section": { "reloading": { "automation": "Nově načíst automatizace", @@ -2963,6 +2963,7 @@ "confirm_delete": "Opravdu chcete smazat přístupový token pro {name} ?", "create": "Vytvořte token", "create_failed": "Nepodařilo se vytvořit přístupový token.", + "created": "Vytvořeno {date}", "created_at": "Vytvořeno {date}", "delete_failed": "Nepodařilo se odstranit přístupový token.", "description": "Vytvořte přístupové tokeny s dlouhou životností, aby vaše skripty mohly komunikovat s instancí Home Assistant. Každý token bude platný po dobu 10 let od vytvoření. Tyto tokeny s dlouhou životností jsou v současné době aktivní.", @@ -2970,9 +2971,10 @@ "header": "Tokeny s dlouhou životností", "last_used": "Naposledy použito {date} z {location}", "learn_auth_requests": "Naučte se, jak posílat ověřené požadavky.", + "name": "Název", "not_used": "Nikdy nebylo použito", "prompt_copy_token": "Zkopírujte přístupový token. Už nikdy nebude znovu zobrazen.", - "prompt_name": "Název?" + "prompt_name": "Pojmenujte token" }, "mfa_setup": { "close": "Zavřít", diff --git a/translations/frontend/cy.json b/translations/frontend/cy.json index abd54bda52..15f461bf91 100644 --- a/translations/frontend/cy.json +++ b/translations/frontend/cy.json @@ -1283,18 +1283,7 @@ "mfa": { "data": { "code": "Cod dilysu dwy-ffactor" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" + } } } } diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 302930a05a..fcc53ba406 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2352,9 +2352,14 @@ "title": "States" }, "templates": { + "all_listeners": "This template listens for all state changed events.", "description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.", + "domain": "Domain", "editor": "Template editor", + "entity": "Entity", "jinja_documentation": "Jinja2 template documentation", + "listeners": "This template listens for the following state changed events:", + "no_listeners": "This template does not listen for any state changed events and will not update automatically.", "reset": "Reset to demo template", "template_extensions": "Home Assistant template extensions", "title": "Template", @@ -2941,6 +2946,11 @@ "submit": "Submit" }, "current_user": "You are currently logged in as {fullName}.", + "customize_sidebar": { + "button": "Edit", + "description": "You can also press and hold the header of the sidebar to activate edit mode.", + "header": "Change the order and hide items from the sidebar" + }, "dashboard": { "description": "Pick a default dashboard for this device.", "dropdown_label": "Dashboard", @@ -3038,6 +3048,7 @@ } }, "sidebar": { + "done": "Done", "external_app_configuration": "App Configuration", "sidebar_toggle": "Sidebar Toggle" } diff --git a/translations/frontend/eo.json b/translations/frontend/eo.json index 8b05489328..23fc03e9a9 100644 --- a/translations/frontend/eo.json +++ b/translations/frontend/eo.json @@ -1,49 +1,4 @@ { - "state_badge": { - "device_tracker": { - "home": "" - } - }, - "state": { - "automation": { - "off": "" - }, - "binary_sensor": { - "default": { - "on": "" - }, - "presence": { - "on": "" - } - }, - "calendar": { - "on": "" - }, - "group": { - "home": "", - "off": "", - "on": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "" - }, - "script": { - "off": "" - }, - "sensor": { - "off": "" - }, - "switch": { - "on": "" - } - }, "ui": { "card": { "climate": { @@ -124,12 +79,7 @@ "edit_ui": "Redakti kun UI", "edit_yaml": "Redakti kiel YAML", "triggers": { - "name": "Ellasilo", - "type": { - "mqtt": { - "label": "" - } - } + "name": "Ellasilo" } }, "picker": { diff --git a/translations/frontend/fa.json b/translations/frontend/fa.json index 2fc44ddc5d..28992b49cb 100644 --- a/translations/frontend/fa.json +++ b/translations/frontend/fa.json @@ -1689,9 +1689,6 @@ }, "step": { "mfa": { - "data": { - "code": "" - }, "description": "باز کردن **{mfa_module_name}** * * * در دستگاه خود را برای مشاهده شما دو فاکتور تأیید هویت کد و هویت خود را تایید کنید:" } } diff --git a/translations/frontend/gsw.json b/translations/frontend/gsw.json index 2e0d098575..b4e756236a 100644 --- a/translations/frontend/gsw.json +++ b/translations/frontend/gsw.json @@ -37,8 +37,7 @@ "unknown": "Unbekannt" }, "device_tracker": { - "home": "Dahei", - "not_home": "" + "home": "Dahei" }, "person": { "home": "Dahei" @@ -97,7 +96,6 @@ "on": "Offä" }, "presence": { - "off": "", "on": "Dahei" }, "problem": { @@ -419,9 +417,6 @@ "time": { "after": "Nachhär", "before": "Vorhär" - }, - "zone": { - "entity": "" } } }, @@ -515,45 +510,6 @@ "mailbox": { "delete_button": "Lösche" }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } - }, "profile": { "change_password": { "confirm_new_password": "Nöis Passwort bestätige", diff --git a/translations/frontend/hi.json b/translations/frontend/hi.json index 0f06beb774..f71427a683 100644 --- a/translations/frontend/hi.json +++ b/translations/frontend/hi.json @@ -19,9 +19,6 @@ }, "state_badge": { "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", - "armed_night": "", "pending": "अपूर्ण" }, "default": { @@ -31,8 +28,7 @@ "unknown": "अज्ञात" }, "device_tracker": { - "home": "घर", - "not_home": "" + "home": "घर" }, "person": { "home": "घर" @@ -79,27 +75,16 @@ "off": "विशद", "on": "अनुसन्धानित" }, - "occupancy": { - "off": "", - "on": "" - }, "opening": { "on": "खुला" }, "presence": { - "off": "", "on": "घर" }, "safety": { "off": "सुरक्षित", "on": "असुरक्षित" }, - "smoke": { - "off": "" - }, - "vibration": { - "on": "" - }, "window": { "off": "बंद", "on": "खुली" @@ -130,15 +115,10 @@ "on": "चालू" }, "group": { - "closing": "", "home": "घर", - "not_home": "", "off": "बंद", - "ok": "", "on": "चालू", - "open": "", - "problem": "समस्या", - "stopped": "" + "problem": "समस्या" }, "input_boolean": { "off": "बंद", @@ -254,9 +234,6 @@ "time": { "after": "बाद", "before": "पहले" - }, - "zone": { - "entity": "" } } }, @@ -428,39 +405,6 @@ "form": { "next": "अगला", "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, "trusted_networks": { "step": { "init": { diff --git a/translations/frontend/hu.json b/translations/frontend/hu.json index 6f4ed3ee3c..0b50127989 100644 --- a/translations/frontend/hu.json +++ b/translations/frontend/hu.json @@ -419,6 +419,8 @@ "unlock": "Kinyit" }, "media_player": { + "media_next_track": "Következő", + "media_previous_track": "Előző", "sound_mode": "Hangzás", "source": "Forrás", "text_to_speak": "Beszéd szövege" @@ -554,6 +556,20 @@ "loading_history": "Állapot előzmények betöltése...", "no_history_found": "Nem található előzmény." }, + "media-browser": { + "choose-source": "Forrás kiválasztása", + "content-type": { + "album": "Album", + "artist": "Előadó", + "library": "Könyvtár", + "playlist": "Lejátszási lista", + "server": "Szerver" + }, + "no_items": "Nincsenek elemek", + "pick-media": "Média kiválasztása", + "play": "Lejátszás", + "play-media": "Média lejátszása" + }, "related-items": { "area": "Terület", "automation": "A következő automatizálások része", @@ -574,6 +590,7 @@ "week": "{count} {count, plural,\n one {héttel}\n other {héttel}\n}" }, "future": "{time} később", + "just_now": "Éppen most", "never": "Soha", "past": "{time} ezelőtt" }, @@ -656,6 +673,9 @@ "required_error_msg": "Ez a mező kötelező", "yaml_not_editable": "Ennek az entitásnak a beállításai nem szerkeszthetők a felhasználói felületről. Csak a felhasználói felületről beállított entitások konfigurálhatók a felhasználói felületről." }, + "image_cropper": { + "crop": "Kivágás" + }, "more_info_control": { "dismiss": "Párbeszédpanel elvetése", "edit": "Entitás szerkesztése", @@ -798,7 +818,7 @@ "confirmation_text": "Minden ebben a területben lévő eszköz hozzárendelés nélküli lesz.", "confirmation_title": "Biztosan törölni szeretnéd ezt a területet?" }, - "description": "Az összes otthoni terület áttekintése", + "description": "Otthoni területek kezelése", "editor": { "area_id": "Terület ID", "create": "Létrehozás", @@ -820,7 +840,7 @@ }, "automation": { "caption": "Automatizálások", - "description": "Automatizálások létrehozása és szerkesztése", + "description": "Automatizálások kezelése", "editor": { "actions": { "add": "Művelet hozzáadása", @@ -852,6 +872,15 @@ "label": "Esemény meghívása", "service_data": "Szolgáltatás adatai" }, + "repeat": { + "label": "Ismétlés", + "type_select": "Ismétlés típusa", + "type": { + "count": { + "label": "Számláló" + } + } + }, "scene": { "label": "Jelenet aktiválása" }, @@ -865,7 +894,7 @@ "wait_template": "Várakozási sablon" } }, - "unsupported_action": "Nem támogatott művelet: {action}" + "unsupported_action": "Nincs felhasználói felület támogatás a művelethez: {action}" }, "alias": "Név", "conditions": { @@ -931,7 +960,7 @@ "zone": "Zóna" } }, - "unsupported_condition": "Nem támogatott feltétel: {condition}" + "unsupported_condition": "Nincs felhasználói felület támogatás a feltételhez: {condition}" }, "default_name": "Új Automatizálás", "description": { @@ -1049,7 +1078,7 @@ "zone": "Zóna" } }, - "unsupported_platform": "Nem támogatott platform: {platform}" + "unsupported_platform": "Nincs felhasználói felület támogatás a platformhoz: {platform}" }, "unsaved_confirm": "Vannak nem mentett módosítások. Biztos, hogy elhagyod az oldalt?" }, @@ -1138,9 +1167,11 @@ }, "alexa": { "banner": "A feltárt entitások szerkesztése funkció le lett tiltva ezen a felületen, mert entitásszűrőket állítottál be a configuration.yaml fájlban.", + "dont_expose_entity": "Entitás feltárásának törlése", "expose": "Feltárás Alexának", + "expose_entity": "Entitás feltárása", "exposed_entities": "Feltárt entitások", - "not_exposed_entities": "Nem feltárt entitások", + "not_exposed_entities": "Feltáratlan entitások", "title": "Alexa" }, "caption": "Home Assistant Felhő", @@ -1177,9 +1208,11 @@ "google": { "banner": "A feltárt entitások szerkesztése funkció le lett tiltva ezen a felületen, mert entitásszűrőket állítottál be a configuration.yaml fájlban.", "disable_2FA": "Kétfaktoros hitelesítés letiltása", + "dont_expose_entity": "Entitás feltárásának törlése", "expose": "Feltárás a Google Asszisztensnek", + "expose_entity": "Entitás feltárása", "exposed_entities": "Feltárt entitások", - "not_exposed_entities": "Nem feltárt entitások", + "not_exposed_entities": "Feltáratlan entitások", "sync_to_google": "A változások szinkronizálása a Google-lal.", "title": "Google Asszisztens" }, @@ -1340,7 +1373,7 @@ }, "entities": { "caption": "Entitások", - "description": "Az összes ismert entitás áttekintése", + "description": "Ismert entitások kezelése", "picker": { "disable_selected": { "button": "Kiválasztottak letiltása", @@ -1392,7 +1425,7 @@ "header": "Home Assistant beállítása", "helpers": { "caption": "Segítők", - "description": "Automatizálások létrehozását segítő elemek", + "description": "Automatizálások létrehozását segítő elemek kezelése", "dialog": { "add_helper": "Segítő hozzáadása", "add_platform": "{platform} hozzáadása", @@ -1420,7 +1453,7 @@ "built_using": "Buildelve:", "caption": "Infó", "custom_uis": "Egyéni felhasználói felületek:", - "description": "Információ a Home Assistant telepítésedről", + "description": "Telepítési információ megtekintése a Home Assistant-ról", "developed_by": "Egy csomó fantasztikus ember által kifejlesztve.", "documentation": "Dokumentáció", "frontend": "frontend-ui", @@ -1459,6 +1492,8 @@ "no_device": "Eszköz nélküli entitások", "no_devices": "Ez az integráció nem rendelkezik eszközökkel.", "options": "Opciók", + "reload": "Újratöltés", + "reload_confirm": "Az integráció újra lett töltve", "rename": "Átnevezés", "restart_confirm": "Indítsd újra a Home Assistant-ot az integráció törlésének befejezéséhez", "settings_button": "{integration} beállításainak szerkesztése", @@ -1483,7 +1518,7 @@ }, "configure": "Beállítás", "configured": "Konfigurálva", - "description": "Integrációk kezelése és beállítása", + "description": "Integrációk kezelése", "details": "Integráció részletei", "discovered": "Felfedezett", "home_assistant_website": "Home Assistant weboldal", @@ -1511,7 +1546,7 @@ "rename_input_label": "Bejegyzés neve", "search": "Integrációk keresése" }, - "introduction": "Itt a komponenseket és a Home Assistant szervert lehet beállítani. Még nem lehet mindent a felületről, de dolgozunk rajta.", + "introduction": "Ebben a nézetben lehetőség van a komponensek és a Home Assistant beállítására. Még nem lehet mindent a felületről, de dolgozunk rajta.", "logs": { "caption": "Napló", "clear": "Törlés", @@ -1567,7 +1602,7 @@ "open": "Megnyitás" } }, - "description": "Lovelace irányítópultjainak konfigurálása", + "description": "Lovelace irányítópultok kezelése", "resources": { "cant_edit_yaml": "A Lovelace-t YAML módban használod, ezért az erőforrásokat nem kezelheted a felhasználói felületről. Kezeld őket a configuration.yaml fájlban.", "caption": "Erőforrások", @@ -1645,7 +1680,7 @@ "scene": { "activated": "Aktivált jelenet: {name}.", "caption": "Jelenetek", - "description": "Jelenetek létrehozása és szerkesztése", + "description": "Jelenetek kezelése", "editor": { "default_name": "Új jelenet", "devices": { @@ -1689,7 +1724,7 @@ }, "script": { "caption": "Szkriptek", - "description": "Szkriptek létrehozása és szerkesztése", + "description": "Szkriptek kezelése", "editor": { "alias": "Név", "default_name": "Új Szkript", @@ -1738,7 +1773,7 @@ "reloading": { "automation": "Automatizálások újratöltése", "core": "Lokáció és testreszabások újratöltése", - "group": "Csoportok újratöltése", + "group": "Csoportok, csoport entitások és értesítési szolgáltatások újratöltése", "heading": "YAML konfiguráció újratöltése", "input_boolean": "Bemeneti logikai változók újratöltése", "input_datetime": "Időpont bemenetek újratöltése", @@ -1768,6 +1803,9 @@ } } }, + "tags": { + "never_scanned": "Sosem volt szkennelve" + }, "users": { "add_user": { "caption": "Felhasználó hozzáadása", @@ -1871,7 +1909,7 @@ "create_group": "Zigbee Home Automation - Csoport létrehozása", "create_group_details": "Add meg a szükséges adatokat egy új zigbee csoport létrehozásához", "creating_group": "Csoport létrehozása", - "description": "Zigbee csoportok létrehozása és módosítása", + "description": "Zigbee csoportok kezelése", "group_details": "Itt van minden részlet a kiválasztott zigbee csoportról.", "group_id": "Csoport ID", "group_info": "Csoportinformációk", @@ -2143,6 +2181,9 @@ "description": "A Gomb kártya gombok hozzáadását teszi lehetővé feladatok végrehajtásához.", "name": "Gomb" }, + "calendar": { + "name": "Naptár" + }, "conditional": { "card": "Kártya", "change_type": "Típus módosítása", @@ -2319,7 +2360,7 @@ "show_code_editor": "Kódszerkesztő megjelenítése", "show_visual_editor": "Vizuális szerkesztő megjelenítése", "toggle_editor": "Szerkesztő", - "typed_header": "{típusú} Kártya Konfiguráció", + "typed_header": "{type} Kártya Konfiguráció", "unsaved_changes": "Vannak nem mentett módosítások" }, "edit_lovelace": { @@ -2396,7 +2437,7 @@ }, "menu": { "close": "Bezárás", - "configure_ui": "Felhasználói felület konfigurálása", + "configure_ui": "Irányítópult szerkesztése", "exit_edit_mode": "Kilépés a felhasználói felület szerkesztési módból", "help": "Súgó", "refresh": "Frissítés", @@ -2589,6 +2630,7 @@ "intro": "Hello {name}, üdvözöl a Home Assistant. Hogyan szeretnéd elnevezni az otthonodat?", "intro_location": "Szeretnénk tudni, hogy hol élsz. Ez segít az információk megjelenítésében és a nap alapú automatizálások beállításában. Ez az adat soha nem lesz megosztva a hálózatodon kívül.", "intro_location_detect": "Segíthetünk neked kitölteni ezt az információt egy külső szolgáltatás egyszeri lekérdezésével.", + "location_name": "A Home Assistant rendszered neve", "location_name_default": "Otthon" }, "integration": { @@ -2659,7 +2701,7 @@ "learn_auth_requests": "Tudj meg többet a hitelesített kérelmek létrehozásáról.", "not_used": "Sosem használt", "prompt_copy_token": "Most másold ki a hozzáférési tokened! Erre később nem lesz lehetőséged.", - "prompt_name": "Név?" + "prompt_name": "Adj nevet a tokennek" }, "mfa_setup": { "close": "Bezárás", diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index 19aaf17e50..f0d6e46475 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -65,10 +65,7 @@ }, "state_badge": { "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", "armed_home": "アームしました", - "armed_night": "", "disarmed": "解除", "disarming": "解除", "pending": "保留", diff --git a/translations/frontend/lt.json b/translations/frontend/lt.json index 08fc0028b0..567cd131d2 100644 --- a/translations/frontend/lt.json +++ b/translations/frontend/lt.json @@ -189,7 +189,6 @@ "ok": "Ok", "on": "Įjungta", "open": "Atidarytas", - "problem": "", "stopped": "Sustabdytas", "unlocked": "Atrakinta" }, @@ -409,7 +408,6 @@ "label": "Laikas" }, "zone": { - "entity": "", "label": "Vieta", "zone": "Vieta" } @@ -690,9 +688,6 @@ "form": { "providers": { "command_line": { - "abort": { - "login_expired": "" - }, "error": { "invalid_auth": "Netinkamas vartotojo vardas arba slaptažodis", "invalid_code": "Netinkamas autentifikacijos kodas" @@ -707,18 +702,7 @@ "mfa": { "data": { "code": "Dvieju lygiu autentifikacija" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" + } } } } diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index cb96ebd163..68d09fa8ae 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -552,6 +552,9 @@ "toggle": "Przełącz" }, "entity": { + "entity-attribute-picker": { + "show_attributes": "Pokaż atrybuty" + }, "entity-picker": { "clear": "Wyczyść", "entity": "Encja", @@ -697,6 +700,7 @@ }, "more_info_control": { "controls": "Sterowanie", + "details": "Szczegóły", "dismiss": "Zamknij okno dialogowe", "edit": "Edytuj encję", "history": "Historia", @@ -1129,6 +1133,8 @@ "add_automation": "Dodaj automatyzację", "delete_automation": "Usuń automatyzację", "delete_confirm": "Czy na pewno chcesz usunąć tę automatyzację?", + "duplicate": "Duplikuj", + "duplicate_automation": "Duplikat automatyzację", "edit_automation": "Edytuj automatyzację", "header": "Edytor automatyzacji", "headers": { @@ -1599,6 +1605,7 @@ "none_found_detail": "Dostosuj kryteria wyszukiwania.", "note_about_integrations": "Jeszcze nie wszystkie integracje można skonfigurować za pomocą interfejsu użytkownika.", "note_about_website_reference": "Więcej jest dostępnych na stronie integracji ", + "reconfigure": "Zmień konfigurację", "rename_dialog": "Edytuj nazwę tego wpisu konfiguracji", "rename_input_label": "Nazwa wpisu", "search": "Szukaj integracji" @@ -2945,6 +2952,7 @@ "header": "Tokeny dostępu", "last_used": "Ostatnio używany {date} z {location}", "learn_auth_requests": "Dowiedz się, jak tworzyć uwierzytelnione żądania.", + "name": "Nazwa", "not_used": "Nigdy nie był używany", "prompt_copy_token": "Skopiuj token. Nie będzie on już ponownie wyświetlany.", "prompt_name": "Nazwa" diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 7122ac3008..739b8ccc63 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -706,6 +706,7 @@ }, "more_info_control": { "controls": "Управление", + "details": "Свойства", "dismiss": "Закрыть диалог", "edit": "Изменить объект", "history": "История", @@ -2962,6 +2963,7 @@ "confirm_delete": "Вы уверены, что хотите удалить токен доступа для {name}?", "create": "Создать токен", "create_failed": "Не удалось создать токен доступа.", + "created": "Создан {date}", "created_at": "Создан {date}", "delete_failed": "Не удалось удалить токен доступа.", "description": "Создайте долгосрочные токены доступа, чтобы Ваши скрипты могли взаимодействовать с Home Assistant. Каждый токен будет действителен в течение 10 лет с момента создания. Ниже Вы можете просмотреть долгосрочные токены доступа, которые в настоящее время активны.", @@ -2969,9 +2971,10 @@ "header": "Долгосрочные токены доступа", "last_used": "Последнее использование {date} из {location}", "learn_auth_requests": "Узнайте, как выполнять аутентифицированные запросы.", + "name": "Название", "not_used": "Никогда не использовался", "prompt_copy_token": "Скопируйте Ваш токен доступа. Он больше не будет показан.", - "prompt_name": "Введите название для токена" + "prompt_name": "Название токена" }, "mfa_setup": { "close": "Закрыть", diff --git a/translations/frontend/sr-Latn.json b/translations/frontend/sr-Latn.json index 11f7af24b5..7f497faa5b 100644 --- a/translations/frontend/sr-Latn.json +++ b/translations/frontend/sr-Latn.json @@ -8,85 +8,7 @@ "shopping_list": "Lista za kupovinu", "states": "Pregled" }, - "state_badge": { - "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", - "armed_night": "" - }, - "device_tracker": { - "home": "", - "not_home": "" - } - }, "state": { - "automation": { - "off": "", - "on": "" - }, - "binary_sensor": { - "default": { - "off": "", - "on": "" - }, - "motion": { - "on": "" - }, - "occupancy": { - "off": "" - }, - "opening": { - "on": "" - }, - "presence": { - "off": "", - "on": "" - }, - "vibration": { - "off": "", - "on": "" - } - }, - "calendar": { - "off": "", - "on": "" - }, - "fan": { - "off": "", - "on": "" - }, - "group": { - "home": "", - "not_home": "", - "off": "", - "on": "", - "problem": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "", - "on": "" - }, - "remote": { - "on": "" - }, - "script": { - "off": "", - "on": "" - }, - "sensor": { - "off": "", - "on": "" - }, - "switch": { - "on": "" - }, "weather": { "clear-night": "Vedra noć", "cloudy": "Oblačno", @@ -151,13 +73,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "type": { "mqtt": { @@ -192,45 +107,6 @@ "set_config_parameter": "Подесите параметар Цонфиг" } } - }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } } } } diff --git a/translations/frontend/sr.json b/translations/frontend/sr.json index b5debac397..2a32b3aca6 100644 --- a/translations/frontend/sr.json +++ b/translations/frontend/sr.json @@ -11,93 +11,21 @@ }, "state_badge": { "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", - "armed_night": "", "arming": "Aktiviranje", "disarming": "Deaktiviraj" }, "default": { "entity_not_found": "Вредност није пронађена", "error": "Грешка" - }, - "device_tracker": { - "home": "", - "not_home": "" } }, "state": { - "automation": { - "off": "", - "on": "" - }, - "binary_sensor": { - "default": { - "off": "", - "on": "" - }, - "motion": { - "on": "" - }, - "occupancy": { - "off": "" - }, - "opening": { - "on": "" - }, - "presence": { - "off": "", - "on": "" - }, - "vibration": { - "off": "", - "on": "" - } - }, - "calendar": { - "off": "", - "on": "" - }, - "fan": { - "off": "", - "on": "" - }, - "group": { - "home": "", - "not_home": "", - "off": "", - "on": "", - "problem": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "", - "on": "" - }, - "remote": { - "on": "" - }, - "script": { - "off": "", - "on": "" - }, - "sensor": { - "off": "", - "on": "" - }, "sun": { "above_horizon": "Iznad horizonta", "below_horizon": "Ispod horizonta" }, "switch": { - "off": "Isključen", - "on": "" + "off": "Isključen" }, "timer": { "active": "укључен", @@ -128,13 +56,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "learn_more": "Сазнајте више о окидачима", "type": { @@ -192,45 +113,6 @@ } } }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } - }, "page-onboarding": { "integration": { "finish": "Крај" diff --git a/translations/frontend/ta.json b/translations/frontend/ta.json index 9043c84c30..7445b2d66a 100644 --- a/translations/frontend/ta.json +++ b/translations/frontend/ta.json @@ -35,8 +35,7 @@ "unknown": "தெரியாத" }, "device_tracker": { - "home": "முகப்பு", - "not_home": "" + "home": "முகப்பு" }, "person": { "home": "முகப்பு" @@ -240,13 +239,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "type": { "mqtt": { @@ -265,45 +257,6 @@ "set_config_parameter": "கட்டமைப்பு அளவுருவை அமைக்கவும்" } } - }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } } } } diff --git a/translations/frontend/te.json b/translations/frontend/te.json index 50cf3c0969..1bddd1e80b 100644 --- a/translations/frontend/te.json +++ b/translations/frontend/te.json @@ -37,8 +37,7 @@ "unknown": "తెలియదు" }, "device_tracker": { - "home": "ఇంట", - "not_home": "" + "home": "ఇంట" }, "person": { "home": "ఇంట" @@ -411,9 +410,6 @@ "time": { "after": "తరువాత", "before": "ముందు" - }, - "zone": { - "entity": "" } } }, @@ -535,8 +531,7 @@ "login_expired": "సెషన్ గడువు ముగిసింది, మళ్ళీ లాగిన్ అవ్వండి." }, "error": { - "invalid_auth": "తప్పు యూజర్ పేరు లేదా తప్పు పాస్ వర్డ్", - "invalid_code": "" + "invalid_auth": "తప్పు యూజర్ పేరు లేదా తప్పు పాస్ వర్డ్" }, "step": { "init": { @@ -544,12 +539,6 @@ "password": "పాస్వర్డ్", "username": "యూజర్ పేరు" } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" } } }, @@ -569,16 +558,6 @@ } } }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, "trusted_networks": { "abort": { "not_whitelisted": "మీ కంప్యూటర్ అనుమతి జాబితాలో లేదు." diff --git a/translations/frontend/tr.json b/translations/frontend/tr.json index f0e477f212..13376adc80 100644 --- a/translations/frontend/tr.json +++ b/translations/frontend/tr.json @@ -2434,8 +2434,7 @@ "mfa": { "data": { "code": "İki adımlı kimlik doğrulama kodu" - }, - "description": "" + } } } }, @@ -2480,8 +2479,7 @@ "mfa": { "data": { "code": "İki adımlı kimlik doğrulama kodu" - }, - "description": "" + } } } }, diff --git a/translations/frontend/ur.json b/translations/frontend/ur.json index 2f7a214405..c130104798 100644 --- a/translations/frontend/ur.json +++ b/translations/frontend/ur.json @@ -1,49 +1,4 @@ { - "state_badge": { - "device_tracker": { - "home": "" - } - }, - "state": { - "automation": { - "off": "" - }, - "binary_sensor": { - "default": { - "on": "" - }, - "presence": { - "on": "" - } - }, - "calendar": { - "on": "" - }, - "group": { - "home": "", - "off": "", - "on": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "" - }, - "script": { - "off": "" - }, - "sensor": { - "off": "" - }, - "switch": { - "on": "" - } - }, "ui": { "card": { "fan": { @@ -84,13 +39,6 @@ "label": "آلہ" } } - }, - "triggers": { - "type": { - "mqtt": { - "label": "" - } - } } } }, diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index fef4a36abf..611909f0cf 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -567,6 +567,9 @@ "loading_history": "正在加载历史状态...", "no_history_found": "没有找到历史状态。" }, + "logbook": { + "entries_not_found": "未找到日志条目。" + }, "media-browser": { "audio_not_supported": "您的浏览器不支持音频元素。", "choose_player": "选择播放器", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "控制项", + "details": "详情", "dismiss": "关闭对话框", "edit": "编辑实体", "history": "历史", @@ -2959,6 +2963,7 @@ "confirm_delete": "确定要删除 {name} 的访问令牌吗?", "create": "创建令牌", "create_failed": "无法创建访问令牌。", + "created": "创建于 {date}", "created_at": "创建于 {date}", "delete_failed": "无法删除访问令牌。", "description": "创建长期访问令牌以允许脚本与 Home Assistant 实例进行交互。每个令牌在创建后有效期为 10 年。以下是处于激活状态的长期访问令牌。", @@ -2966,9 +2971,10 @@ "header": "长期访问令牌", "last_used": "上次使用于 {date} 来自 {location}", "learn_auth_requests": "了解如何创建经过身份验证的请求。", + "name": "名称", "not_used": "从未使用过", "prompt_copy_token": "请复制您的访问令牌。它不会再显示出来。", - "prompt_name": "名称?" + "prompt_name": "为令牌指定名称" }, "mfa_setup": { "close": "关闭", From c979cfb912a91062e5cb48acfb6cb9137e46f14c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 12:52:37 +0200 Subject: [PATCH 104/146] Fix sidebar for not existing hidden panel (#6944) Fixes #6940 --- src/components/ha-sidebar.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 49daf43104..028fe8defd 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -287,27 +287,29 @@ class HaSidebar extends LitElement { ? html` ${this._hiddenPanels.map((url) => { const panel = this.hass.panels[url]; + if (!panel) { + return ""; + } return html` ${panel.url_path === "lovelace" + >${panel.url_path === this.hass.defaultPanel ? hass.localize("panel.states") : hass.localize(`panel.${panel.title}`) || panel.title} - + + + `; })}
    @@ -544,7 +546,7 @@ class HaSidebar extends LitElement { private async _unhidePanel(ev: Event) { ev.preventDefault(); - const index = this._hiddenPanels.indexOf((ev.target as any).panel); + const index = this._hiddenPanels.indexOf((ev.currentTarget as any).panel); if (index < 0) { return; } From 266f2e763d98cdb97505a8a13a77329175c17f8a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 17:48:40 +0200 Subject: [PATCH 105/146] Sort listening entity and domain in template dev tools (#6953) --- .../template/developer-tools-template.ts | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index f2f011f098..dfabd912d6 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -14,8 +14,8 @@ import { debounce } from "../../../common/util/debounce"; import "../../../components/ha-circular-progress"; import "../../../components/ha-code-editor"; import { - subscribeRenderTemplate, RenderTemplateResult, + subscribeRenderTemplate, } from "../../../data/ws-templates"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; @@ -151,41 +151,49 @@ class HaPanelDevTemplate extends LitElement { ? "" : this._templateResult.listeners.all ? html` - +

    ${this.hass.localize( "ui.panel.developer-tools.tabs.templates.all_listeners" )} - +

    ` : this._templateResult.listeners.domains.length || this._templateResult.listeners.entities.length ? html` - ${this.hass.localize( - "ui.panel.developer-tools.tabs.templates.listeners" - )} +

    + ${this.hass.localize( + "ui.panel.developer-tools.tabs.templates.listeners" + )} +

      - ${this._templateResult.listeners.domains.map( - (domain) => - html` -
    • - ${this.hass.localize( - "ui.panel.developer-tools.tabs.templates.domain" - )}: - ${domain} -
    • - ` - )} - ${this._templateResult.listeners.entities.map( - (entity_id) => - html` -
    • - ${this.hass.localize( - "ui.panel.developer-tools.tabs.templates.entity" - )}: - ${entity_id} -
    • - ` - )} + ${this._templateResult.listeners.domains + .sort() + .map( + (domain) => + html` +
    • + ${this.hass.localize( + "ui.panel.developer-tools.tabs.templates.domain" + )}: ${domain} +
    • + ` + )} + ${this._templateResult.listeners.entities + .sort() + .map( + (entity_id) => + html` +
    • + ${this.hass.localize( + "ui.panel.developer-tools.tabs.templates.entity" + )}: ${entity_id} +
    • + ` + )}
    ` : html` From 90e09fc384d15f029463b9de43a9ff2479354c4f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 18:07:14 +0200 Subject: [PATCH 106/146] Add default hold actions (#6952) Fixes #6942 --- src/panels/lovelace/cards/hui-button-card.ts | 30 +++++-------------- src/panels/lovelace/cards/hui-glance-card.ts | 23 ++++++++++---- src/panels/lovelace/cards/hui-light-card.ts | 11 +++---- .../lovelace/cards/hui-picture-glance-card.ts | 10 +++++-- .../lovelace/cards/hui-thermostat-card.ts | 14 ++++----- .../config-elements/hui-light-card-editor.ts | 6 ++-- .../hui-picture-glance-card-editor.ts | 8 ++--- .../lovelace/elements/hui-icon-element.ts | 4 +-- .../lovelace/elements/hui-image-element.ts | 7 +++-- .../elements/hui-state-badge-element.ts | 6 ++-- .../elements/hui-state-icon-element.ts | 10 +++++-- .../elements/hui-state-label-element.ts | 6 ++-- 12 files changed, 69 insertions(+), 66 deletions(-) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index a2662ab7bd..65c045f8ab 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -21,6 +21,7 @@ import { DOMAINS_TOGGLE } from "../../../common/const"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { computeActiveState } from "../../../common/entity/compute_active_state"; import { computeDomain } from "../../../common/entity/compute_domain"; +import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { stateIcon } from "../../../common/entity/state_icon"; @@ -36,7 +37,6 @@ import { hasAction } from "../common/has-action"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { ButtonCardConfig } from "./types"; -import { computeStateDisplay } from "../../../common/entity/compute_state_display"; @customElement("hui-button-card") export class HuiButtonCard extends LitElement implements LovelaceCard { @@ -63,11 +63,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { return { type: "button", - tap_action: { action: "toggle" }, - hold_action: { action: "more-info" }, - show_icon: true, - show_name: true, - show_state: false, entity: foundEntities[0] || "", }; } @@ -92,29 +87,18 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { } this._config = { + tap_action: { + action: + config.entity && DOMAINS_TOGGLE.has(computeDomain(config.entity)) + ? "toggle" + : "more-info", + }, hold_action: { action: "more-info" }, - double_tap_action: { action: "none" }, show_icon: true, show_name: true, state_color: true, ...config, }; - - if (config.entity && DOMAINS_TOGGLE.has(computeDomain(config.entity))) { - this._config = { - tap_action: { - action: "toggle", - }, - ...this._config, - }; - } else { - this._config = { - tap_action: { - action: "more-info", - }, - ...this._config, - }; - } } protected shouldUpdate(changedProps: PropertyValues): boolean { diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 46b5be3735..0da08a86a1 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -20,18 +20,22 @@ import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon"; import { UNAVAILABLE_STATES } from "../../../data/entity"; -import { ActionHandlerEvent } from "../../../data/lovelace"; +import { + ActionHandlerEvent, + CallServiceActionConfig, + MoreInfoActionConfig, +} from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entites"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { processConfigEntities } from "../common/process-config-entities"; +import "../components/hui-timestamp-display"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import "../components/hui-timestamp-display"; import { GlanceCardConfig, GlanceConfigEntity } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-glance-card") export class HuiGlanceCard extends LitElement implements LovelaceCard { @@ -86,7 +90,14 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { state_color: true, ...config, }; - const entities = processConfigEntities(config.entities); + const entities = processConfigEntities( + config.entities + ).map((entityConf) => { + return { + hold_action: { action: "more-info" } as MoreInfoActionConfig, + ...entityConf, + }; + }); for (const entity of entities) { if ( @@ -95,7 +106,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { !entity.tap_action.service) || (entity.hold_action && entity.hold_action.action === "call-service" && - !entity.hold_action.service) + !(entity.hold_action as CallServiceActionConfig).service) ) { throw new Error( 'Missing required property "service" when tap_action or hold_action is call-service' diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index f05a1c4c1c..0a9bc908b2 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -1,13 +1,13 @@ -import "../../../components/ha-icon-button"; +import { mdiDotsVertical } from "@mdi/js"; import "@thomasloven/round-slider"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -20,7 +20,8 @@ import { computeStateName } from "../../../common/entity/compute_state_name"; import { stateIcon } from "../../../common/entity/state_icon"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-card"; -import { UNAVAILABLE_STATES, UNAVAILABLE } from "../../../data/entity"; +import "../../../components/ha-icon-button"; +import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; import { SUPPORT_BRIGHTNESS } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant, LightEntity } from "../../../types"; @@ -32,7 +33,6 @@ import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LightCardConfig } from "./types"; -import { mdiDotsVertical } from "@mdi/js"; @customElement("hui-light-card") export class HuiLightCard extends LitElement implements LovelaceCard { @@ -77,8 +77,9 @@ export class HuiLightCard extends LitElement implements LovelaceCard { } this._config = { - ...config, tap_action: { action: "toggle" }, + hold_action: { action: "more-info" }, + ...config, }; } diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts index 45c0ea1516..0c04eeccf3 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.ts +++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -28,10 +28,10 @@ import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { processConfigEntities } from "../common/process-config-entities"; import "../components/hui-image"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { PictureGlanceCardConfig, PictureGlanceEntityConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]); @@ -104,7 +104,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { } }); - this._config = config; + this._config = { + hold_action: { action: "more-info" }, + ...config, + }; } protected shouldUpdate(changedProps: PropertyValues): boolean { @@ -225,6 +228,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { entityConf = { tap_action: { action: dialog ? "more-info" : "toggle" }, + hold_action: { action: "more-info" }, ...entityConf, }; diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index e8a7f71b48..8470bd4c34 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -1,4 +1,4 @@ -import "../../../components/ha-icon-button"; +import { mdiDotsVertical } from "@mdi/js"; import "@thomasloven/round-slider"; import { HassEntity } from "home-assistant-js-websocket"; import { @@ -6,13 +6,13 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, + query, svg, TemplateResult, - query, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { UNIT_F } from "../../../common/const"; @@ -20,6 +20,8 @@ import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_elemen import { fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-card"; +import type { HaCard } from "../../../components/ha-card"; +import "../../../components/ha-icon-button"; import { ClimateEntity, CLIMATE_PRESET_NONE, @@ -28,14 +30,11 @@ import { } from "../../../data/climate"; import { UNAVAILABLE } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; -import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entites"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { ThermostatCardConfig } from "./types"; -import type { HaCard } from "../../../components/ha-card"; -import { mdiDotsVertical } from "@mdi/js"; const modeIcons: { [mode in HvacMode]: string } = { auto: "hass:calendar-sync", @@ -385,8 +384,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { class="${classMap({ "selected-icon": currentMode === mode })}" .mode="${mode}" .icon="${modeIcons[mode]}" - @action=${this._handleAction} - .actionHandler=${actionHandler()} + @click=${this._handleAction} tabindex="0" > `; diff --git a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts index 90b63d4276..1d90cd20ad 100644 --- a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts @@ -62,11 +62,11 @@ export class HuiLightCardEditor extends LitElement } get _hold_action(): ActionConfig { - return this._config!.hold_action || { action: "none" }; + return this._config!.hold_action || { action: "more-info" }; } - get _double_tap_action(): ActionConfig { - return this._config!.double_tap_action || { action: "none" }; + get _double_tap_action(): ActionConfig | undefined { + return this._config!.double_tap_action; } protected render(): TemplateResult { diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts index 7f8ef8ebd9..0009348efb 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts @@ -90,12 +90,12 @@ export class HuiPictureGlanceCardEditor extends LitElement return this._config!.aspect_ratio || ""; } - get _tap_action(): ActionConfig | undefined { - return this._config!.tap_action; + get _tap_action(): ActionConfig { + return this._config!.tap_action || { action: "toggle" }; } - get _hold_action(): ActionConfig | undefined { - return this._config!.hold_action; + get _hold_action(): ActionConfig { + return this._config!.hold_action || { action: "more-info" }; } get _theme(): string { diff --git a/src/panels/lovelace/elements/hui-icon-element.ts b/src/panels/lovelace/elements/hui-icon-element.ts index 82e7a7fc39..4811f219ac 100644 --- a/src/panels/lovelace/elements/hui-icon-element.ts +++ b/src/panels/lovelace/elements/hui-icon-element.ts @@ -3,8 +3,8 @@ import { CSSResult, customElement, html, - LitElement, internalProperty, + LitElement, TemplateResult, } from "lit-element"; import { ifDefined } from "lit-html/directives/if-defined"; @@ -28,7 +28,7 @@ export class HuiIconElement extends LitElement implements LovelaceElement { throw Error("Invalid Configuration: 'icon' required"); } - this._config = config; + this._config = { hold_action: { action: "more-info" }, ...config }; } protected render(): TemplateResult { diff --git a/src/panels/lovelace/elements/hui-image-element.ts b/src/panels/lovelace/elements/hui-image-element.ts index a42c7bab8f..50e45c153a 100644 --- a/src/panels/lovelace/elements/hui-image-element.ts +++ b/src/panels/lovelace/elements/hui-image-element.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import { ifDefined } from "lit-html/directives/if-defined"; @@ -29,12 +29,13 @@ export class HuiImageElement extends LitElement implements LovelaceElement { throw Error("Error in element configuration"); } + this._config = { hold_action: { action: "more-info" }, ...config }; + // eslint-disable-next-line wc/no-self-class this.classList.toggle( "clickable", - config.tap_action && config.tap_action.action !== "none" + this._config.tap_action && this._config.tap_action.action !== "none" ); - this._config = config; } protected render(): TemplateResult { diff --git a/src/panels/lovelace/elements/hui-state-badge-element.ts b/src/panels/lovelace/elements/hui-state-badge-element.ts index 2a008921fa..1baa6b0d8d 100644 --- a/src/panels/lovelace/elements/hui-state-badge-element.ts +++ b/src/panels/lovelace/elements/hui-state-badge-element.ts @@ -1,9 +1,9 @@ import { customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -16,9 +16,9 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceElement, StateBadgeElementConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-state-badge-element") export class HuiStateBadgeElement extends LitElement @@ -32,7 +32,7 @@ export class HuiStateBadgeElement extends LitElement throw Error("Invalid Configuration: 'entity' required"); } - this._config = config; + this._config = { hold_action: { action: "more-info" }, ...config }; } protected shouldUpdate(changedProps: PropertyValues): boolean { diff --git a/src/panels/lovelace/elements/hui-state-icon-element.ts b/src/panels/lovelace/elements/hui-state-icon-element.ts index d4a1a2eba1..84c8593ad0 100644 --- a/src/panels/lovelace/elements/hui-state-icon-element.ts +++ b/src/panels/lovelace/elements/hui-state-icon-element.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -18,9 +18,9 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceElement, StateIconElementConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-state-icon-element") export class HuiStateIconElement extends LitElement implements LovelaceElement { @@ -33,7 +33,11 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement { throw Error("Invalid Configuration: 'entity' required"); } - this._config = { state_color: true, ...config }; + this._config = { + state_color: true, + hold_action: { action: "more-info" }, + ...config, + }; } protected shouldUpdate(changedProps: PropertyValues): boolean { diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts index 08de7d4516..7296f0dc64 100644 --- a/src/panels/lovelace/elements/hui-state-label-element.ts +++ b/src/panels/lovelace/elements/hui-state-label-element.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -18,9 +18,9 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceElement, StateLabelElementConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-state-label-element") class HuiStateLabelElement extends LitElement implements LovelaceElement { @@ -33,7 +33,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement { throw Error("Invalid Configuration: 'entity' required"); } - this._config = config; + this._config = { hold_action: { action: "more-info" }, ...config }; } protected shouldUpdate(changedProps: PropertyValues): boolean { From 7e70ba6ab2299b7e148b36746bde48155f2139ac Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 18:18:57 +0200 Subject: [PATCH 107/146] FIx entities picker (#6951) Fixes #6947 ? --- src/components/entity/ha-entity-picker.ts | 6 ++-- .../lovelace/components/hui-entity-editor.ts | 31 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 51ddf0d30f..3cafa0a88b 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -6,6 +6,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResult, + customElement, html, LitElement, property, @@ -51,7 +52,8 @@ const rowRenderer = ( root.querySelector("[secondary]")!.textContent = model.item.entity_id; }; -class HaEntityPicker extends LitElement { +@customElement("ha-entity-picker") +export class HaEntityPicker extends LitElement { @property({ type: Boolean }) public autofocus = false; @property({ type: Boolean }) public disabled?: boolean; @@ -276,8 +278,6 @@ class HaEntityPicker extends LitElement { } } -customElements.define("ha-entity-picker", HaEntityPicker); - declare global { interface HTMLElementTagNameMap { "ha-entity-picker": HaEntityPicker; diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 90e4b5eeb4..ddde4ef768 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -18,10 +18,10 @@ import Sortable, { } from "sortablejs/modular/sortable.core.esm"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-entity-picker"; +import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker"; import "../../../components/ha-icon-button"; import { sortableStyles } from "../../../resources/ha-sortable-style"; import { HomeAssistant } from "../../../types"; -import { EditorTarget } from "../editor/types"; import { EntityConfig } from "../entity-rows/types"; @customElement("hui-entity-editor") @@ -73,7 +73,7 @@ export class HuiEntityEditor extends LitElement { .hass=${this.hass} .value=${entityConf.entity} .index=${index} - @change=${this._valueChanged} + @value-changed=${this._valueChanged} allow-custom-entity >
    @@ -83,7 +83,7 @@ export class HuiEntityEditor extends LitElement {
    `; } @@ -136,15 +136,15 @@ export class HuiEntityEditor extends LitElement { }); } - private async _addEntity(ev: Event): Promise { - const target = ev.target! as EditorTarget; - if (target.value === "") { + private async _addEntity(ev: CustomEvent): Promise { + const value = ev.detail.value; + if (value === "") { return; } const newConfigEntities = this.entities!.concat({ - entity: target.value as string, + entity: value as string, }); - target.value = ""; + (ev.target as HaEntityPicker).value = ""; fireEvent(this, "entities-changed", { entities: newConfigEntities }); } @@ -160,16 +160,17 @@ export class HuiEntityEditor extends LitElement { fireEvent(this, "entities-changed", { entities: newEntities }); } - private _valueChanged(ev: Event): void { - const target = ev.target! as EditorTarget; + private _valueChanged(ev: CustomEvent): void { + const value = ev.detail.value; + const index = (ev.target as any).index; const newConfigEntities = this.entities!.concat(); - if (target.value === "") { - newConfigEntities.splice(target.index!, 1); + if (value === "") { + newConfigEntities.splice(index, 1); } else { - newConfigEntities[target.index!] = { - ...newConfigEntities[target.index!], - entity: target.value!, + newConfigEntities[index] = { + ...newConfigEntities[index], + entity: value!, }; } From 8b490c50475cca775cc7e8d6bcb4f35b41ef5f69 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Sat, 12 Sep 2020 11:59:19 -0500 Subject: [PATCH 108/146] Media Browser: Use Media Class (#6904) Co-authored-by: Bram Kragten --- .../media-player/ha-media-player-browse.ts | 334 ++++++++++-------- src/data/media-player.ts | 80 +++++ .../media-browser/ha-panel-media-browser.ts | 39 +- src/translations/en.json | 25 +- 4 files changed, 313 insertions(+), 165 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 741a85b238..7bf868254f 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -2,7 +2,7 @@ import "@material/mwc-button/mwc-button"; import "@material/mwc-fab/mwc-fab"; import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list-item"; -import { mdiArrowLeft, mdiClose, mdiFolder, mdiPlay, mdiPlus } from "@mdi/js"; +import { mdiArrowLeft, mdiClose, mdiPlay, mdiPlus } from "@mdi/js"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import { @@ -19,7 +19,6 @@ import { import { classMap } from "lit-html/directives/class-map"; import { ifDefined } from "lit-html/directives/if-defined"; import { styleMap } from "lit-html/directives/style-map"; -import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; @@ -27,6 +26,7 @@ import { browseLocalMediaPlayer, browseMediaPlayer, BROWSER_SOURCE, + MediaClassBrowserSettings, MediaPickedEvent, MediaPlayerBrowseAction, } from "../../data/media-player"; @@ -93,34 +93,6 @@ export class HaMediaPlayerBrowse extends LitElement { this._navigate(item); } - private _renderError(err: { message: string; code: string }) { - if (err.message === "Media directory does not exist.") { - return html` -

    No local media found.

    -

    - It looks like you have not yet created a media directory. -
    Create a directory with the name "media" in the - configuration directory of Home Assistant - (${this.hass.config.config_dir}).
    Place your video, audio and - image files in this directory to be able to browse and play them in - the browser or on supported media players. -

    - -

    - Check the - documentation - for more info -

    - `; - } - return err.message; - } - protected render(): TemplateResult { if (this._loading) { return html``; @@ -136,7 +108,7 @@ export class HaMediaPlayerBrowse extends LitElement { text: this._renderError(this._error), }); } else { - return html`
    + return html`
    ${this._renderError(this._error)}
    `; } @@ -155,17 +127,12 @@ export class HaMediaPlayerBrowse extends LitElement { ? this._mediaPlayerItems[this._mediaPlayerItems.length - 2] : undefined; - const hasExpandableChildren: - | MediaPlayerItem - | undefined = this._hasExpandableChildren(currentItem.children); - - const showImages: boolean | undefined = currentItem.children?.some( - (child) => child.thumbnail && child.thumbnail !== currentItem.thumbnail - ); - - const mediaType = this.hass.localize( - `ui.components.media-browser.content-type.${currentItem.media_content_type}` + const subtitle = this.hass.localize( + `ui.components.media-browser.class.${currentItem.media_class}` ); + const mediaClass = MediaClassBrowserSettings[currentItem.media_class]; + const childrenMediaClass = + MediaClassBrowserSettings[currentItem.children_media_class]; return html`
    -
    - ${currentItem.thumbnail - ? html` -
    - ${this._narrow && currentItem?.can_play - ? html` - - +
    + ${currentItem.thumbnail + ? html` +
    + ${this._narrow && currentItem?.can_play + ? html` + + + ${this.hass.localize( + `ui.components.media-browser.${this.action}` )} - .path=${this.action === "play" ? mdiPlay : mdiPlus} - > - ${this.hass.localize( - `ui.components.media-browser.${this.action}` - )} - - ` - : ""} -
    - ` - : html``} -
    - + ` + : html``} +
    + + ${currentItem.can_play && + (!currentItem.thumbnail || !this._narrow) ? html` -
    - - ${previousItem.title} -
    - ` - : ""} -

    ${currentItem.title}

    - ${mediaType - ? html` -

    - ${mediaType} -

    + + + ${this.hass.localize( + `ui.components.media-browser.${this.action}` + )} + ` : ""}
    - ${currentItem.can_play && (!currentItem.thumbnail || !this._narrow) - ? html` - - - ${this.hass.localize( - `ui.components.media-browser.${this.action}` - )} - - ` - : ""}
    + ${this.dialog + ? html` + + + + ` + : ""}
    - ${this.dialog - ? html` - - - - ` - : ""}
    ${this._error - ? html`
    - ${this._renderError(this._error)} -
    ` + ? html` +
    + ${this._renderError(this._error)} +
    + ` : currentItem.children?.length - ? hasExpandableChildren + ? childrenMediaClass.layout === "grid" ? html` -
    +
    ${currentItem.children.map( (child) => html`
    - ${child.can_expand && !child.thumbnail + ${!child.thumbnail ? html` ` : ""} @@ -298,7 +281,9 @@ export class HaMediaPlayerBrowse extends LitElement { ${child.can_play ? html` html` ` - : html`
    - ${this.hass.localize("ui.components.media-browser.no_items")} -
    `} + : html` +
    + ${this.hass.localize("ui.components.media-browser.no_items")} +
    + `} `; } @@ -504,14 +492,38 @@ export class HaMediaPlayerBrowse extends LitElement { this._resizeObserver.observe(this); } - private _hasExpandableChildren = memoizeOne((children?: MediaPlayerItem[]) => - children?.find((item: MediaPlayerItem) => item.can_expand) - ); - private _closeDialogAction(): void { fireEvent(this, "close-dialog"); } + private _renderError(err: { message: string; code: string }) { + if (err.message === "Media directory does not exist.") { + return html` +

    No local media found.

    +

    + It looks like you have not yet created a media directory. +
    Create a directory with the name "media" in the + configuration directory of Home Assistant + (${this.hass.config.config_dir}).
    Place your video, audio and + image files in this directory to be able to browse and play them in + the browser or on supported media players. +

    + +

    + Check the + documentation + for more info +

    + `; + } + return html`err.message`; + } + static get styles(): CSSResultArray { return [ haStyle, @@ -529,12 +541,9 @@ export class HaMediaPlayerBrowse extends LitElement { } .header { - display: flex; + display: block; justify-content: space-between; border-bottom: 1px solid var(--divider-color); - } - - .header { background-color: var(--card-background-color); position: sticky; position: -webkit-sticky; @@ -543,6 +552,10 @@ export class HaMediaPlayerBrowse extends LitElement { padding: 20px 24px 10px; } + .header-wrapper { + display: flex; + } + .header-content { display: flex; flex-wrap: wrap; @@ -570,6 +583,7 @@ export class HaMediaPlayerBrowse extends LitElement { .header-info mwc-button { display: block; + --mdc-theme-primary: var(--primary-color); } .breadcrumb { @@ -655,7 +669,7 @@ export class HaMediaPlayerBrowse extends LitElement { width: 100%; } - ha-card { + .children ha-card { width: 100%; padding-bottom: 100%; position: relative; @@ -663,6 +677,11 @@ export class HaMediaPlayerBrowse extends LitElement { background-size: cover; background-repeat: no-repeat; background-position: center; + transition: padding-bottom 0.1s ease-out; + } + + .portrait.children ha-card { + padding-bottom: 150%; } .child .folder, @@ -678,18 +697,36 @@ export class HaMediaPlayerBrowse extends LitElement { } .child .play { + transition: color 0.5s; + border-radius: 50%; + bottom: calc(50% - 35px); + right: calc(50% - 35px); + opacity: 0; + transition: opacity 0.1s ease-out; + } + + .child .play:not(.can_expand) { + --mdc-icon-button-size: 70px; + --mdc-icon-size: 48px; + } + + .ha-card-parent:hover .play:not(.can_expand) { + opacity: 1; + color: var(--primary-color); + } + + .child .play.can_expand { + opacity: 1; + background-color: rgba(var(--rgb-card-background-color), 0.5); bottom: 4px; right: 4px; - transition: all 0.5s; - background-color: rgba(var(--rgb-card-background-color), 0.5); - border-radius: 50%; } .child .play:hover { color: var(--primary-color); } - ha-card:hover { + .ha-card-parent:hover ha-card { opacity: 0.5; } @@ -706,6 +743,7 @@ export class HaMediaPlayerBrowse extends LitElement { .child .type { font-size: 12px; color: var(--secondary-text-color); + padding-left: 2px; } mwc-list-item .graphic { diff --git a/src/data/media-player.ts b/src/data/media-player.ts index af11824211..70b29c2d63 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -1,5 +1,23 @@ import type { HassEntity } from "home-assistant-js-websocket"; import type { HomeAssistant } from "../types"; +import { + mdiFolder, + mdiPlaylistMusic, + mdiFileMusic, + mdiAlbum, + mdiMusic, + mdiTelevisionClassic, + mdiMovie, + mdiVideo, + mdiImage, + mdiWeb, + mdiGamepadVariant, + mdiAccountMusic, + mdiPodcast, + mdiApplication, + mdiAccountMusicOutline, + mdiDramaMasks, +} from "@mdi/js"; export const SUPPORT_PAUSE = 1; export const SUPPORT_SEEK = 2; @@ -22,6 +40,66 @@ export type MediaPlayerBrowseAction = "pick" | "play"; export const BROWSER_SOURCE = "browser"; +export type MediaClassBrowserSetting = { + icon: string; + thumbnail_ratio?: string; + layout?: string; + show_list_images?: boolean; +}; + +export const MediaClassBrowserSettings: { + [type: string]: MediaClassBrowserSetting; +} = { + album: { icon: mdiAlbum, layout: "grid" }, + app: { icon: mdiApplication, layout: "grid" }, + artist: { icon: mdiAccountMusic, layout: "grid", show_list_images: true }, + channel: { + icon: mdiTelevisionClassic, + thumbnail_ratio: "portrait", + layout: "grid", + }, + composer: { + icon: mdiAccountMusicOutline, + layout: "grid", + show_list_images: true, + }, + contributing_artist: { + icon: mdiAccountMusic, + layout: "grid", + show_list_images: true, + }, + directory: { icon: mdiFolder, layout: "grid", show_list_images: true }, + episode: { + icon: mdiTelevisionClassic, + layout: "grid", + thumbnail_ratio: "portrait", + }, + game: { + icon: mdiGamepadVariant, + layout: "grid", + thumbnail_ratio: "portrait", + }, + genre: { icon: mdiDramaMasks, layout: "grid", show_list_images: true }, + image: { icon: mdiImage, layout: "grid" }, + movie: { icon: mdiMovie, thumbnail_ratio: "portrait", layout: "grid" }, + music: { icon: mdiMusic }, + playlist: { icon: mdiPlaylistMusic, layout: "grid", show_list_images: true }, + podcast: { icon: mdiPodcast, layout: "grid" }, + season: { + icon: mdiTelevisionClassic, + layout: "grid", + thumbnail_ratio: "portrait", + }, + track: { icon: mdiFileMusic }, + tv_show: { + icon: mdiTelevisionClassic, + layout: "grid", + thumbnail_ratio: "portrait", + }, + url: { icon: mdiWeb }, + video: { icon: mdiVideo, layout: "grid" }, +}; + export interface MediaPickedEvent { item: MediaPlayerItem; } @@ -40,6 +118,8 @@ export interface MediaPlayerItem { title: string; media_content_type: string; media_content_id: string; + media_class: string; + children_media_class: string; can_play: boolean; can_expand: boolean; thumbnail?: string; diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index a58243857e..63c71d2e92 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -1,5 +1,4 @@ import "@material/mwc-icon-button"; -import { mdiPlayNetwork } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import { @@ -46,9 +45,9 @@ class PanelMediaBrowser extends LitElement { const title = this._entityId === BROWSER_SOURCE - ? `${this.hass.localize("ui.components.media-browser.web-browser")} - ` + ? `${this.hass.localize("ui.components.media-browser.web-browser")}` : stateObj?.attributes.friendly_name - ? `${stateObj?.attributes.friendly_name} - ` + ? `${stateObj?.attributes.friendly_name}` : undefined; return html` @@ -59,17 +58,17 @@ class PanelMediaBrowser extends LitElement { .hass=${this.hass} .narrow=${this.narrow} > -
    - ${title || ""}${this.hass.localize( - "ui.components.media-browser.media-player-browser" - )} -
    - - +
    +
    ${this.hass.localize( - "ui.components.media-browser.choose_player" + "ui.components.media-browser.media-player-browser" )} - +
    +
    ${title || ""}
    +
    + + ${this.hass.localize("ui.components.media-browser.choose_player")} +
    @@ -134,9 +133,25 @@ class PanelMediaBrowser extends LitElement { return [ haStyle, css` + :host { + --mdc-theme-primary: var(--app-header-text-color); + } ha-media-player-browse { height: calc(100vh - 84px); } + :host([narrow]) app-toolbar mwc-button { + width: 65px; + } + .heading { + overflow: hidden; + white-space: nowrap; + } + .heading .secondary { + color: var(--secondary-text-color); + font-size: 14px; + overflow: hidden; + text-overflow: ellipsis; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index e7d2ea366e..8eadb910a9 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -373,12 +373,27 @@ "video_not_supported": "Your browser does not support the video element.", "media_not_supported": "The Browser Media Player does not support this type of media", "media_browsing_error": "Media Browsing Error", - "content-type": { - "server": "Server", - "library": "Library", - "artist": "Artist", + "class": { "album": "Album", - "playlist": "Playlist" + "app": "App", + "artist": "Artist", + "channel": "Channel", + "composer": "Composer", + "contributing_artist": "Contributing Artist", + "directory": "Library", + "episode": "Episode", + "game": "Game", + "genre": "Genre", + "image": "Image", + "movie": "Movie", + "music": "Music", + "playlist": "Playlist", + "podcast": "Podcast", + "season": "Season", + "track": "Track", + "tv_show": "TV Show", + "url": "Url", + "video": "Video" } } }, From 8a998369d62443df5b83e3c2c6a2ddb452bf0158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 19:15:00 +0200 Subject: [PATCH 109/146] Add padding to rendered template result (#6954) --- src/panels/developer-tools/template/developer-tools-template.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index dfabd912d6..c9ca775fbf 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -249,6 +249,7 @@ class HaPanelDevTemplate extends LitElement { clear: both; white-space: pre-wrap; background-color: var(--secondary-background-color); + padding: 8px; } .all_listeners { From 99d0a0a6fdd1490aa4db4984339c0052f4ba6e4f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 19:39:54 +0200 Subject: [PATCH 110/146] Lazy load more info content, split logbook and history (#6936) --- gallery/src/components/demo-more-info.js | 2 +- gallery/src/components/more-info-content.ts | 73 ++++++++ gallery/src/demos/demo-more-info-light.ts | 2 +- src/common/const.ts | 1 - src/common/util/throttle.ts | 50 +++++ src/dialogs/more-info/ha-more-info-dialog.ts | 91 ++++++++-- src/dialogs/more-info/ha-more-info-history.ts | 118 +++--------- src/dialogs/more-info/ha-more-info-logbook.ts | 171 ++++++++++++++++++ src/dialogs/more-info/more-info-content.ts | 73 -------- 9 files changed, 391 insertions(+), 190 deletions(-) create mode 100644 gallery/src/components/more-info-content.ts create mode 100644 src/common/util/throttle.ts create mode 100644 src/dialogs/more-info/ha-more-info-logbook.ts delete mode 100644 src/dialogs/more-info/more-info-content.ts diff --git a/gallery/src/components/demo-more-info.js b/gallery/src/components/demo-more-info.js index 9def4e6095..9e4a34023f 100644 --- a/gallery/src/components/demo-more-info.js +++ b/gallery/src/components/demo-more-info.js @@ -2,8 +2,8 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; /* eslint-plugin-disable lit */ import { PolymerElement } from "@polymer/polymer/polymer-element"; import "../../../src/components/ha-card"; -import "../../../src/dialogs/more-info/more-info-content"; import "../../../src/state-summary/state-card-content"; +import "./more-info-content"; class DemoMoreInfo extends PolymerElement { static get template() { diff --git a/gallery/src/components/more-info-content.ts b/gallery/src/components/more-info-content.ts new file mode 100644 index 0000000000..6d7271155e --- /dev/null +++ b/gallery/src/components/more-info-content.ts @@ -0,0 +1,73 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { property, PropertyValues, UpdatingElement } from "lit-element"; +import dynamicContentUpdater from "../../../src/common/dom/dynamic_content_updater"; +import { stateMoreInfoType } from "../../../src/common/entity/state_more_info_type"; +import "../../../src/dialogs/more-info/controls/more-info-alarm_control_panel"; +import "../../../src/dialogs/more-info/controls/more-info-automation"; +import "../../../src/dialogs/more-info/controls/more-info-camera"; +import "../../../src/dialogs/more-info/controls/more-info-climate"; +import "../../../src/dialogs/more-info/controls/more-info-configurator"; +import "../../../src/dialogs/more-info/controls/more-info-counter"; +import "../../../src/dialogs/more-info/controls/more-info-cover"; +import "../../../src/dialogs/more-info/controls/more-info-default"; +import "../../../src/dialogs/more-info/controls/more-info-fan"; +import "../../../src/dialogs/more-info/controls/more-info-group"; +import "../../../src/dialogs/more-info/controls/more-info-humidifier"; +import "../../../src/dialogs/more-info/controls/more-info-input_datetime"; +import "../../../src/dialogs/more-info/controls/more-info-light"; +import "../../../src/dialogs/more-info/controls/more-info-lock"; +import "../../../src/dialogs/more-info/controls/more-info-media_player"; +import "../../../src/dialogs/more-info/controls/more-info-person"; +import "../../../src/dialogs/more-info/controls/more-info-script"; +import "../../../src/dialogs/more-info/controls/more-info-sun"; +import "../../../src/dialogs/more-info/controls/more-info-timer"; +import "../../../src/dialogs/more-info/controls/more-info-vacuum"; +import "../../../src/dialogs/more-info/controls/more-info-water_heater"; +import "../../../src/dialogs/more-info/controls/more-info-weather"; +import { HomeAssistant } from "../../../src/types"; + +class MoreInfoContent extends UpdatingElement { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property() public stateObj?: HassEntity; + + private _detachedChild?: ChildNode; + + protected firstUpdated(): void { + this.style.position = "relative"; + this.style.display = "block"; + } + + // This is not a lit element, but an updating element, so we implement update + protected update(changedProps: PropertyValues): void { + super.update(changedProps); + const stateObj = this.stateObj; + const hass = this.hass; + + if (!stateObj || !hass) { + if (this.lastChild) { + this._detachedChild = this.lastChild; + // Detach child to prevent it from doing work. + this.removeChild(this.lastChild); + } + return; + } + + if (this._detachedChild) { + this.appendChild(this._detachedChild); + this._detachedChild = undefined; + } + + const moreInfoType = + stateObj.attributes && "custom_ui_more_info" in stateObj.attributes + ? stateObj.attributes.custom_ui_more_info + : "more-info-" + stateMoreInfoType(stateObj); + + dynamicContentUpdater(this, moreInfoType.toUpperCase(), { + hass, + stateObj, + }); + } +} + +customElements.define("more-info-content", MoreInfoContent); diff --git a/gallery/src/demos/demo-more-info-light.ts b/gallery/src/demos/demo-more-info-light.ts index 19677825d5..8c5ac611e9 100644 --- a/gallery/src/demos/demo-more-info-light.ts +++ b/gallery/src/demos/demo-more-info-light.ts @@ -3,10 +3,10 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; import { PolymerElement } from "@polymer/polymer/polymer-element"; import "../../../src/components/ha-card"; import { SUPPORT_BRIGHTNESS } from "../../../src/data/light"; -import "../../../src/dialogs/more-info/more-info-content"; import { getEntity } from "../../../src/fake_data/entity"; import { provideHass } from "../../../src/fake_data/provide_hass"; import "../components/demo-more-infos"; +import "../components/more-info-content"; const ENTITIES = [ getEntity("light", "bed_light", "on", { diff --git a/src/common/const.ts b/src/common/const.ts index dc2bf20595..640c4fb13a 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -44,7 +44,6 @@ export const DOMAINS_WITH_MORE_INFO = [ "script", "sun", "timer", - "updater", "vacuum", "water_heater", "weather", diff --git a/src/common/util/throttle.ts b/src/common/util/throttle.ts new file mode 100644 index 0000000000..1cd98ea188 --- /dev/null +++ b/src/common/util/throttle.ts @@ -0,0 +1,50 @@ +// From: underscore.js https://github.com/jashkenas/underscore/blob/master/underscore.js + +// Returns a function, that, when invoked, will only be triggered at most once +// during a given window of time. Normally, the throttled function will run +// as much as it can, without ever going more than once per `wait` duration; +// but if you'd like to disable the execution on the leading edge, pass +// `false for leading`. To disable execution on the trailing edge, ditto. +export const throttle = ( + func: T, + wait: number, + leading = true, + trailing = true +): T => { + let timeout: number | undefined; + let previous = 0; + let context: any; + let args: any; + const later = () => { + previous = leading === false ? 0 : Date.now(); + timeout = undefined; + func.apply(context, args); + if (!timeout) { + context = null; + args = null; + } + }; + // @ts-ignore + return function (...argmnts) { + // @ts-ignore + // @typescript-eslint/no-this-alias + context = this; + args = argmnts; + + const now = Date.now(); + if (!previous && leading === false) { + previous = now; + } + const remaining = wait - (now - previous); + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + } + previous = now; + func.apply(context, args); + } else if (!timeout && trailing !== false) { + timeout = window.setTimeout(later, remaining); + } + }; +}; diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index f5beadea90..bb217a732c 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -13,10 +13,15 @@ import { } from "lit-element"; import { cache } from "lit-html/directives/cache"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; -import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const"; +import { + DOMAINS_MORE_INFO_NO_HISTORY, + DOMAINS_WITH_MORE_INFO, +} from "../../common/const"; +import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; +import { stateMoreInfoType } from "../../common/entity/state_more_info_type"; import { navigate } from "../../common/navigate"; import "../../components/ha-dialog"; import "../../components/ha-header-bar"; @@ -30,21 +35,38 @@ import "../../state-summary/state-card-content"; import { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; import "./ha-more-info-history"; -import "./more-info-content"; +import "./ha-more-info-logbook"; const DOMAINS_NO_INFO = ["camera", "configurator"]; -const CONTROL_DOMAINS = [ - "light", - "media_player", - "vacuum", - "alarm_control_panel", - "climate", - "humidifier", - "weather", -]; const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"]; const EDITABLE_DOMAINS = ["script"]; +const MORE_INFO_CONTROL_IMPORT = { + alarm_control_panel: () => import("./controls/more-info-alarm_control_panel"), + automation: () => import("./controls/more-info-automation"), + camera: () => import("./controls/more-info-camera"), + climate: () => import("./controls/more-info-climate"), + configurator: () => import("./controls/more-info-configurator"), + counter: () => import("./controls/more-info-counter"), + cover: () => import("./controls/more-info-cover"), + fan: () => import("./controls/more-info-fan"), + group: () => import("./controls/more-info-group"), + humidifier: () => import("./controls/more-info-humidifier"), + input_datetime: () => import("./controls/more-info-input_datetime"), + light: () => import("./controls/more-info-light"), + lock: () => import("./controls/more-info-lock"), + media_player: () => import("./controls/more-info-media_player"), + person: () => import("./controls/more-info-person"), + script: () => import("./controls/more-info-script"), + sun: () => import("./controls/more-info-sun"), + timer: () => import("./controls/more-info-timer"), + vacuum: () => import("./controls/more-info-vacuum"), + water_heater: () => import("./controls/more-info-water_heater"), + weather: () => import("./controls/more-info-weather"), + hidden: () => {}, + default: () => import("./controls/more-info-default"), +}; + export interface MoreInfoDialogParams { entityId: string | null; } @@ -57,6 +79,8 @@ export class MoreInfoDialog extends LitElement { @internalProperty() private _entityId?: string | null; + @internalProperty() private _moreInfoType?: string; + @internalProperty() private _currTabIndex = 0; public showDialog(params: MoreInfoDialogParams) { @@ -73,6 +97,23 @@ export class MoreInfoDialog extends LitElement { fireEvent(this, "dialog-closed", { dialog: this.localName }); } + protected updated(changedProperties) { + if (!this.hass || !this._entityId || !changedProperties.has("_entityId")) { + return; + } + const stateObj = this.hass.states[this._entityId]; + if (!stateObj) { + return; + } + if (stateObj.attributes && "custom_ui_more_info" in stateObj.attributes) { + this._moreInfoType = stateObj.attributes.custom_ui_more_info; + } else { + const type = stateMoreInfoType(stateObj); + this._moreInfoType = `more-info-${type}`; + MORE_INFO_CONTROL_IMPORT[type](); + } + } + protected render() { if (!this._entityId) { return html``; @@ -137,7 +178,7 @@ export class MoreInfoDialog extends LitElement { ` : ""} - ${CONTROL_DOMAINS.includes(domain) && + ${DOMAINS_WITH_MORE_INFO.includes(domain) && this._computeShowHistoryComponent(entityId) ? html` `} - - ${CONTROL_DOMAINS.includes(domain) || + ${DOMAINS_WITH_MORE_INFO.includes(domain) || !this._computeShowHistoryComponent(entityId) ? "" : html``} + .hass=${this.hass} + .entityId=${this._entityId} + > + `} + ${this._moreInfoType + ? dynamicElement(this._moreInfoType, { + hass: this.hass, + stateObj, + }) + : ""} ${stateObj.attributes.restored ? html`

    @@ -210,6 +257,10 @@ export class MoreInfoDialog extends LitElement { .hass=${this.hass} .entityId=${this._entityId} > + ` )}

    diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 746bbb4c82..1dc70b68d4 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -9,14 +9,11 @@ import { TemplateResult, } from "lit-element"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; -import { computeStateDomain } from "../../common/entity/compute_state_domain"; -import "../../components/ha-circular-progress"; +import { throttle } from "../../common/util/throttle"; import "../../components/state-history-charts"; import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; -import { getLogbookData, LogbookEntry } from "../../data/logbook"; -import "../../panels/logbook/ha-logbook"; -import { haStyle, haStyleScrollbar } from "../../resources/styles"; +import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @customElement("ha-more-info-history") @@ -27,21 +24,14 @@ export class MoreInfoHistory extends LitElement { @internalProperty() private _stateHistory?: HistoryResult; - @internalProperty() private _entries?: LogbookEntry[]; - - @internalProperty() private _persons = {}; - - private _historyRefreshInterval?: number; + private _throttleGetStateHistory = throttle(() => { + this._getStateHistory(); + }, 10000); protected render(): TemplateResult { if (!this.entityId) { return html``; } - const stateObj = this.hass.states[this.entityId]; - - if (!stateObj) { - return html``; - } return html`${isComponentLoaded(this.hass, "history") ? html`` - : ""} - ${isComponentLoaded(this.hass, "logbook") - ? !this._entries - ? html` - - ` - : this._entries.length - ? html` - - ` - : html`
    - ${this.hass.localize("ui.components.logbook.entries_not_found")} -
    ` : ""} `; } - protected firstUpdated(): void { - this._fetchPersonNames(); - } - protected updated(changedProps: PropertyValues): void { super.updated(changedProps); - if (!this.entityId) { - clearInterval(this._historyRefreshInterval); - } if (changedProps.has("entityId")) { this._stateHistory = undefined; - this._entries = undefined; - this._getStateHistory(); - this._getLogBookData(); + if (!this.entityId) { + return; + } - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = window.setInterval(() => { - this._getStateHistory(); - }, 60 * 1000); + this._throttleGetStateHistory(); + return; + } + + if (!this.entityId || !changedProps.has("hass")) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + + if ( + oldHass && + this.hass.states[this.entityId] !== oldHass?.states[this.entityId] + ) { + // wait for commit of data (we only account for the default setting of 1 sec) + setTimeout(this._throttleGetStateHistory, 1000); } } @@ -118,55 +89,14 @@ export class MoreInfoHistory extends LitElement { ); } - private async _getLogBookData() { - if (!isComponentLoaded(this.hass, "logbook")) { - return; - } - const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); - const now = new Date(); - this._entries = await getLogbookData( - this.hass, - yesterday.toISOString(), - now.toISOString(), - this.entityId, - true - ); - } - - private _fetchPersonNames() { - Object.values(this.hass.states).forEach((entity) => { - if ( - entity.attributes.user_id && - computeStateDomain(entity) === "person" - ) { - this._persons[entity.attributes.user_id] = - entity.attributes.friendly_name; - } - }); - } - static get styles() { return [ haStyle, - haStyleScrollbar, css` state-history-charts { display: block; margin-bottom: 16px; } - .no-entries { - text-align: center; - padding: 16px; - color: var(--secondary-text-color); - } - ha-logbook { - max-height: 250px; - overflow: auto; - } - ha-circular-progress { - display: flex; - justify-content: center; - } `, ]; } diff --git a/src/dialogs/more-info/ha-more-info-logbook.ts b/src/dialogs/more-info/ha-more-info-logbook.ts new file mode 100644 index 0000000000..a3258133f2 --- /dev/null +++ b/src/dialogs/more-info/ha-more-info-logbook.ts @@ -0,0 +1,171 @@ +import { + css, + customElement, + html, + internalProperty, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; +import { isComponentLoaded } from "../../common/config/is_component_loaded"; +import { computeStateDomain } from "../../common/entity/compute_state_domain"; +import { throttle } from "../../common/util/throttle"; +import "../../components/ha-circular-progress"; +import "../../components/state-history-charts"; +import { getLogbookData, LogbookEntry } from "../../data/logbook"; +import "../../panels/logbook/ha-logbook"; +import { haStyle, haStyleScrollbar } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-more-info-logbook") +export class MoreInfoLogbook extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public entityId!: string; + + @internalProperty() private _logbookEntries?: LogbookEntry[]; + + @internalProperty() private _persons = {}; + + private _lastLogbookDate?: Date; + + private _throttleGetLogbookEntries = throttle(() => { + this._getLogBookData(); + }, 10000); + + protected render(): TemplateResult { + if (!this.entityId) { + return html``; + } + const stateObj = this.hass.states[this.entityId]; + + if (!stateObj) { + return html``; + } + + return html` + ${isComponentLoaded(this.hass, "logbook") + ? !this._logbookEntries + ? html` + + ` + : this._logbookEntries.length + ? html` + + ` + : html`
    + ${this.hass.localize("ui.components.logbook.entries_not_found")} +
    ` + : ""} + `; + } + + protected firstUpdated(): void { + this._fetchPersonNames(); + } + + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + if (changedProps.has("entityId")) { + this._lastLogbookDate = undefined; + this._logbookEntries = undefined; + + if (!this.entityId) { + return; + } + + this._throttleGetLogbookEntries(); + return; + } + + if (!this.entityId || !changedProps.has("hass")) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + + if ( + oldHass && + this.hass.states[this.entityId] !== oldHass?.states[this.entityId] + ) { + // wait for commit of data (we only account for the default setting of 1 sec) + setTimeout(this._throttleGetLogbookEntries, 1000); + } + } + + private async _getLogBookData() { + if (!isComponentLoaded(this.hass, "logbook")) { + return; + } + const lastDate = + this._lastLogbookDate || + new Date(new Date().getTime() - 24 * 60 * 60 * 1000); + const now = new Date(); + const newEntries = await getLogbookData( + this.hass, + lastDate.toISOString(), + now.toISOString(), + this.entityId, + true + ); + this._logbookEntries = this._logbookEntries + ? [...newEntries, ...this._logbookEntries] + : newEntries; + this._lastLogbookDate = now; + } + + private _fetchPersonNames() { + Object.values(this.hass.states).forEach((entity) => { + if ( + entity.attributes.user_id && + computeStateDomain(entity) === "person" + ) { + this._persons[entity.attributes.user_id] = + entity.attributes.friendly_name; + } + }); + } + + static get styles() { + return [ + haStyle, + haStyleScrollbar, + css` + .no-entries { + text-align: center; + padding: 16px; + color: var(--secondary-text-color); + } + ha-logbook { + max-height: 250px; + overflow: auto; + display: block; + margin-top: 16px; + } + ha-circular-progress { + display: flex; + justify-content: center; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-logbook": MoreInfoLogbook; + } +} diff --git a/src/dialogs/more-info/more-info-content.ts b/src/dialogs/more-info/more-info-content.ts deleted file mode 100644 index 369d9e8d4f..0000000000 --- a/src/dialogs/more-info/more-info-content.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { HassEntity } from "home-assistant-js-websocket"; -import { property, PropertyValues, UpdatingElement } from "lit-element"; -import dynamicContentUpdater from "../../common/dom/dynamic_content_updater"; -import { stateMoreInfoType } from "../../common/entity/state_more_info_type"; -import { HomeAssistant } from "../../types"; -import "./controls/more-info-alarm_control_panel"; -import "./controls/more-info-automation"; -import "./controls/more-info-camera"; -import "./controls/more-info-climate"; -import "./controls/more-info-configurator"; -import "./controls/more-info-counter"; -import "./controls/more-info-cover"; -import "./controls/more-info-default"; -import "./controls/more-info-fan"; -import "./controls/more-info-group"; -import "./controls/more-info-humidifier"; -import "./controls/more-info-input_datetime"; -import "./controls/more-info-light"; -import "./controls/more-info-lock"; -import "./controls/more-info-media_player"; -import "./controls/more-info-person"; -import "./controls/more-info-script"; -import "./controls/more-info-sun"; -import "./controls/more-info-timer"; -import "./controls/more-info-vacuum"; -import "./controls/more-info-water_heater"; -import "./controls/more-info-weather"; - -class MoreInfoContent extends UpdatingElement { - @property({ attribute: false }) public hass?: HomeAssistant; - - @property() public stateObj?: HassEntity; - - private _detachedChild?: ChildNode; - - protected firstUpdated(): void { - this.style.position = "relative"; - this.style.display = "block"; - } - - // This is not a lit element, but an updating element, so we implement update - protected update(changedProps: PropertyValues): void { - super.update(changedProps); - const stateObj = this.stateObj; - const hass = this.hass; - - if (!stateObj || !hass) { - if (this.lastChild) { - this._detachedChild = this.lastChild; - // Detach child to prevent it from doing work. - this.removeChild(this.lastChild); - } - return; - } - - if (this._detachedChild) { - this.appendChild(this._detachedChild); - this._detachedChild = undefined; - } - - const moreInfoType = - stateObj.attributes && "custom_ui_more_info" in stateObj.attributes - ? stateObj.attributes.custom_ui_more_info - : "more-info-" + stateMoreInfoType(stateObj); - - dynamicContentUpdater(this, moreInfoType.toUpperCase(), { - hass, - stateObj, - }); - } -} - -customElements.define("more-info-content", MoreInfoContent); From 5480e5418559e574295877ea0f548cb2e9f84e4b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 20:07:22 +0200 Subject: [PATCH 111/146] Mute stream outside of more info (#6959) --- src/components/ha-camera-stream.ts | 10 +++++++--- src/dialogs/more-info/controls/more-info-camera.ts | 2 +- src/panels/config/users/ha-config-users.ts | 9 +++++---- src/panels/lovelace/components/hui-image.ts | 7 ++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index b2db579890..7b091055e0 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -26,7 +26,11 @@ class HaCameraStream extends LitElement { @property({ attribute: false }) public stateObj?: CameraEntity; - @property({ type: Boolean }) public showControls = false; + @property({ type: Boolean, attribute: "controls" }) + public controls = false; + + @property({ type: Boolean, attribute: "muted" }) + public muted = false; // We keep track if we should force MJPEG with a string // that way it automatically resets if we change entity. @@ -56,9 +60,9 @@ class HaCameraStream extends LitElement { ? html` diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index 4bc02b6a79..ffd315d9dd 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -48,7 +48,7 @@ class MoreInfoCamera extends LitElement { ${this._cameraPrefs ? html` diff --git a/src/panels/config/users/ha-config-users.ts b/src/panels/config/users/ha-config-users.ts index 2e9432b8d1..30ed2e8093 100644 --- a/src/panels/config/users/ha-config-users.ts +++ b/src/panels/config/users/ha-config-users.ts @@ -1,3 +1,5 @@ +import "@material/mwc-fab"; +import { mdiPlus } from "@mdi/js"; import { customElement, LitElement, @@ -11,7 +13,7 @@ import { DataTableColumnContainer, RowClickedEvent, } from "../../../components/data-table/ha-data-table"; -import "@material/mwc-fab"; +import "../../../components/ha-svg-icon"; import { deleteUser, fetchUsers, updateUser, User } from "../../../data/user"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-tabs-subpage-data-table"; @@ -19,8 +21,6 @@ import { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; import { showAddUserDialog } from "./show-dialog-add-user"; import { showUserDetailDialog } from "./show-dialog-user-detail"; -import "../../../components/ha-svg-icon"; -import { mdiPlus } from "@mdi/js"; @customElement("ha-config-users") export class HaConfigUsers extends LitElement { @@ -56,7 +56,7 @@ export class HaConfigUsers extends LitElement { ), sortable: true, filterable: true, - width: "25%", + width: "30%", template: (groupIds) => html` ${this.hass.localize(`groups.${groupIds[0]}`)} `, @@ -66,6 +66,7 @@ export class HaConfigUsers extends LitElement { "ui.panel.config.users.picker.headers.system" ), type: "icon", + width: "80px", sortable: true, filterable: true, template: (generated) => html` diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 004bf705ff..f9133cc612 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, query, TemplateResult, @@ -16,8 +16,8 @@ import { STATES_OFF } from "../../../common/const"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import "../../../components/ha-camera-stream"; import { fetchThumbnailUrlWithCache } from "../../../data/camera"; -import { CameraEntity, HomeAssistant } from "../../../types"; import { UNAVAILABLE } from "../../../data/entity"; +import { CameraEntity, HomeAssistant } from "../../../types"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; @@ -132,8 +132,9 @@ export class HuiImage extends LitElement { ${this.cameraImage && this.cameraView === "live" ? html` ` : html` From c1afed7f98f6c0722157540d78e7fbafbf98b1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 20:34:34 +0200 Subject: [PATCH 112/146] Sort media sources (#6960) Co-authored-by: Bram Kragten --- .../hui-dialog-select-media-player.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/panels/media-browser/hui-dialog-select-media-player.ts b/src/panels/media-browser/hui-dialog-select-media-player.ts index d19cebe0ee..03b53b6a2d 100644 --- a/src/panels/media-browser/hui-dialog-select-media-player.ts +++ b/src/panels/media-browser/hui-dialog-select-media-player.ts @@ -10,10 +10,12 @@ import { TemplateResult, } from "lit-element"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeStateName } from "../../common/entity/compute_state_name"; +import { compare } from "../../common/string/compare"; import { createCloseHeading } from "../../components/ha-dialog"; import { BROWSER_SOURCE } from "../../data/media-player"; -import type { HomeAssistant } from "../../types"; import { haStyleDialog } from "../../resources/styles"; +import type { HomeAssistant } from "../../types"; import type { SelectMediaPlayerDialogParams } from "./show-select-media-source-dialog"; @customElement("hui-dialog-select-media-player") @@ -55,13 +57,15 @@ export class HuiDialogSelectMediaPlayer extends LitElement { "ui.components.media-browser.web-browser" )} - ${this._params.mediaSources.map( - (source) => html` - ${source.attributes.friendly_name} - ` - )} + ${this._params.mediaSources + .sort((a, b) => compare(computeStateName(a), computeStateName(b))) + .map( + (source) => html` + ${computeStateName(source)} + ` + )} `; From 857e4e49d8a32a211e3dc9396ca2294a0442fc2a Mon Sep 17 00:00:00 2001 From: Ludeeus Date: Sat, 12 Sep 2020 18:58:49 +0000 Subject: [PATCH 113/146] Bumped version to 20200912.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f666c7161d..7ee304d284 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200909.0", + version="20200912.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From af0246cd27b414a78eeeb5e82cfab67dc9a216d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 21:05:01 +0200 Subject: [PATCH 114/146] convert ha-refresh-tokens-card (#6962) --- .../ha-long-lived-access-tokens-card.ts | 18 ++- src/panels/profile/ha-refresh-tokens-card.js | 126 --------------- src/panels/profile/ha-refresh-tokens-card.ts | 150 ++++++++++++++++++ 3 files changed, 161 insertions(+), 133 deletions(-) delete mode 100644 src/panels/profile/ha-refresh-tokens-card.js create mode 100644 src/panels/profile/ha-refresh-tokens-card.ts diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.ts b/src/panels/profile/ha-long-lived-access-tokens-card.ts index 8c8eb36d96..a12c1a7a20 100644 --- a/src/panels/profile/ha-long-lived-access-tokens-card.ts +++ b/src/panels/profile/ha-long-lived-access-tokens-card.ts @@ -1,4 +1,5 @@ -import "@material/mwc-button"; +import "@material/mwc-button/mwc-button"; +import { mdiDelete } from "@mdi/js"; import { css, CSSResultArray, @@ -12,8 +13,8 @@ import memoizeOne from "memoize-one"; import relativeTime from "../../common/datetime/relative_time"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-card"; -import "../../components/ha-icon-button"; import "../../components/ha-settings-row"; +import "../../components/ha-svg-icon"; import { RefreshToken } from "../../data/refresh_token"; import { showAlertDialog, @@ -79,11 +80,14 @@ class HaLongLivedTokens extends LitElement { ) )}
    - + > + + ` )}
    @@ -178,8 +182,8 @@ class HaLongLivedTokens extends LitElement { a { color: var(--primary-color); } - ha-icon-button { - color: var(--primary-text-color); + mwc-button { + --mdc-theme-primary: var(--primary-color); } `, ]; diff --git a/src/panels/profile/ha-refresh-tokens-card.js b/src/panels/profile/ha-refresh-tokens-card.js deleted file mode 100644 index 9c4db68a7a..0000000000 --- a/src/panels/profile/ha-refresh-tokens-card.js +++ /dev/null @@ -1,126 +0,0 @@ -import "@polymer/paper-tooltip/paper-tooltip"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatDateTime } from "../../common/datetime/format_date_time"; -import "../../components/ha-card"; -import "../../components/ha-icon-button"; -import "../../components/ha-settings-row"; -import { - showAlertDialog, - showConfirmationDialog, -} from "../../dialogs/generic/show-dialog-box"; -import { EventsMixin } from "../../mixins/events-mixin"; -import LocalizeMixin from "../../mixins/localize-mixin"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -class HaRefreshTokens extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - -
    - [[localize('ui.panel.profile.refresh_tokens.description')]] -
    - -
    - `; - } - - static get properties() { - return { - hass: Object, - refreshTokens: Array, - }; - } - - _computeTokens(refreshTokens) { - return refreshTokens.filter((tkn) => tkn.type === "normal").reverse(); - } - - _formatTitle(clientId) { - return this.localize( - "ui.panel.profile.refresh_tokens.token_title", - "clientId", - clientId - ); - } - - _formatCreatedAt(created) { - return this.localize( - "ui.panel.profile.refresh_tokens.created_at", - "date", - formatDateTime(new Date(created), this.hass.language) - ); - } - - _formatLastUsed(item) { - return item.last_used_at - ? this.localize( - "ui.panel.profile.refresh_tokens.last_used", - "date", - formatDateTime(new Date(item.last_used_at), this.hass.language), - "location", - item.last_used_ip - ) - : this.localize("ui.panel.profile.refresh_tokens.not_used"); - } - - async _handleDelete(ev) { - const token = ev.model.item; - if ( - !(await showConfirmationDialog(this, { - text: this.localize( - "ui.panel.profile.refresh_tokens.confirm_delete", - "name", - token.client_id - ), - })) - ) { - return; - } - try { - await this.hass.callWS({ - type: "auth/delete_refresh_token", - refresh_token_id: token.id, - }); - this.fire("hass-refresh-tokens"); - } catch (err) { - // eslint-disable-next-line - console.error(err); - showAlertDialog(this, { - text: this.localize("ui.panel.profile.refresh_tokens.delete_failed"), - }); - } - } -} - -customElements.define("ha-refresh-tokens-card", HaRefreshTokens); diff --git a/src/panels/profile/ha-refresh-tokens-card.ts b/src/panels/profile/ha-refresh-tokens-card.ts new file mode 100644 index 0000000000..fb8ab60587 --- /dev/null +++ b/src/panels/profile/ha-refresh-tokens-card.ts @@ -0,0 +1,150 @@ +import "@material/mwc-button/mwc-button"; +import { mdiDelete } from "@mdi/js"; +import "@polymer/paper-tooltip/paper-tooltip"; +import { + css, + CSSResultArray, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import relativeTime from "../../common/datetime/relative_time"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-card"; +import "../../components/ha-settings-row"; +import "../../components/ha-svg-icon"; +import { RefreshToken } from "../../data/refresh_token"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../dialogs/generic/show-dialog-box"; +import { haStyle } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-refresh-tokens-card") +class HaRefreshTokens extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public refreshTokens?: RefreshToken[]; + + private _refreshTokens = memoizeOne( + (refreshTokens: RefreshToken[]): RefreshToken[] => + refreshTokens?.filter((token) => token.type === "normal").reverse() + ); + + protected render(): TemplateResult { + const refreshTokens = this._refreshTokens(this.refreshTokens!); + return html` +
    + ${this.hass.localize("ui.panel.profile.refresh_tokens.description")} + ${refreshTokens?.length + ? refreshTokens!.map( + (token) => html` + ${this.hass.localize( + "ui.panel.profile.refresh_tokens.token_title", + "clientId", + token.client_id + )} + +
    + ${this.hass.localize( + "ui.panel.profile.refresh_tokens.created_at", + "date", + relativeTime(new Date(token.created_at), this.hass.localize) + )} +
    +
    + ${token.last_used_at + ? this.hass.localize( + "ui.panel.profile.refresh_tokens.last_used", + "date", + relativeTime( + new Date(token.last_used_at), + this.hass.localize + ), + "location", + token.last_used_ip + ) + : this.hass.localize( + "ui.panel.profile.refresh_tokens.not_used" + )} +
    +
    + ${token.is_current + ? html` + ${this.hass.localize( + "ui.panel.profile.refresh_tokens.current_token_tooltip" + )} + ` + : ""} + + + +
    +
    ` + ) + : ""} +
    +
    `; + } + + private async _deleteToken(ev: Event): Promise { + const token = (ev.currentTarget as any).token; + if ( + !(await showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.profile.refresh_tokens.confirm_delete", + "name", + token.client_name + ), + })) + ) { + return; + } + try { + await this.hass.callWS({ + type: "auth/delete_refresh_token", + refresh_token_id: token.id, + }); + fireEvent(this, "hass-refresh-tokens"); + } catch (err) { + await showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.profile.refresh_tokens.delete_failed" + ), + text: err.message, + }); + } + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + ha-settings-row { + padding: 0; + } + mwc-button { + --mdc-theme-primary: var(--primary-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-refresh-tokens-card": HaRefreshTokens; + } +} From 50d37ce4f6f9b48265abc262d17c56c25e750ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 21:25:40 +0200 Subject: [PATCH 115/146] Remove icon slot (#6964) --- src/components/media-player/ha-media-player-browse.ts | 1 - src/panels/profile/ha-long-lived-access-tokens-card.ts | 2 +- src/panels/profile/ha-refresh-tokens-card.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 7bf868254f..0b14444536 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -206,7 +206,6 @@ export class HaMediaPlayerBrowse extends LitElement { @click=${this._actionClicked} > - + ` )} diff --git a/src/panels/profile/ha-refresh-tokens-card.ts b/src/panels/profile/ha-refresh-tokens-card.ts index fb8ab60587..920d69e3db 100644 --- a/src/panels/profile/ha-refresh-tokens-card.ts +++ b/src/panels/profile/ha-refresh-tokens-card.ts @@ -89,7 +89,7 @@ class HaRefreshTokens extends LitElement { .title=${this.hass.localize(`ui.common.delete`)} @click=${this._deleteToken} > - +
    ` From 6b88081360eff09f79883c793cfd904197bf2f02 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Sun, 13 Sep 2020 00:32:53 +0000 Subject: [PATCH 116/146] [ci skip] Translation update --- translations/frontend/bg.json | 111 +++++++++++++++++++++++++++-- translations/frontend/ca.json | 15 +++- translations/frontend/en.json | 22 ++++++ translations/frontend/ru.json | 8 +++ translations/frontend/zh-Hant.json | 17 ++++- 5 files changed, 165 insertions(+), 8 deletions(-) diff --git a/translations/frontend/bg.json b/translations/frontend/bg.json index f5703b28f4..68302a4647 100644 --- a/translations/frontend/bg.json +++ b/translations/frontend/bg.json @@ -19,6 +19,7 @@ "logbook": "Дневник", "mailbox": "Пощенска кутия", "map": "Карта", + "media_browser": "Медиен браузър", "profile": "Профил", "shopping_list": "Списък за пазаруване", "states": "Състояние" @@ -455,6 +456,7 @@ }, "common": { "cancel": "Отмени", + "continue": "Продължи", "delete": "Изтриване", "loading": "Зареждане", "save": "Запазване", @@ -466,6 +468,10 @@ "device": "Устройство" }, "entity": { + "entity-attribute-picker": { + "attribute": "Атрибут", + "show_attributes": "Показване на атрибутите" + }, "entity-picker": { "entity": "Обект" } @@ -474,6 +480,18 @@ "loading_history": "Зареждане на история за състоянието...", "no_history_found": "Не е намерена история за състоянието" }, + "logbook": { + "entries_not_found": "Липсват логове в дневника" + }, + "media-browser": { + "audio_not_supported": "Браузърът не поддържа този аудио елемент.", + "choose_player": "Изберете плейър", + "media_browsing_error": "Грешка в медийния браузър", + "media_not_supported": "Плейърът на медийния браузър не поддържа този тип медия", + "media_player": "Медиен плейър", + "video_not_supported": "Браузърът не поддържа този видео елемент.", + "web-browser": "Уеб браузър" + }, "relative_time": { "duration": { "day": "{count}{count, plural,\n one {ден}\n other {дни}\n}", @@ -496,6 +514,9 @@ "enable_new_entities_label": "Активирай новодобавените устройства.", "title": "Системни опции за {integration}" }, + "domain_toggler": { + "reset_entities": "Нулиране на обекти" + }, "entity_registry": { "editor": { "delete": "Изтриване", @@ -515,6 +536,9 @@ "yaml_not_editable": "Настройките на този обект не могат да бъдат редактирани. Могат да се конфигурират само обектите, създадени от потребителския интерфейс." }, "more_info_control": { + "controls": "Контроли", + "details": "Детайли", + "history": "История", "script": { "last_action": "Последно задействане" }, @@ -644,7 +668,13 @@ "label": "Извикване на услуга", "service_data": "Данни за услугата" }, + "wait_for_trigger": { + "continue_timeout": "Продължи след изчакване", + "label": "Изчакайте спусък/тригър", + "timeout": "Време на изчакване (опция)" + }, "wait_template": { + "continue_timeout": "Продължи след изчакване", "label": "Изчакване", "timeout": "Изчакване (по избор)", "wait_template": "Шаблон за изчакване" @@ -692,7 +722,9 @@ "time": { "after": "След", "before": "Преди", - "label": "Време" + "label": "Време", + "type_input": "Стойност на помощника за дата/час", + "type_value": "Фиксирано време" }, "zone": { "entity": "Обект с местоположение", @@ -751,6 +783,7 @@ "value_template": "Шаблон за стойност (незадължително)" }, "state": { + "attribute": "Атрибут (опция)", "for": "За период от", "from": "От", "label": "Състояние", @@ -775,7 +808,9 @@ }, "time": { "at": "В", - "label": "Време" + "label": "Време", + "type_input": "Стойност на помощника за дата/час", + "type_value": "Фиксирано време" }, "webhook": { "label": "Webhook", @@ -796,6 +831,8 @@ }, "picker": { "add_automation": "Добавяне на автоматизация", + "duplicate": "Дублиране", + "duplicate_automation": "Дублиране на автоматизация", "header": "Редактор на автоматизации", "headers": { "name": "Име" @@ -815,10 +852,26 @@ "config_documentation": "Документация за конфигурацията" } }, + "alexa": { + "dont_expose_entity": "Не излагай обекта", + "expose_entity": "Изложи обекта", + "exposed": "{избран} изложен", + "follow_domain": "следван домейн", + "manage_domains": "Управление на домейни", + "not_exposed": "{избран} неизложен" + }, "caption": "Home Assistant Cloud", "description_features": "Контролирайте дома си, и когато не сте вкъщи, активирайте интегрирациите с Alexa и Google Assistant.", "description_login": "Влезли сте като {email}", - "description_not_login": "Не сте влезли" + "description_not_login": "Не сте влезли", + "google": { + "dont_expose_entity": "Не излагай обекта", + "expose_entity": "Изложи обекта", + "exposed": "{избран} изложен", + "follow_domain": "следван домейн", + "manage_domains": "Управление на домейни", + "not_exposed": "{избран} неизложен" + } }, "core": { "caption": "Общи", @@ -882,6 +935,7 @@ }, "integrations": { "add_integration": "Добавяне на интеграция", + "attention": "Задълителна проверка", "caption": "Интеграции", "config_entry": { "delete": "Изтриване", @@ -897,6 +951,7 @@ "options": "Настройки", "rename": "Преименуване", "restart_confirm": "Рестартирайте Home Assistant за да завършите премахването на интеграцията", + "services": "{брой} {число, множествено число,\n една {услуга}\n други {услуги}\n}", "system_options": "Системни настройки" }, "config_flow": { @@ -915,6 +970,7 @@ "none": "Нищо не е конфигурирано към момента", "none_found": "Не са намерени интеграции", "none_found_detail": "Коригирайте критериите си за търсене.", + "reconfigure": "Преконфигурирай", "rename_dialog": "Редактирайте името на този запис в конфигурацията" }, "introduction": "Тук е възможно да конфигурирате Вашите компоненти и Home Assistant. Не всичко е възможно да се конфигурира от Интерфейса, но работим по върпоса.", @@ -942,6 +998,23 @@ "mqtt": { "title": "MQTT" }, + "ozw": { + "node": { + "button": "Детайли за възела", + "not_found": "Възел не е намерен" + }, + "nodes_table": { + "failed": "Неуспешно", + "id": "ID", + "manufacturer": "Производител", + "model": "Модел", + "query_stage": "Етап на заявка", + "zwave_plus": "Z-Wave плюс" + }, + "refresh_node": { + "button": "Обнови възела" + } + }, "person": { "caption": "Хора", "description": "Управлявайте хората, които следите от Home Assistant.", @@ -979,8 +1052,13 @@ "group": "Презареждане на гурпите", "heading": "Презареждане на YAML конфигурацията", "introduction": "Някои части от Home Assistant могат да се презаредят без да е необходимо рестартиране. Натискането на Презареди ще отхвърли настоящата конфигурация и ще зареди новата конфигурация.", + "mqtt": "Презареди mqtt обектите", + "reload": "Презарежди {домейн}", + "rpi_gpio": "Презареди GPIO обектите на Raspberry Pi ", "scene": "Презареди сцените", - "script": "Презареждане на скриптовете" + "script": "Презареждане на скриптовете", + "smtp": "Презареди smtp услугите за уведомяване", + "telegram": "Презареди telegram услугите за уведомяване" }, "server_management": { "confirm_restart": "Сигурни ли сте, че искате да рестартирате Home Assistant?", @@ -1005,6 +1083,8 @@ "create": "Създаване", "name": "Име", "password": "Парола", + "password_confirm": "Потвърди парола", + "password_not_match": "Паролите не съвпадат", "username": "Потребителско име" }, "caption": "Потребители", @@ -1017,6 +1097,8 @@ "deactivate_user": "Деактивиране на потребителя", "delete_user": "Изтриване на потребител", "name": "Име", + "new_password": "Нова парола", + "password_changed": "Паролата е променена!", "system_generated_users_not_editable": "Неуспешно обновяване на системно генерираните потребители" }, "picker": { @@ -1102,6 +1184,11 @@ "title": "Състояния" }, "templates": { + "all_listeners": "Този шаблон следи за всички събития на промяна на състояние.", + "domain": "Домейн", + "entity": "Обект", + "listeners": "Този шаблон следи за събития със следните промени на състояния:", + "no_listeners": "Този шаблон не следи за събития на промяна на състояние и не се актуализира автоматично.", "title": "Шаблон" } } @@ -1144,6 +1231,9 @@ "entities": { "toggle": "Превключване на обекти." }, + "generic": { + "state_color": "Да се оцветят ли иконите спрямо състоянието?" + }, "iframe": { "name": "Уеб страница" }, @@ -1155,6 +1245,10 @@ } }, "cardpicker": { + "by_card": "По карта", + "by_entity": "По обект", + "domain": "Домейн", + "entity": "Обект", "no_description": "Няма налично описание." }, "edit_card": { @@ -1166,6 +1260,7 @@ "move": "Преместване", "options": "Още опции", "pick_card": "Изберете картата, която искате да добавите.", + "search_cards": "Търсене на карти", "toggle_editor": "Превключете редактора" }, "edit_lovelace": { @@ -1419,6 +1514,11 @@ "submit": "Промяна" }, "current_user": "В момента сте влезли като {fullName}.", + "customize_sidebar": { + "button": "Редактиране", + "description": "Може също да се задържи водача на страничната лента, за да се активира режим за редактиране.", + "header": "Промяна на реда и скриване на елементи от страничната лента" + }, "dashboard": { "dropdown_label": "Табло", "header": "Табло" @@ -1438,6 +1538,7 @@ "confirm_delete": "Сигурни ли сте, че искате да изтриете кода за достъп за {name}?", "create": "Създай код", "create_failed": "Възникна грешка при създаването на код за достъп.", + "created": "Създаване {дата}", "created_at": "Създаден на {date}", "delete_failed": "Възникна грешка при изтриването на кода за достъп.", "description": "Създайте дългосрочни кодове за достъп за да могат скриптовете Ви да взаимодействат с Home Assistant. Всеки код е валиден за 10 години от създаването си. Следните дългосрочни кодове са активни в момента.", @@ -1445,6 +1546,7 @@ "header": "Дългосрочни кодове за достъп", "last_used": "Последно използван на {date} от {location}", "learn_auth_requests": "Научете се как да правите оторизирани заявки.", + "name": "Име", "not_used": "Никога не е бил използван", "prompt_copy_token": "Копирайте кода си за достъп. Той няма да бъде показан отново.", "prompt_name": "Име?" @@ -1495,6 +1597,7 @@ } }, "sidebar": { + "done": "Готово", "external_app_configuration": "Конфигурация на приложение" } } diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 54945cf396..9e89a2f943 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -1943,7 +1943,7 @@ }, "introduction": "L'editor de scripts et permet crear i editar scripts. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat Home Assistant correctament.", "learn_more": "Més informació sobre els scripts", - "no_scripts": "No hem trobat cap script editable", + "no_scripts": "No s'ha pogut trobar cap script editable", "show_info": "Mostra informació sobre l'script", "trigger_script": "Dispara l'script" } @@ -2352,9 +2352,14 @@ "title": "Estats" }, "templates": { + "all_listeners": "Aquesta plantilla escolta a tots els esdeveniments de canvi d'estat.", "description": "Les plantilles es renderitzen mitjançant el motor Jinja2 amb algunes extensions específiques de Home Assistant.", + "domain": "Domini", "editor": "Editor de plantilles", + "entity": "Entitat", "jinja_documentation": "Documentació sobre plantilles amb Jinja2", + "listeners": "Aquesta plantilla escolta als següents esdeveniments de canvi d'estat:", + "no_listeners": "Aquesta plantilla no escolta cap esdeveniment de canvi d'estat i no s'actualitza automàticament.", "reset": "Restableix a la plantilla de demostració", "template_extensions": "Extensions de plantilla de Home Assistant", "title": "Plantilla", @@ -2501,7 +2506,7 @@ "no_theme": "Sense tema", "refresh_interval": "Interval d'actualització", "search": "Cerca", - "secondary_info_attribute": "Atribut d’informació secundària", + "secondary_info_attribute": "Atribut d'informació secundària", "show_icon": "Mostra icona?", "show_name": "Mostra nom?", "show_state": "Mostra estat?", @@ -2941,6 +2946,11 @@ "submit": "Envia" }, "current_user": "Has iniciat la sessió com a {fullName}.", + "customize_sidebar": { + "button": "Edita", + "description": "També pots mantenir premuda la capçalera de la barra lateral per activar el mode d'edició.", + "header": "Canvia l'ordre i/o amaga elements de la barra lateral" + }, "dashboard": { "description": "Tria un panell per defecte per a aquest dispositiu.", "dropdown_label": "Panell", @@ -3038,6 +3048,7 @@ } }, "sidebar": { + "done": "Fet", "external_app_configuration": "Configuració de l'aplicació", "sidebar_toggle": "Commutació de la barra lateral" } diff --git a/translations/frontend/en.json b/translations/frontend/en.json index fcc53ba406..2074324131 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -574,6 +574,28 @@ "audio_not_supported": "Your browser does not support the audio element.", "choose_player": "Choose Player", "choose-source": "Choose Source", + "class": { + "album": "Album", + "app": "App", + "artist": "Artist", + "channel": "Channel", + "composer": "Composer", + "contributing_artist": "Contributing Artist", + "directory": "Library", + "episode": "Episode", + "game": "Game", + "genre": "Genre", + "image": "Image", + "movie": "Movie", + "music": "Music", + "playlist": "Playlist", + "podcast": "Podcast", + "season": "Season", + "track": "Track", + "tv_show": "TV Show", + "url": "Url", + "video": "Video" + }, "content-type": { "album": "Album", "artist": "Artist", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 739b8ccc63..30a5044abe 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -2353,7 +2353,9 @@ }, "templates": { "description": "Здесь Вы можете протестировать поведение шаблонов. В Home Assistant используется шаблонизатор Jinja2 с некоторыми специальными расширениями.", + "domain": "Домен", "editor": "Редактор шаблонов", + "entity": "Объект", "jinja_documentation": "Узнайте больше о шаблонизаторе Jinja2", "reset": "Вернуться к демонстрационному шаблону", "template_extensions": "Узнайте больше о шаблонах Home Assistant", @@ -2941,6 +2943,11 @@ "submit": "Подтвердить" }, "current_user": "Добро пожаловать, {fullName}! Вы вошли в систему.", + "customize_sidebar": { + "button": "Изменить", + "description": "Режим редактирования можно включить, нажав и удерживая заголовок боковой панели", + "header": "Изменить элементы боковой панели" + }, "dashboard": { "description": "Панель, используемая по умолчанию для этого устройства", "dropdown_label": "Панель", @@ -3038,6 +3045,7 @@ } }, "sidebar": { + "done": "Готово", "external_app_configuration": "Настройки приложения", "sidebar_toggle": "Переключатель в боковой панели" } diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 0fcc924359..5273e7c538 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -1982,7 +1982,7 @@ "smtp": "重新載入 SMTP 通知服務", "statistics": "重新載入統計資訊實體", "telegram": "重新載入 Telegram 通知服務", - "template": "重新載入範例實體", + "template": "重新載入模板實體", "trend": "重新載入趨勢實體", "universal": "重新載入通用媒體播放器實體", "zone": "重新載入區域" @@ -2352,9 +2352,14 @@ "title": "狀態" }, "templates": { + "all_listeners": "此模板監聽所有狀態變更事件。", "description": "模版使用 Jinja2 模板引擎及 Home Assistant 特殊擴充進行模板渲染。", + "domain": "區域", "editor": "模板編輯器", + "entity": "實體", "jinja_documentation": "Jinja2 模版文件", + "listeners": "此模板監聽以下狀態變更事件:", + "no_listeners": "此模板不監聽任何狀態變更事件,將不會自動更新。", "reset": "重置示範範模板", "template_extensions": "Home Assistant 模板擴充", "title": "模板", @@ -2941,6 +2946,11 @@ "submit": "傳送" }, "current_user": "目前登入身份:{fullName}。", + "customize_sidebar": { + "button": "編輯", + "description": "同時也能長按側邊列的標題以啟動編輯模式。", + "header": "自側邊列中變更順序或隱藏項目" + }, "dashboard": { "description": "選擇此設備的預設主面板。", "dropdown_label": "主面板", @@ -2963,6 +2973,7 @@ "confirm_delete": "確定要刪除{name}存取密鑰嗎?", "create": "創建密鑰", "create_failed": "創建存取密鑰失敗。", + "created": "新增日期:{date}", "created_at": "於{date}創建", "delete_failed": "刪除存取密鑰失敗。", "description": "創建長效存取密鑰,可供運用腳本與 Home Assistant 實體進行互動。每個密鑰於創建後,有效期為十年。目前已啟用之永久有效密鑰如下。", @@ -2970,9 +2981,10 @@ "header": "永久有效存取密鑰", "last_used": "上次使用:於{date}、位置{location}", "learn_auth_requests": "學習如何進行驗證請求。", + "name": "名稱", "not_used": "從未使用過", "prompt_copy_token": "複製存取密鑰,將不會再次顯示。", - "prompt_name": "名稱?" + "prompt_name": "為密鑰命名" }, "mfa_setup": { "close": "關閉", @@ -3036,6 +3048,7 @@ } }, "sidebar": { + "done": "完成", "external_app_configuration": "App 設定", "sidebar_toggle": "側邊欄開關" } From 9af805ab5e17428510f9c24833090302eb917547 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Sun, 13 Sep 2020 12:43:17 -0700 Subject: [PATCH 117/146] Make moon icon more readable (#6969) --- src/data/weather.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/weather.ts b/src/data/weather.ts index 369a64ec34..fa6f6354b0 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -200,7 +200,7 @@ export const weatherSVGStyles = css` fill: var(--weather-icon-sun-color, #fdd93c); } .moon { - fill: var(--weather-icon-moon-color, #fdf9cc); + fill: var(--weather-icon-moon-color, #fcf497); } .cloud-back { fill: var(--weather-icon-cloud-back-color, #d4d4d4); From 1bc9b95289520af3639ac5fc56027abaa9f9b4cf Mon Sep 17 00:00:00 2001 From: Kendell R Date: Sun, 13 Sep 2020 12:43:54 -0700 Subject: [PATCH 118/146] Remove useless "My Title" (#6970) --- src/panels/lovelace/cards/hui-entities-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts index 2e55a41b39..90383f3bdb 100644 --- a/src/panels/lovelace/cards/hui-entities-card.ts +++ b/src/panels/lovelace/cards/hui-entities-card.ts @@ -50,7 +50,7 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard { ["light", "switch", "sensor"] ); - return { type: "entities", title: "My Title", entities: foundEntities }; + return { type: "entities", entities: foundEntities }; } @internalProperty() private _config?: EntitiesCardConfig; From 868399ed6f9213501631bfa14aa6520c16497908 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Sun, 13 Sep 2020 15:27:16 -0500 Subject: [PATCH 119/146] HA Logs: Copy log (#6945) --- .../config/logs/dialog-system-log-detail.ts | 72 +++++++++++++++---- src/translations/en.json | 3 +- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/panels/config/logs/dialog-system-log-detail.ts b/src/panels/config/logs/dialog-system-log-detail.ts index ad5f28c648..34d05afca9 100644 --- a/src/panels/config/logs/dialog-system-log-detail.ts +++ b/src/panels/config/logs/dialog-system-log-detail.ts @@ -1,14 +1,21 @@ +import "@material/mwc-icon-button/mwc-icon-button"; +import { mdiContentCopy } from "@mdi/js"; import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; +import "@polymer/paper-tooltip/paper-tooltip"; +import type { PaperTooltipElement } from "@polymer/paper-tooltip/paper-tooltip"; import { css, CSSResult, html, + internalProperty, LitElement, property, - internalProperty, + query, TemplateResult, } from "lit-element"; +import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/dialog/ha-paper-dialog"; +import "../../../components/ha-svg-icon"; import { domainToName, fetchIntegrationManifest, @@ -16,12 +23,11 @@ import { IntegrationManifest, } from "../../../data/integration"; import { getLoggedErrorIntegration } from "../../../data/system_log"; -import { PolymerChangedEvent } from "../../../polymer-types"; +import type { PolymerChangedEvent } from "../../../polymer-types"; import { haStyleDialog } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; -import { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail"; +import type { HomeAssistant } from "../../../types"; +import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail"; import { formatSystemLogTime } from "./util"; -import { fireEvent } from "../../../common/dom/fire_event"; class DialogSystemLogDetail extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -30,6 +36,8 @@ class DialogSystemLogDetail extends LitElement { @internalProperty() private _manifest?: IntegrationManifest; + @query("paper-tooltip") private _toolTip?: PaperTooltipElement; + public async showDialog(params: SystemLogDetailDialogParams): Promise { this._params = params; this._manifest = undefined; @@ -66,13 +74,25 @@ class DialogSystemLogDetail extends LitElement { opened @opened-changed="${this._openedChanged}" > -

    - ${this.hass.localize( - "ui.panel.config.logs.details", - "level", - item.level - )} -

    +
    +

    + ${this.hass.localize( + "ui.panel.config.logs.details", + "level", + item.level + )} +

    + + + + ${this.hass.localize("ui.common.copied")} +

    Logger: ${item.name}
    @@ -148,6 +168,25 @@ class DialogSystemLogDetail extends LitElement { } } + private _copyLog(): void { + const copyElement = this.shadowRoot?.querySelector( + "paper-dialog-scrollable" + ) as HTMLElement; + + const selection = window.getSelection()!; + const range = document.createRange(); + + range.selectNodeContents(copyElement); + selection.removeAllRanges(); + selection.addRange(range); + + document.execCommand("copy"); + window.getSelection()!.removeAllRanges(); + + this._toolTip!.show(); + setTimeout(() => this._toolTip?.hide(), 3000); + } + static get styles(): CSSResult[] { return [ haStyleDialog, @@ -164,6 +203,15 @@ class DialogSystemLogDetail extends LitElement { pre { margin-bottom: 0; } + .heading { + display: flex; + display: flex; + align-items: center; + justify-content: space-between; + } + .heading ha-svg-icon { + cursor: pointer; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index 8eadb910a9..f4dee440c1 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -278,7 +278,8 @@ "successfully_saved": "Successfully saved", "successfully_deleted": "Successfully deleted", "back": "Back", - "error_required": "Required" + "error_required": "Required", + "copied": "Copied" }, "components": { "logbook": { From ff081dd0f123d3c4ef36868f0014e41178c8b0f0 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Mon, 14 Sep 2020 00:32:37 +0000 Subject: [PATCH 120/146] [ci skip] Translation update --- translations/frontend/ca.json | 22 +++ translations/frontend/cs.json | 33 ++++ translations/frontend/en.json | 1 + translations/frontend/es-419.json | 282 ++++++++++++++++++++++++++--- translations/frontend/es.json | 9 + translations/frontend/nb.json | 30 +++ translations/frontend/nl.json | 32 ++++ translations/frontend/pl.json | 62 ++++++- translations/frontend/ru.json | 21 +++ translations/frontend/zh-Hans.json | 33 ++++ translations/frontend/zh-Hant.json | 22 +++ 11 files changed, 514 insertions(+), 33 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 9e89a2f943..c7ae05df35 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -574,6 +574,28 @@ "audio_not_supported": "El teu navegador no és compatible amb l'element d'àudio.", "choose_player": "Tria el reproductor", "choose-source": "Tria la font", + "class": { + "album": "Àlbum", + "app": "Aplicació", + "artist": "Artista", + "channel": "Canal", + "composer": "Compositor", + "contributing_artist": "Artista col·laborador", + "directory": "Biblioteca", + "episode": "Episodi", + "game": "Joc", + "genre": "Gènere", + "image": "Imatge", + "movie": "Pel·lícula", + "music": "Música", + "playlist": "Llista de reproducció", + "podcast": "Podcast", + "season": "Temporada", + "track": "Pista", + "tv_show": "Programa de TV", + "url": "URL", + "video": "Vídeo" + }, "content-type": { "album": "Àlbum", "artist": "Artista", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 8107413439..2bf3b4226f 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -574,6 +574,28 @@ "audio_not_supported": "Váš prohlížeč nepodporuje element \"audio\".", "choose_player": "Vyberte přehrávač", "choose-source": "Zvolte zdroj", + "class": { + "album": "Album", + "app": "Aplikace", + "artist": "Umělec", + "channel": "Kanál", + "composer": "Skladatel", + "contributing_artist": "Přispívající umělec", + "directory": "Knihovna", + "episode": "Epizoda", + "game": "Hra", + "genre": "Žánr", + "image": "Obrázek", + "movie": "Film", + "music": "Hudba", + "playlist": "Seznam skladeb", + "podcast": "Podcast", + "season": "Sezóna", + "track": "Stopa", + "tv_show": "Televizní pořad", + "url": "Url adresa", + "video": "Video" + }, "content-type": { "album": "Album", "artist": "Umělec", @@ -2352,9 +2374,14 @@ "title": "Stavy" }, "templates": { + "all_listeners": "Tato šablona naslouchá všem změnám stavu.", "description": "Šablony jsou vykreslovány pomocí Jinja2 šablonového enginu s některými specifickými rozšířeními pro Home Assistant.", + "domain": "Doména", "editor": "Editor šablon", + "entity": "Entita", "jinja_documentation": "Dokumentace šablony Jinja2", + "listeners": "Tato šablona naslouchá následujícím změnám stavu:", + "no_listeners": "Tato šablona neposlouchá žádné změny stavu a nebude se automaticky aktualizovat.", "reset": "Obnovit ukázkovou šablonu", "template_extensions": "Rozšíření šablony Home Assistant", "title": "Šablony", @@ -2941,6 +2968,11 @@ "submit": "Odeslat" }, "current_user": "Nyní jste přihlášeni jako {fullName}.", + "customize_sidebar": { + "button": "Upravit", + "description": "Režim úprav můžete aktivovat také stisknutím a podržením záhlaví postranního panelu.", + "header": "Změna pořadí a skrytí položek postranního panelu" + }, "dashboard": { "description": "Vyberte výchozí dashboard pro toto zařízení.", "dropdown_label": "Dashboard", @@ -3038,6 +3070,7 @@ } }, "sidebar": { + "done": "Hotovo", "external_app_configuration": "Konfigurace aplikace", "sidebar_toggle": "Přepínač postranního panelu" } diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 2074324131..c4308bd7a5 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -507,6 +507,7 @@ "cancel": "Cancel", "close": "Close", "continue": "Continue", + "copied": "Copied", "delete": "Delete", "error_required": "Required", "loading": "Loading", diff --git a/translations/frontend/es-419.json b/translations/frontend/es-419.json index ced016f2a1..9f6f2ba0a4 100644 --- a/translations/frontend/es-419.json +++ b/translations/frontend/es-419.json @@ -19,6 +19,7 @@ "logbook": "", "mailbox": "", "map": "", + "media_browser": "Navegador de medios", "profile": "Perfil", "shopping_list": "Lista de compras", "states": "" @@ -106,7 +107,7 @@ }, "automation": { "off": "Desactivado", - "on": "Encendido" + "on": "Activada" }, "binary_sensor": { "battery": { @@ -205,7 +206,7 @@ "fan_only": "Sólo ventilador", "heat": "Calentar", "heat_cool": "Calentar/Enfriar", - "off": "Desactivar" + "off": "Apagado" }, "configurator": { "configure": "Configurar", @@ -237,7 +238,7 @@ "home": "En Casa", "locked": "Cerrado", "not_home": "Fuera de Casa", - "off": "Desactivado", + "off": "Apagado", "ok": "OK", "on": "Encendido", "open": "Abierto", @@ -419,9 +420,16 @@ "unlock": "Desbloquear" }, "media_player": { + "browse_media": "Explorar medios", + "media_next_track": "Siguiente", + "media_play": "Reproducir", + "media_play_pause": "Reproducir/pausa", + "media_previous_track": "Anterior", "sound_mode": "Modo de sonido", "source": "Fuente", - "text_to_speak": "Texto a hablar" + "text_to_speak": "Texto a hablar", + "turn_off": "Apagar", + "turn_on": "Encender" }, "persistent_notification": { "dismiss": "Descartar" @@ -498,6 +506,7 @@ "back": "Atrás", "cancel": "Cancelar", "close": "Cerrar", + "continue": "Continuar", "delete": "Eliminar", "error_required": "Requerido", "loading": "Cargando", @@ -544,6 +553,10 @@ "toggle": "Alternar" }, "entity": { + "entity-attribute-picker": { + "attribute": "Atributo", + "show_attributes": "Mostrar atributos" + }, "entity-picker": { "clear": "Limpiar", "entity": "Entidad", @@ -554,6 +567,58 @@ "loading_history": "Cargando historial de estado...", "no_history_found": "No se encontró historial de estado." }, + "logbook": { + "entries_not_found": "No se encontraron entradas en el libro de registro." + }, + "media-browser": { + "audio_not_supported": "Su navegador no soporta el elemento de audio.", + "choose_player": "Elige el reproductor", + "choose-source": "Elige la fuente", + "class": { + "album": "Álbum", + "app": "Aplicación", + "artist": "Artista", + "channel": "Canal", + "composer": "Compositor", + "contributing_artist": "Artista colaborador", + "directory": "Biblioteca", + "episode": "Episodio", + "game": "Juego", + "genre": "Género", + "image": "Imagen", + "movie": "Película", + "music": "Música", + "playlist": "Lista de reproducción", + "podcast": "Podcast", + "season": "Temporada", + "track": "Pista", + "tv_show": "Programa de TV", + "url": "URL", + "video": "Video" + }, + "content-type": { + "album": "Álbum", + "artist": "Artista", + "library": "Biblioteca", + "playlist": "Lista de reproducción", + "server": "Servidor" + }, + "media_browsing_error": "Error de navegación de medios", + "media_not_supported": "El Reproductor multimedia no es compatible con este tipo de medios", + "media_player": "Reproductor multimedia", + "media-player-browser": "Navegador del reproductor multimedia", + "no_items": "No hay elementos", + "pick": "Elegir", + "pick-media": "Elija medios", + "play": "Reproducir", + "play-media": "Reproducir medios", + "video_not_supported": "Su navegador no soporta el elemento de vídeo.", + "web-browser": "Navegador web" + }, + "picture-upload": { + "label": "Imagen", + "unsupported_format": "Formato no admitido, elija una imagen JPEG, PNG o GIF." + }, "related-items": { "area": "Área", "automation": "Parte de las siguientes automatizaciones", @@ -574,6 +639,7 @@ "week": "{count} {count, plural,\n one {semana}\n other {semanas}\n}" }, "future": "en {time}", + "just_now": "Ahora mismo", "never": "Nunca", "past": "Hace {time}" }, @@ -652,13 +718,19 @@ "pattern": "Patrón de expresiones regulares para la validación del lado del cliente", "text": "Texto" }, - "platform_not_loaded": "La integración {platform} no está cargada. Agregue su configuración agregando 'default_config:' o ''{platform}:''.", + "platform_not_loaded": "La integración {platform} no está cargada. Añádela a su archivo de configuración agregando 'default_config:' o ''{platform}:''.", "required_error_msg": "Este campo es requerido", "yaml_not_editable": "La configuración de esta entidad no se puede editar desde la interfaz de usuario. Solo las entidades configuradas desde la interfaz de usuario se pueden configurar desde la interfaz de usuario." }, + "image_cropper": { + "crop": "Cortar" + }, "more_info_control": { + "controls": "Controles", + "details": "Detalles", "dismiss": "Descartar diálogo", "edit": "Editar entidad", + "history": "Historial", "person": { "create_zone": "Crear zona desde ubicación actual" }, @@ -724,11 +796,11 @@ }, "zha_device_info": { "buttons": { - "add": "Agregar dispositivos", + "add": "Agregar dispositivos usando este dispositivo", "clusters": "Administrar clústeres", "reconfigure": "Reconfigurar dispositivo", "remove": "Eliminar dispositivo", - "zigbee_information": "Información de Zigbee" + "zigbee_information": "Firma del dispositivo Zigbee" }, "confirmations": { "remove": "¿Está seguro de que desea eliminar el dispositivo?" @@ -748,7 +820,7 @@ "unknown": "Desconocido", "zha_device_card": { "area_picker_label": "Área", - "device_name_placeholder": "Nombre de usuario", + "device_name_placeholder": "Cambiar el nombre del dispositivo", "update_name_button": "Actualizar Nombre" } } @@ -798,7 +870,7 @@ "confirmation_text": "Todos los dispositivos en esta área quedarán sin asignar.", "confirmation_title": "¿Está seguro de que desea eliminar esta área?" }, - "description": "Visión general de todas las áreas de su casa.", + "description": "Gestione las áreas de su casa.", "editor": { "area_id": "Identificador del área", "create": "Crear", @@ -859,13 +931,19 @@ "label": "Llamar servico", "service_data": "Datos" }, + "wait_for_trigger": { + "continue_timeout": "Continuar cuando el tiempo venza", + "label": "Esperar por un desencadenador", + "timeout": "Tiempo limite (opcional)" + }, "wait_template": { + "continue_timeout": "Continuar cuando el tiempo venza", "label": "Esperar", "timeout": "Tiempo de espera (opcional)", "wait_template": "Plantilla de espera" } }, - "unsupported_action": "Acción no soportada: {action}" + "unsupported_action": "No hay soporte en la interfaz de usuario para la acción: {action}" }, "alias": "Nombre", "conditions": { @@ -923,7 +1001,9 @@ "time": { "after": "Después de", "before": "Antes de", - "label": "Hora" + "label": "Hora", + "type_input": "Valor de un auxiliar de tipo fecha/tiempo", + "type_value": "Tiempo corregido" }, "zone": { "entity": "Entidad con ubicación", @@ -931,7 +1011,7 @@ "zone": "Zona" } }, - "unsupported_condition": "Condición no soportada: {condition}" + "unsupported_condition": "No hay soporte en la interfaz de usuario para la condición: {condition}" }, "default_name": "Nueva Automatización", "description": { @@ -1000,7 +1080,7 @@ "start": "Inicio" }, "mqtt": { - "label": "", + "label": "MQTT", "payload": "Payload (opcional)", "topic": "Topic" }, @@ -1011,6 +1091,7 @@ "value_template": "Plantilla de valor (opcional)" }, "state": { + "attribute": "Atributo (opcional)", "for": "Por", "from": "De", "label": "Estado", @@ -1023,6 +1104,9 @@ "sunrise": "Salida del sol", "sunset": "Puesta de sol" }, + "tag": { + "label": "Etiqueta" + }, "template": { "label": "Plantilla", "value_template": "Plantilla de valor" @@ -1034,8 +1118,10 @@ "seconds": "Segundos" }, "time": { - "at": "A", - "label": "Hora" + "at": "A las", + "label": "Hora", + "type_input": "Valor de un auxiliar de tipo fecha/tiempo", + "type_value": "Tiempo corregido" }, "webhook": { "label": "Webhook", @@ -1050,7 +1136,7 @@ "zone": "Zona" } }, - "unsupported_platform": "Plataforma no soportada: {platform}" + "unsupported_platform": "No hay soporte en la interfaz de usuario para la plataforma: {platform}" }, "unsaved_confirm": "Tiene cambios sin guardar. ¿Estás seguro que quieres salir?" }, @@ -1058,6 +1144,8 @@ "add_automation": "Agregar automatización", "delete_automation": "Eliminar automatización", "delete_confirm": "¿Está seguro de que desea eliminar esta automatización?", + "duplicate": "Duplicar", + "duplicate_automation": "Duplicar automatización", "edit_automation": "Editar automatización", "header": "Editor de automatizaciones", "headers": { @@ -1139,8 +1227,13 @@ }, "alexa": { "banner": "La edición de las entidades expuestas a través de esta interfaz de usuario está deshabilitada porque ha configurado filtros de entidad en configuration.yaml.", + "dont_expose_entity": "No exponer la entidad", "expose": "Exponer a Alexa", + "expose_entity": "Exponer la entidad", + "exposed": "{selected} expuesto", "exposed_entities": "Entidades expuestas", + "follow_domain": "Seguir dominio", + "not_exposed": "{selected} no expuesto", "not_exposed_entities": "Entidades no expuestas", "title": "Alexa" }, @@ -1178,8 +1271,14 @@ "google": { "banner": "La edición de las entidades expuestas a través de esta interfaz de usuario está deshabilitada porque ha configurado filtros de entidad en configuration.yaml.", "disable_2FA": "Deshabilitar la autenticación de dos factores", + "dont_expose_entity": "No exponer la entidad", "expose": "Exponer al Asistente de Google", + "expose_entity": "Exponer la entidad", + "exposed": "{selected} expuesto", "exposed_entities": "Entidades expuestas", + "follow_domain": "Seguir dominio", + "manage_domains": "Gestionar dominios", + "not_exposed": "{selected} no expuesto", "not_exposed_entities": "Entidades no expuestas", "sync_to_google": "Sincronizar los cambios con Google.", "title": "Asistente de Google" @@ -1281,6 +1380,7 @@ } }, "devices": { + "add_prompt": "Aún no se ha agregado ningún {name} con este dispositivo. Puede agregar uno haciendo clic en el botón + de arriba.", "automation": { "actions": { "caption": "Cuando algo se desencadena..." @@ -1300,6 +1400,7 @@ "caption": "Dispositivos", "confirm_delete": "¿Está seguro de que desea eliminar este dispositivo?", "confirm_rename_entity_ids": "¿También desea cambiar el nombre de la identificación de la entidad de sus entidades?", + "confirm_rename_entity_ids_warning": "Esto no cambiará ninguna configuración (como automatizaciones, scripts, escenas, Lovelace) que esté usando actualmente estas entidades, tendrás que actualizarlas tú mismo.", "data_table": { "area": "Área", "battery": "Batería", @@ -1341,7 +1442,7 @@ }, "entities": { "caption": "Entidades", - "description": "Visión general de todas las entidades conocidas.", + "description": "Gestione todas las entidades conocidas.", "picker": { "disable_selected": { "button": "Deshabilitar selección", @@ -1393,7 +1494,7 @@ "header": "Configurar Home Assistant", "helpers": { "caption": "Auxiliares", - "description": "Elementos que pueden ayudar a construir automatizaciones.", + "description": "Gestionar elementos ayudan a construir automatizaciones", "dialog": { "add_helper": "Agregar auxiliar", "add_platform": "Añadir {platform}", @@ -1444,6 +1545,7 @@ }, "integrations": { "add_integration": "Agregar integración", + "attention": "Atención requerida", "caption": "Integraciones", "config_entry": { "area": "En {area}", @@ -1464,6 +1566,7 @@ "options": "Opciones", "rename": "Renombrar", "restart_confirm": "Reinicie Home Assistant para terminar de eliminar esta integración.", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Editar configuración para {integration}", "system_options": "Opciones de Sistema", "system_options_button": "Opciones del sistema para {integration}", @@ -1486,7 +1589,7 @@ }, "configure": "Configurar", "configured": "Configurado", - "description": "Administrar y configurar integraciones", + "description": "Gestione las integraciones", "details": "Detalles de integración", "discovered": "Descubierto", "home_assistant_website": "Sitio web de Home Assistant", @@ -1510,6 +1613,7 @@ "none_found_detail": "Ajuste sus criterios de búsqueda", "note_about_integrations": "No todas las integraciones se pueden configurar a través de la interfaz de usuario.", "note_about_website_reference": "Hay más disponibles en ", + "reconfigure": "Reconfigurar", "rename_dialog": "Editar el nombre de esta entrada de configuración", "rename_input_label": "Ingresar Nombre", "search": "Buscar integraciones" @@ -1619,13 +1723,84 @@ "title": "", "topic": "tema" }, + "ozw": { + "button": "Configurar", + "common": { + "controller": "Controlador", + "instance": "Instancia", + "network": "Red" + }, + "navigation": { + "network": "Red", + "nodes": "Nodos", + "select_instance": "Seleccione la instancia" + }, + "network_status": { + "details": { + "driverallnodesqueried": "Todos los nodos han sido consultados", + "driverallnodesqueriedsomedead": "Todos los nodos han sido consultados. Algunos nodos fueron encontrados inactivos.", + "driverawakenodesqueries": "Se han consultado todos los nodos activos", + "driverfailed": "No se pudo conectar al controlador Z-Wave", + "driverready": "Iniciando el controlador de Z-Wave", + "driverremoved": "El controlador ha sido eliminado", + "driverreset": "El controlador se ha reiniciado", + "offline": "OZWDaemon fuera de línea", + "ready": "Listo para conectar", + "started": "Conectado a MQTT", + "starting": "Conectando con MQTT", + "stopped": "OpenZWave se detuvo" + }, + "offline": "Fuera de línea", + "online": "En línea", + "starting": "Comenzando", + "unknown": "Desconocido" + }, + "network": { + "header": "Gestión de red", + "introduction": "Gestione las funciones de toda la red." + }, + "node_query_stages": { + "complete": "El proceso de consulta está completo", + "configuration": "Obteniendo los valores de configuración del nodo", + "dynamic": "Obteniendo los valores que cambian con frecuencia del nodo" + }, + "node": { + "button": "Detalles del nodo", + "not_found": "Nodo no encontrado" + }, + "nodes_table": { + "failed": "Fallido", + "id": "ID", + "manufacturer": "Fabricante", + "model": "Modelo", + "query_stage": "Etapa de consulta", + "zwave_plus": "Z-Wave Plus" + }, + "refresh_node": { + "battery_note": "Si el nodo funciona con batería, asegúrese de activarlo antes de continuar.", + "button": "Actualizar nodo", + "complete": "Actualización de nodo completa", + "description": "Esto le indicará a OpenZWave que vuelva a consultar un nodo y actualice las clases de comando, las capacidades y los valores del nodo.", + "node_status": "Estado del nodo", + "refreshing_description": "Actualizando la información del nodo ...", + "start_refresh_button": "Iniciar actualización", + "step": "Paso", + "title": "Actualizar la información del nodos", + "wakeup_header": "Instrucciones de activación para", + "wakeup_instructions_source": "Las instrucciones de activación se obtienen de la base de datos de dispositivos de la comunidad OpenZWave." + }, + "select_instance": { + "header": "Seleccione una instancia OpenZWave", + "introduction": "Tiene más de una instancia de OpenZWave en ejecución. ¿Qué instancia te gustaría gestionar?" + } + }, "person": { "add_person": "Agregar persona", "caption": "Personas", "confirm_delete": "¿Está seguro de que desea eliminar a esta persona?", "confirm_delete2": "Todos los dispositivos que pertenecen a esta persona quedarán sin asignar.", "create_person": "Crear persona", - "description": "Gestiona las personas que rastrea Home Assistant.", + "description": "Gestione las personas que rastrea Home Assistant.", "detail": { "create": "Crear", "delete": "Eliminar", @@ -1648,7 +1823,7 @@ "scene": { "activated": "Escena activada {name}.", "caption": "Escenas", - "description": "Crear y editar escenas", + "description": "Gestionar escenas", "editor": { "default_name": "Nueva escena", "devices": { @@ -1743,7 +1918,7 @@ "reloading": { "automation": "Recargar automatizaciones", "core": "Recargar ubicación y personalizaciones", - "group": "Recargar grupos", + "group": "Recargar grupos, entidades grupales, y servicios de notificación", "heading": "Recarga de configuración YAML", "input_boolean": "Recargar controles booleanos", "input_datetime": "Recargar controles de fechas", @@ -1751,9 +1926,16 @@ "input_select": "Recargar controles de selección", "input_text": "Recargar controles de texto", "introduction": "Algunas partes de Home Assistant pueden recargarse sin requerir un reinicio. Al presionar recargar se descargará su configuración YAML actual y se cargará la nueva.", + "mqtt": "Recargar entidades MQTT", "person": "Recargar personas", + "reload": "Recargar {domain}", + "rest": "Recargar entidades \"rest\" y servicios de notificación.", + "rpi_gpio": "Recargue las entidades GPIO de la Raspberry Pi", "scene": "Recargar escenas", "script": "Recargar scripts", + "smtp": "Recargar servicios de notificación smtp", + "telegram": "Recargar servicios de notificación de telegram", + "template": "Recargar las entidades de la plantilla", "zone": "Recargar zonas" }, "server_management": { @@ -1773,6 +1955,32 @@ } } }, + "tags": { + "add_tag": "Añadir etiqueta", + "automation_title": "La etiqueta {name} ha sido escaneada", + "caption": "Etiquetas", + "create_automation": "Crea automatización con etiqueta", + "description": "Gestionar etiquetas", + "detail": { + "create": "Crear", + "create_and_write": "Crear y escribir", + "delete": "Eliminar", + "description": "Descripción", + "name": "Nombre", + "new_tag": "Nueva etiqueta", + "tag_id": "ID de etiqueta", + "tag_id_placeholder": "Autogenerado cuando se deja vacío", + "update": "Actualizar" + }, + "edit": "Editar", + "headers": { + "last_scanned": "Último escaneado", + "name": "Nombre" + }, + "never_scanned": "Nunca escaneado", + "no_tags": "Sin etiquetas", + "write": "Escribir" + }, "users": { "add_user": { "caption": "Agregar usuario", @@ -1809,7 +2017,7 @@ "system": "Sistema" } }, - "users_privileges_note": "El grupo de usuarios es un trabajo en progreso. El usuario no podrá administrar la instancia a través de la interfaz de usuario. Todavía estamos auditando todos los puntos finales de la API de administración para garantizar que limiten correctamente el acceso a los administradores." + "users_privileges_note": "El grupo de usuarios es un trabajo en progreso. El usuario no podrá administrar la instancia a través de la interfaz de usuario. Todavía estamos auditando todos los puntos finales de la API de administración para garantizar que limiten correctamente el acceso solo a los administradores." }, "zha": { "add_device_page": { @@ -1847,7 +2055,7 @@ "clusters": { "header": "Clústeres", "help_cluster_dropdown": "Seleccione un clúster para ver atributos y comandos.", - "introduction": "Los clústeres son los bloques de construcción para la funcionalidad de Zigbee. Separa la funcionalidad en unidades lógicas. Hay tipos de clientes y servidores que se componen de atributos y comandos." + "introduction": "Los clústeres son los bloques de construcción para la funcionalidad de Zigbee. Ellos separan la funcionalidad en unidades lógicas. Hay tipos de clientes y servidores que se componen de atributos y comandos." }, "common": { "add_devices": "Agregar dispositivos", @@ -1879,7 +2087,7 @@ "create_group": "Zigbee Home Automation - Crear grupo", "create_group_details": "Ingrese los detalles requeridos para crear un nuevo grupo zigbee", "creating_group": "Creando grupo", - "description": "Crear y modificar grupos Zigbee", + "description": "Gestione los grupos Zigbee", "group_details": "Aquí están todos los detalles para el grupo Zigbee seleccionado.", "group_id": "Identificación del grupo", "group_info": "Información del grupo", @@ -2091,9 +2299,14 @@ "title": "Estados" }, "templates": { + "all_listeners": "Esta plantilla escucha todos los eventos de cambio de estado.", "description": "Las plantillas se representan utilizando el motor de plantillas Jinja2 con algunas extensiones específicas de Home Assistant.", + "domain": "Dominio", "editor": "Editor de plantillas", + "entity": "Entidad", "jinja_documentation": "Documentación de plantillas Jinja2", + "listeners": "Esta plantilla escucha los eventos de los siguientes cambios de estado:", + "no_listeners": "Esta plantilla no escucha ningún evento de cambio de estado y no se actualizará automáticamente.", "template_extensions": "Extensiones de plantilla de Home Assistant", "title": "Plantilla", "unknown_error_template": "Error desconocido al mostrar la plantilla" @@ -2335,7 +2548,11 @@ } }, "cardpicker": { + "by_card": "Por Tarjeta", + "by_entity": "Por Entidad", "custom_card": "Personalizado", + "domain": "Dominio", + "entity": "Entidad", "no_description": "No hay descripción disponible." }, "edit_card": { @@ -2349,10 +2566,11 @@ "options": "Mas opciones", "pick_card": "¿Qué tarjeta desea agregar?", "pick_card_view_title": "¿Qué tarjeta le gustaría agregar a su vista de {name} ?", + "search_cards": "Buscar tarjetas", "show_code_editor": "Mostrar editor de código", "show_visual_editor": "Mostrar el editor visual", "toggle_editor": "Cambiar editor", - "typed_header": "{tipo} Configuración de la tarjeta", + "typed_header": "{type} Configuración de la tarjeta", "unsaved_changes": "Tiene cambios no guardados" }, "edit_lovelace": { @@ -2429,7 +2647,7 @@ }, "menu": { "close": "Cerrar", - "configure_ui": "Configurar interfaz de usuario", + "configure_ui": "Editar interfaz de usuario", "exit_edit_mode": "Salir del modo de edición de la interfaz de usuario", "help": "Ayuda", "refresh": "Refrescar", @@ -2661,6 +2879,11 @@ "submit": "Enviar" }, "current_user": "Actualmente estás conectado como {fullName} .", + "customize_sidebar": { + "button": "Editar", + "description": "También puede mantener pulsado el encabezado de la barra lateral para activar el modo de edición.", + "header": "Cambiar el orden y ocultar elementos de la barra lateral" + }, "dashboard": { "description": "Elija un tablero predeterminado para este dispositivo.", "dropdown_label": "Tablero", @@ -2683,6 +2906,7 @@ "confirm_delete": "¿Está seguro de que desea eliminar el token de acceso para {name}?", "create": "Crear Token", "create_failed": "No se pudo crear el token de acceso.", + "created": "Creado {date}", "created_at": "Creado en {date}", "delete_failed": "No se pudo eliminar el token de acceso.", "description": "Cree tokens de acceso de larga duración para permitir que sus secuencias de comandos interactúen con la instancia de su Home Assistant. Cada token tendrá una validez de 10 años a partir de su creación. Los siguientes tokens de acceso de larga duración están activos actualmente.", @@ -2690,9 +2914,10 @@ "header": "Tokens de acceso de larga duración", "last_used": "Utilizado por última vez en {date} desde {location}.", "learn_auth_requests": "Aprenda a realizar solicitudes autenticadas.", + "name": "Nombre", "not_used": "Nunca se ha utilizado", "prompt_copy_token": "Copia tu token de acceso. No se volverá a mostrar", - "prompt_name": "¿Nombre?" + "prompt_name": "Dale un nombre al token" }, "mfa_setup": { "close": "Cerrar", @@ -2748,6 +2973,7 @@ } }, "sidebar": { + "done": "Hecho", "external_app_configuration": "Configuración de la aplicación", "sidebar_toggle": "Alternar barra lateral" } diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 93eaa86477..10b6151b0d 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -571,6 +571,15 @@ "audio_not_supported": "Tu navegador no es compatible con el elemento de audio.", "choose_player": "Elige reproductor", "choose-source": "Elige la fuente", + "class": { + "playlist": "Lista de reproducción", + "podcast": "Podcast", + "season": "Temporada", + "track": "Pista", + "tv_show": "Programa de TV", + "url": "Url", + "video": "Vídeo" + }, "content-type": { "album": "Álbum", "artist": "Artista", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index ab4abc249b..f835f9e909 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -574,6 +574,28 @@ "audio_not_supported": "Nettleseren din støtter ikke lydelementet.", "choose_player": "Velg spiller", "choose-source": "Velg kilde", + "class": { + "album": "Album", + "app": "App", + "artist": "Artist", + "channel": "Kanal", + "composer": "Komponist", + "contributing_artist": "Medvirkende artist", + "directory": "Bibliotek", + "episode": "Episode", + "game": "Spill", + "genre": "Sjanger", + "image": "Bilde", + "movie": "Film", + "music": "Musikk", + "playlist": "Spilleliste", + "podcast": "Podcast", + "season": "Sesong", + "track": "Spor", + "tv_show": "TV Serie", + "url": "Url", + "video": "Video" + }, "content-type": { "album": "Album", "artist": "Artist", @@ -2353,7 +2375,9 @@ }, "templates": { "description": "Maler blir rendret ved hjelp av Jinja2-malmotoren med noen spesifikke utvidelser for Home Assistant.", + "domain": "Domene", "editor": "Maleditor", + "entity": "Entitet", "jinja_documentation": "Jinja2 mal dokumentasjon", "reset": "Tilbakestill til demomal", "template_extensions": "Mal utvidelser for Home Assistant", @@ -2941,6 +2965,9 @@ "submit": "Send inn" }, "current_user": "Du er logget inn som {fullName}.", + "customize_sidebar": { + "header": "Endre rekkefølgen og skjul elementer fra sidepanelet" + }, "dashboard": { "description": "Velg et standard instrumentbord for denne enheten.", "dropdown_label": "", @@ -2963,6 +2990,7 @@ "confirm_delete": "Er du sikker på at du vil slette tilgangstoken for {name}?", "create": "Opprett Token", "create_failed": "Kunne ikke opprette tilgangstoken.", + "created": "Opprettet {date}", "created_at": "Opprettet den {date}", "delete_failed": "Kan ikke slette tilgangstokenet.", "description": "Opprett langvarige tilgangstokener slik at skriptene dine kan samhandle med din Home Assistant instans. Hver token vil være gyldig i 10 år fra opprettelsen. Følgende langvarige tilgangstokener er for tiden aktive.", @@ -2970,6 +2998,7 @@ "header": "Langvarige tilgangstokener", "last_used": "Sist brukt den {date} fra {location}", "learn_auth_requests": "Lær hvordan du lager godkjente forespørsler.", + "name": "Navn", "not_used": "Har aldri blitt brukt", "prompt_copy_token": "Kopier tilgangstoken. Det blir ikke vist igjen.", "prompt_name": "Navn?" @@ -3036,6 +3065,7 @@ } }, "sidebar": { + "done": "Ferdig", "external_app_configuration": "Appkonfigurasjon", "sidebar_toggle": "Vis/Skjul sidepanel" } diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 7e36879dae..b4f6f2fbce 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -574,6 +574,28 @@ "audio_not_supported": "Uw browser ondersteunt het audio-element niet.", "choose_player": "Kies speler", "choose-source": "Kies bron", + "class": { + "album": "Album", + "app": "App", + "artist": "Artiest", + "channel": "Kanaal", + "composer": "Componist", + "contributing_artist": "Bijdragende artiest", + "directory": "Bibliotheek", + "episode": "Aflevering", + "game": "Spel", + "genre": "Genre", + "image": "Afbeelding", + "movie": "Film", + "music": "Muziek", + "playlist": "Afspeellijst", + "podcast": "Podcast", + "season": "Seizoen", + "track": "Nummer", + "tv_show": "TV-programma", + "url": "Url", + "video": "Video" + }, "content-type": { "album": "Album", "artist": "Artiest", @@ -2353,7 +2375,9 @@ }, "templates": { "description": "Sjablonen worden weergegeven met de Jinja2-sjabloonediter samen met enkele extensies van Home Assistant.", + "domain": "Domein", "editor": "Sjabloonediter", + "entity": "Entiteit", "jinja_documentation": "Jinja2-sjabloondocumentatie", "reset": "Resetten naar demosjabloon", "template_extensions": "Home Assistant sjabloon extensiesHome Assistant", @@ -2941,6 +2965,11 @@ "submit": "Verzenden" }, "current_user": "Je bent momenteel ingelogd als {fullName}.", + "customize_sidebar": { + "button": "Bewerken", + "description": "U kunt ook de koptekst van de zijbalk ingedrukt houden om de bewerkingsmodus te activeren.", + "header": "Wijzig de volgorde en verberg items van de zijbalk" + }, "dashboard": { "description": "Kies een standaard dashboard voor dit apparaat.", "dropdown_label": "Dashboard", @@ -2963,6 +2992,7 @@ "confirm_delete": "Weet je zeker dat je de toegangstoken voor {name} wilt verwijderen?", "create": "Token aanmaken", "create_failed": "De toegangstoken kon niet worden aangemaakt.", + "created": "Gemaakt op {date}", "created_at": "Gemaakt op {date}", "delete_failed": "Verwijderen van de toegangstoken is mislukt.", "description": "Maak toegangstokens met een lange levensduur zodat je scripts kunnen communiceren met je Home Assistant-instantie. Elk token is tien jaar geldig vanaf de aanmaakdatum. De volgende langlevende toegangstokens zijn momenteel actief.", @@ -2970,6 +3000,7 @@ "header": "Toegangtokens met lange levensduur", "last_used": "Laatst gebruikt op {date} vanaf {location}", "learn_auth_requests": "Kom te weten hoe je geverifieerde verzoeken kunt maken", + "name": "Naam", "not_used": "Is nog nooit gebruikt", "prompt_copy_token": "Kopieer je toegangstoken. Het wordt niet meer getoond.", "prompt_name": "Naam?" @@ -3036,6 +3067,7 @@ } }, "sidebar": { + "done": "Gedaan", "external_app_configuration": "App configuratie", "sidebar_toggle": "Zijbalk in- en uitschakelen" } diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index 68d09fa8ae..ffee82cabd 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -19,6 +19,7 @@ "logbook": "Dziennik", "mailbox": "Poczta", "map": "Mapa", + "media_browser": "Odtwarzacz mediów", "profile": "Profil", "shopping_list": "Lista zakupów", "states": "Przegląd" @@ -553,6 +554,7 @@ }, "entity": { "entity-attribute-picker": { + "attribute": "Atrybut", "show_attributes": "Pokaż atrybuty" }, "entity-picker": { @@ -565,10 +567,35 @@ "loading_history": "Ładowanie historii...", "no_history_found": "Nie znaleziono historii." }, + "logbook": { + "entries_not_found": "Nie znaleziono wpisów w dzienniku." + }, "media-browser": { "audio_not_supported": "Twoja przeglądarka nie obsługuje elementu audio.", "choose_player": "Wybierz odtwarzacz", "choose-source": "Wybierz źródło", + "class": { + "album": "Album", + "app": "Aplikacja", + "artist": "Artysta", + "channel": "Kanał", + "composer": "Kompozytor", + "contributing_artist": "Artysta współpracujący", + "directory": "Biblioteka", + "episode": "Odcinek", + "game": "Gra", + "genre": "Gatunek", + "image": "Obraz", + "movie": "Film", + "music": "Muzyka", + "playlist": "Playlista", + "podcast": "Podcast", + "season": "Sezon", + "track": "Ścieżka", + "tv_show": "Program telewizyjny", + "url": "URL", + "video": "Wideo" + }, "content-type": { "album": "Album", "artist": "Artysta", @@ -576,6 +603,7 @@ "playlist": "Lista odtwarzania", "server": "Serwer" }, + "media_browsing_error": "Błąd przeglądania multimediów", "media_not_supported": "Przeglądarka odtwarzacza mediów nie obsługuje tego typu mediów", "media_player": "Odtwarzacz mediów", "media-player-browser": "Przeglądarka odtwarzacza mediów", @@ -931,7 +959,13 @@ "label": "Wywołanie usługi", "service_data": "Dane usługi" }, + "wait_for_trigger": { + "continue_timeout": "Kontynuuj po przekroczeniu limitu czasu", + "label": "Czekaj na wyzwalacz", + "timeout": "Limit czasu (opcjonalnie)" + }, "wait_template": { + "continue_timeout": "Kontynuuj po przekroczeniu limitu czasu", "label": "Oczekiwanie", "timeout": "Limit czasu (opcjonalnie)", "wait_template": "Szablon czekania" @@ -995,7 +1029,9 @@ "time": { "after": "Po", "before": "Przed", - "label": "Czas" + "label": "Czas", + "type_input": "Wartość pomocnika typu data/czas", + "type_value": "Ustalony czas" }, "zone": { "entity": "Encja z lokalizacją", @@ -1083,6 +1119,7 @@ "value_template": "Szablon wartości (opcjonalnie)" }, "state": { + "attribute": "Atrybut (opcjonalnie)", "for": "Przez", "from": "Z", "label": "Stan", @@ -1110,7 +1147,9 @@ }, "time": { "at": "O", - "label": "Czas" + "label": "Czas", + "type_input": "Wartość pomocnika typu data/czas", + "type_value": "Ustalony czas" }, "webhook": { "label": "Webhook", @@ -1134,7 +1173,7 @@ "delete_automation": "Usuń automatyzację", "delete_confirm": "Czy na pewno chcesz usunąć tę automatyzację?", "duplicate": "Duplikuj", - "duplicate_automation": "Duplikat automatyzację", + "duplicate_automation": "Duplikuj automatyzację", "edit_automation": "Edytuj automatyzację", "header": "Edytor automatyzacji", "headers": { @@ -1502,7 +1541,7 @@ }, "types": { "input_boolean": "Przełącznik", - "input_datetime": "Data i/lub godzina", + "input_datetime": "Data i/lub czas", "input_number": "Numer", "input_select": "Pole wyboru", "input_text": "Tekst" @@ -1535,6 +1574,7 @@ }, "integrations": { "add_integration": "Dodaj integrację", + "attention": "Wymaga uwagi", "caption": "Integracje", "config_entry": { "area": "obszar: {area}", @@ -1937,7 +1977,7 @@ "reloading": { "automation": "Automatyzacje", "command_line": "Encje komponentu linia komend", - "core": "Lokalizacja i dostosowywanie", + "core": "Lokalizację i dostosowywanie", "filesize": "Encje komponentu wielkość pliku", "filter": "Encje komponentu filtr", "generic": "Encje komponentu kamera IP generic", @@ -2334,9 +2374,14 @@ "title": "Stany" }, "templates": { + "all_listeners": "Ten szablon nasłuchuje wszystkich zdarzeń zmiany stanu.", "description": "Szablony są renderowane przy użyciu silnika szablonów Jinja2 z kilkoma specyficznymi rozszerzeniami Home Assistanta.", + "domain": "Domena", "editor": "Edytor szablonów", + "entity": "Encja", "jinja_documentation": "Dokumentacja szablonów Jinja2", + "listeners": "Ten szablon nasłuchuje następujących zdarzeń zmiany stanu:", + "no_listeners": "Ten szablon nie nasłuchuje żadnych zdarzeń zmiany stanu i nie zostanie zaktualizowany automatycznie.", "reset": "Zresetuj do szablonu demonstracyjnego", "template_extensions": "Rozszerzenia szablonów Home Assistanta", "title": "Szablon", @@ -2923,6 +2968,11 @@ "submit": "Zatwierdź" }, "current_user": "Jesteś obecnie zalogowany jako {fullName}.", + "customize_sidebar": { + "button": "Edytuj", + "description": "Możesz także nacisnąć i przytrzymać nagłówek paska bocznego, aby aktywować tryb edycji.", + "header": "Zmień kolejność i ukryj elementy na pasku bocznym" + }, "dashboard": { "description": "Wybierz domyślny dashboard dla tego urządzenia.", "dropdown_label": "Dashboard", @@ -2945,6 +2995,7 @@ "confirm_delete": "Czy na pewno chcesz usunąć token dla {name}?", "create": "Utwórz token", "create_failed": "Nie udało się utworzyć tokena.", + "created": "Utworzony {date}", "created_at": "Utworzony {date}", "delete_failed": "Nie udało się usunąć tokena.", "description": "Długoterminowe tokeny dostępu umożliwiają skryptom interakcję z Home Assistantem. Każdy token będzie ważny przez 10 lat od utworzenia. Następujące tokeny są obecnie aktywne.", @@ -3019,6 +3070,7 @@ } }, "sidebar": { + "done": "Gotowe", "external_app_configuration": "Konfiguracja aplikacji", "sidebar_toggle": "Przełącz pasek boczny" } diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 30a5044abe..476f5bdb03 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -574,6 +574,27 @@ "audio_not_supported": "Ваш браузер не поддерживает аудио.", "choose_player": "Выберите медиаплеер", "choose-source": "Выбрать источник", + "class": { + "album": "Альбом", + "app": "Приложение", + "artist": "Исполнитель", + "channel": "Канал", + "composer": "Композитор", + "directory": "Библиотека", + "episode": "Эпизод", + "game": "Игра", + "genre": "Жанр", + "image": "Изображение", + "movie": "Фильм", + "music": "Музыка", + "playlist": "Плейлист", + "podcast": "Подкаст", + "season": "Сезон", + "track": "Трек", + "tv_show": "Сериалы", + "url": "URL-адрес", + "video": "Видео" + }, "content-type": { "album": "Альбом", "artist": "Исполнитель", diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index 611909f0cf..d8bc96cb89 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -574,6 +574,28 @@ "audio_not_supported": "您的浏览器不支持音频元素。", "choose_player": "选择播放器", "choose-source": "选择媒体源", + "class": { + "album": "专辑", + "app": "应用", + "artist": "艺术家", + "channel": "频道", + "composer": "作曲家", + "contributing_artist": "参与创作者", + "directory": "媒体库", + "episode": "分集", + "game": "游戏", + "genre": "体裁", + "image": "图片", + "movie": "电影", + "music": "音乐", + "playlist": "播放列表", + "podcast": "播客", + "season": "播出季", + "track": "音轨", + "tv_show": "电视节目", + "url": "网址", + "video": "视频" + }, "content-type": { "album": "专辑", "artist": "艺术家", @@ -2352,9 +2374,14 @@ "title": "状态" }, "templates": { + "all_listeners": "此模板监听所有的状态改变事件。", "description": "模板使用 jinja2 模板引擎和一些 Home Assistant 特定的插件进行渲染。", + "domain": "域", "editor": "模板编辑器", + "entity": "实体", "jinja_documentation": "Jinja2 模板文档", + "listeners": "此模板监听以下状态改变事件:", + "no_listeners": "此模板不监听任何状态改变事件,并且不会自动更新。", "reset": "重置为演示模板", "template_extensions": "Home Assistant 模板插件", "title": "模板", @@ -2941,6 +2968,11 @@ "submit": "提交" }, "current_user": "您目前以 {fullName} 的身份登录。", + "customize_sidebar": { + "button": "编辑", + "description": "您也可以长按侧边栏标题来进入编辑模式。", + "header": "排序和隐藏侧边栏中的项目" + }, "dashboard": { "description": "选择此设备的默认仪表盘。", "dropdown_label": "仪表盘", @@ -3038,6 +3070,7 @@ } }, "sidebar": { + "done": "完成", "external_app_configuration": "应用配置", "sidebar_toggle": "侧边栏切换" } diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 5273e7c538..5343241299 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -574,6 +574,28 @@ "audio_not_supported": "瀏覽器不支援音效元件。", "choose_player": "選擇播放器", "choose-source": "選擇來源", + "class": { + "album": "專輯", + "app": "App", + "artist": "演唱者", + "channel": "頻道", + "composer": "作曲者", + "contributing_artist": "參與藝術家", + "directory": "媒體庫", + "episode": "集", + "game": "遊戲", + "genre": "類型", + "image": "圖像", + "movie": "電影", + "music": "音樂", + "playlist": "播放列表", + "podcast": "Podcast", + "season": "季", + "track": "音軌", + "tv_show": "電視節目", + "url": "網址", + "video": "影片" + }, "content-type": { "album": "專輯", "artist": "演唱者", From fab6cebf0d4b055e5be1abe46a9fd97198de7082 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Mon, 14 Sep 2020 02:33:23 -0500 Subject: [PATCH 121/146] fix hard to read text (#6980) --- src/panels/media-browser/ha-panel-media-browser.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index 63c71d2e92..8fa84cba67 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -64,7 +64,7 @@ class PanelMediaBrowser extends LitElement { "ui.components.media-browser.media-player-browser" )}

    -
    ${title || ""}
    +
    ${title || ""}
    ${this.hass.localize("ui.components.media-browser.choose_player")} @@ -146,8 +146,7 @@ class PanelMediaBrowser extends LitElement { overflow: hidden; white-space: nowrap; } - .heading .secondary { - color: var(--secondary-text-color); + .heading .secondary-text { font-size: 14px; overflow: hidden; text-overflow: ellipsis; From 426f93998298b520d17e72aa70c1068f99710865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Mon, 14 Sep 2020 09:34:59 +0200 Subject: [PATCH 122/146] Add Execute button to script editor (#6957) --- src/panels/config/script/ha-script-editor.ts | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index b3195e0cf7..9e4ce3cc07 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -18,12 +18,14 @@ import { navigate } from "../../../common/navigate"; import { computeRTL } from "../../../common/util/compute_rtl"; import "../../../components/ha-card"; import "../../../components/ha-icon-input"; +import { showToast } from "../../../util/toast"; import "@material/mwc-fab"; import { Action, deleteScript, getScriptEditorInitData, ScriptConfig, + triggerScript, MODES, MODES_MAX, } from "../../../data/script"; @@ -193,6 +195,22 @@ export class HaScriptEditor extends LitElement { ` : html``}
    + ${this.scriptEntityId + ? html` +
    + + + ${this.hass.localize("ui.card.script.execute")} + +
    + ` + : ``} @@ -301,6 +319,18 @@ export class HaScriptEditor extends LitElement { } } + private async _runScript(ev) { + ev.stopPropagation(); + await triggerScript(this.hass, this.scriptEntityId); + showToast(this, { + message: this.hass.localize( + "ui.notification_toast.triggered", + "name", + this._config!.alias + ), + }); + } + private _modeChanged(ev: CustomEvent) { const mode = ((ev.target as PaperListboxElement)?.selectedItem as any) ?.mode; From f6eb31bf9daf249842a53035d16c39d44a181f36 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Mon, 14 Sep 2020 00:37:59 -0700 Subject: [PATCH 123/146] Use --error-color instead of a fixed color (#6961) Co-authored-by: Zack Barett --- src/panels/lovelace/cards/hui-error-card.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-error-card.ts b/src/panels/lovelace/cards/hui-error-card.ts index 9da8f631b7..b995c6c04d 100644 --- a/src/panels/lovelace/cards/hui-error-card.ts +++ b/src/panels/lovelace/cards/hui-error-card.ts @@ -43,8 +43,8 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { return css` :host { display: block; - background-color: #ef5350; - color: white; + background-color: var(--error-color); + color: var(--color-on-error, white); padding: 8px; font-weight: 500; user-select: text; From e465ec88354a0c4e293ef9f827fdf0852f3202f9 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Mon, 14 Sep 2020 00:39:47 -0700 Subject: [PATCH 124/146] Make code editor font family follow theme (#6958) --- src/components/ha-code-editor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index 452e75dffd..dbbc9b616f 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -97,6 +97,7 @@ export class HaCodeEditor extends UpdatingElement { .CodeMirror { height: var(--code-mirror-height, auto); direction: var(--code-mirror-direction, ltr); + font-family: var(--code-font-family, monospace); } .CodeMirror-scroll { max-height: var(--code-mirror-max-height, --code-mirror-height); From 709112c498464985be9b22bfe2f610a72ba3ce70 Mon Sep 17 00:00:00 2001 From: Kendell R Date: Mon, 14 Sep 2020 00:40:35 -0700 Subject: [PATCH 125/146] Do safety check before detecting hex value and handle YAML numbers better (#6956) Co-authored-by: Bram Kragten --- src/common/dom/apply_themes_on_element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/dom/apply_themes_on_element.ts b/src/common/dom/apply_themes_on_element.ts index f994f0d99d..0ac552abdd 100644 --- a/src/common/dom/apply_themes_on_element.ts +++ b/src/common/dom/apply_themes_on_element.ts @@ -105,12 +105,12 @@ const processTheme = ( const keys = {}; for (const key of Object.keys(combinedTheme)) { const prefixedKey = `--${key}`; - const value = combinedTheme[key]!; + const value = String(combinedTheme[key]!); styles[prefixedKey] = value; keys[prefixedKey] = ""; // Try to create a rgb value for this key if it is not a var - if (!value.startsWith("#")) { + if (value.startsWith("#")) { // Can't convert non hex value continue; } From 5b94a4de9ae0dc6d608f148d528e6d8e7f23cb84 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 14 Sep 2020 11:14:32 +0200 Subject: [PATCH 126/146] Fix muted on video --- src/components/ha-hls-player.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-hls-player.ts b/src/components/ha-hls-player.ts index 8f47b08870..912bcab7b6 100644 --- a/src/components/ha-hls-player.ts +++ b/src/components/ha-hls-player.ts @@ -61,7 +61,7 @@ class HaHLSPlayer extends LitElement { return html`
    ` ) @@ -135,8 +135,8 @@ class HaRefreshTokens extends LitElement { ha-settings-row { padding: 0; } - mwc-button { - --mdc-theme-primary: var(--primary-color); + mwc-icon-button { + color: var(--primary-text-color); } `, ]; From 7628569579fe8ba493060a197049c6a21c1f231a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 00:07:45 +0200 Subject: [PATCH 131/146] Cleanup more info styling (#7004) --- src/dialogs/more-info/controls/more-info-person.ts | 5 ++++- src/dialogs/more-info/ha-more-info-dialog.ts | 6 +++--- src/dialogs/more-info/ha-more-info-history.ts | 14 -------------- src/dialogs/more-info/ha-more-info-logbook.ts | 2 -- src/panels/logbook/ha-logbook.ts | 2 +- 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-person.ts b/src/dialogs/more-info/controls/more-info-person.ts index ef8b3f803b..84fa6a733b 100644 --- a/src/dialogs/more-info/controls/more-info-person.ts +++ b/src/dialogs/more-info/controls/more-info-person.ts @@ -32,7 +32,7 @@ class MoreInfoPerson extends LitElement { return html` ${this.stateObj.attributes.latitude && this.stateObj.attributes.longitude ? html` @@ -78,6 +78,9 @@ class MoreInfoPerson extends LitElement { margin: 36px 0 8px 0; text-align: right; } + ha-map { + margin-top: 16px; + } `; } } diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index bb217a732c..4d787863d8 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -26,10 +26,8 @@ import { navigate } from "../../common/navigate"; import "../../components/ha-dialog"; import "../../components/ha-header-bar"; import "../../components/ha-svg-icon"; -import "../../components/state-history-charts"; import { removeEntityRegistryEntry } from "../../data/entity_registry"; import { showEntityEditorDialog } from "../../panels/config/entities/show-dialog-entity-editor"; -import "../../panels/logbook/ha-logbook"; import { haStyleDialog } from "../../resources/styles"; import "../../state-summary/state-card-content"; import { HomeAssistant } from "../../types"; @@ -394,7 +392,9 @@ export class MoreInfoDialog extends LitElement { --dialog-content-padding: 0; } - state-card-content { + state-card-content, + ha-more-info-history, + ha-more-info-logbook:not(:last-child) { display: block; margin-bottom: 16px; } diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 1dc70b68d4..93970b7b93 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -1,5 +1,4 @@ import { - css, customElement, html, internalProperty, @@ -13,7 +12,6 @@ import { throttle } from "../../common/util/throttle"; import "../../components/state-history-charts"; import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; -import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @customElement("ha-more-info-history") @@ -88,18 +86,6 @@ export class MoreInfoHistory extends LitElement { this.hass!.language ); } - - static get styles() { - return [ - haStyle, - css` - state-history-charts { - display: block; - margin-bottom: 16px; - } - `, - ]; - } } declare global { diff --git a/src/dialogs/more-info/ha-more-info-logbook.ts b/src/dialogs/more-info/ha-more-info-logbook.ts index a3258133f2..6c559e7dae 100644 --- a/src/dialogs/more-info/ha-more-info-logbook.ts +++ b/src/dialogs/more-info/ha-more-info-logbook.ts @@ -152,8 +152,6 @@ export class MoreInfoLogbook extends LitElement { ha-logbook { max-height: 250px; overflow: auto; - display: block; - margin-top: 16px; } ha-circular-progress { display: flex; diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 86e61d3535..9c5e85d5df 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -285,7 +285,7 @@ class HaLogbook extends LitElement { .narrow .entry { flex-direction: column; line-height: 1.5; - padding: 8px; + padding: 8px 0; } .narrow .icon-message ha-icon { From fb75d8c1f2defd644f77994bc0833c143f751e5d Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 00:08:16 +0200 Subject: [PATCH 132/146] Fix hex to rgb conversion (#6999) --- src/common/dom/apply_themes_on_element.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/dom/apply_themes_on_element.ts b/src/common/dom/apply_themes_on_element.ts index 0ac552abdd..902a1a196a 100644 --- a/src/common/dom/apply_themes_on_element.ts +++ b/src/common/dom/apply_themes_on_element.ts @@ -1,14 +1,14 @@ -import { derivedStyles, darkStyles } from "../../resources/styles"; +import { darkStyles, derivedStyles } from "../../resources/styles"; import { HomeAssistant, Theme } from "../../types"; import { hex2rgb, + lab2hex, + lab2rgb, rgb2hex, rgb2lab, - lab2rgb, - lab2hex, } from "../color/convert-color"; +import { labBrighten, labDarken } from "../color/lab"; import { rgbContrast } from "../color/rgb"; -import { labDarken, labBrighten } from "../color/lab"; interface ProcessedTheme { keys: { [key: string]: "" }; @@ -105,12 +105,12 @@ const processTheme = ( const keys = {}; for (const key of Object.keys(combinedTheme)) { const prefixedKey = `--${key}`; - const value = String(combinedTheme[key]!); + const value = String(combinedTheme[key]); styles[prefixedKey] = value; keys[prefixedKey] = ""; // Try to create a rgb value for this key if it is not a var - if (value.startsWith("#")) { + if (!value.startsWith("#")) { // Can't convert non hex value continue; } From 858196ab53ded57939a10de64a8110011ddbc564 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 00:19:23 +0200 Subject: [PATCH 133/146] Update en.json --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index f4dee440c1..fff67d34fb 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2150,7 +2150,7 @@ "save": "Save", "unsaved_changes": "Unsaved changes", "saved": "Saved", - "confirm_remove_config_title": "Are you sure you want to remove your Lovelace UI configuration? We will automatically generate your Lovelace UI views with your areas and devices.", + "confirm_remove_config_title": "Are you sure you want to remove your Lovelace UI configuration?", "confirm_remove_config_text": "We will automatically generate your Lovelace UI views with your areas and devices if you remove your Lovelace UI configuration.", "confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?", "confirm_unsaved_comments": "Your configuration contains comment(s), these will not be saved. Do you want to continue?", From 4392d78ff6098d26c68a3281511b9f4498d08361 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 15 Sep 2020 06:26:26 +0800 Subject: [PATCH 134/146] Allow ExoPlayer only from more-info-camera (#6974) Co-authored-by: Bram Kragten --- src/components/ha-camera-stream.ts | 4 ++++ src/components/ha-hls-player.ts | 5 ++++- src/dialogs/more-info/controls/more-info-camera.ts | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index 7b091055e0..eb9ce99718 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -32,6 +32,9 @@ class HaCameraStream extends LitElement { @property({ type: Boolean, attribute: "muted" }) public muted = false; + @property({ type: Boolean, attribute: "allow-exoplayer" }) + public allowExoPlayer = false; + // We keep track if we should force MJPEG with a string // that way it automatically resets if we change entity. @internalProperty() private _forceMJPEG?: string; @@ -61,6 +64,7 @@ class HaCameraStream extends LitElement { { - if (!this.hass!.auth.external) { + if (!this.hass!.auth.external || !this.allowExoPlayer) { return false; } const externalConfig = await getExternalConfig(this.hass!.auth.external); diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index ffd315d9dd..7fa55778b2 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -48,6 +48,7 @@ class MoreInfoCamera extends LitElement { ${this._cameraPrefs From 183eff745daaf4e220fd59918e9492e3b953c184 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Tue, 15 Sep 2020 00:32:23 +0000 Subject: [PATCH 135/146] [ci skip] Translation update --- translations/frontend/ca.json | 1 + translations/frontend/cs.json | 1 + translations/frontend/de.json | 93 +++++++++++++++++++++++++++--- translations/frontend/en.json | 2 +- translations/frontend/es.json | 33 ++++++++++- translations/frontend/fr.json | 50 +++++++++++++++- translations/frontend/nb.json | 8 ++- translations/frontend/nl.json | 6 +- translations/frontend/pl.json | 1 + translations/frontend/ro.json | 49 ++++++++++++++++ translations/frontend/ru.json | 5 ++ translations/frontend/zh-Hant.json | 1 + 12 files changed, 237 insertions(+), 13 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index c7ae05df35..15df5c0e86 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -507,6 +507,7 @@ "cancel": "Cancel·la", "close": "Tanca", "continue": "Continua", + "copied": "Copiat", "delete": "Elimina", "error_required": "Obligatori", "loading": "Carregant", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 2bf3b4226f..5675a163a4 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -507,6 +507,7 @@ "cancel": "Zrušit", "close": "Zavřít", "continue": "Pokračovat", + "copied": "Zkopírováno", "delete": "Smazat", "error_required": "Povinné", "loading": "Načítání", diff --git a/translations/frontend/de.json b/translations/frontend/de.json index 4628cab1cc..be5fda9173 100644 --- a/translations/frontend/de.json +++ b/translations/frontend/de.json @@ -419,6 +419,7 @@ "unlock": "Entriegeln" }, "media_player": { + "media_next_track": "Weiter", "media_play": "Abspielen", "sound_mode": "Sound-Modus", "source": "Quelle", @@ -501,6 +502,8 @@ "back": "Zurück", "cancel": "Abbrechen", "close": "Schließen", + "continue": "Weiter", + "copied": "Kopiert", "delete": "Löschen", "error_required": "Benötigt", "loading": "Laden", @@ -557,8 +560,24 @@ "loading_history": "Lade Zustandsverlauf...", "no_history_found": "Kein Zustandsverlauf gefunden." }, + "logbook": { + "entries_not_found": "Keine Logbucheinträge gefunden." + }, "media-browser": { + "audio_not_supported": "Ihr Browser unterstützt das Audioelement nicht.", "choose-source": "Quelle wählen", + "class": { + "album": "Album", + "app": "App", + "directory": "Bibliothek", + "episode": "Episode", + "game": "Spiel", + "movie": "Film", + "music": "Musik", + "season": "Episode", + "url": "URL", + "video": "Video" + }, "content-type": { "album": "Album", "artist": "Künstler", @@ -566,7 +585,13 @@ "playlist": "Playlist", "server": "Server" }, - "play": "Abspielen" + "no_items": "Keine Einträge", + "play": "Abspielen", + "video_not_supported": "Ihr Browser unterstützt das Videoelement nicht." + }, + "picture-upload": { + "label": "Bild", + "unsupported_format": "Nicht unterstütztes Format, bitte wähle ein JPEG-, PNG- oder GIF-Bild." }, "related-items": { "area": "Bereich", @@ -672,8 +697,10 @@ "yaml_not_editable": "Die Einstellungen dieser Entität können nicht über die Benutzeroberfläche bearbeitet werden. Nur über die Benutzeroberfläche eingerichtete Entitäten können über die Benutzeroberfläche konfiguriert werden." }, "more_info_control": { + "details": "Details", "dismiss": "Dialog ausblenden", "edit": "Entität bearbeiten", + "history": "Verlauf", "person": { "create_zone": "Zone vom aktuellen Standort erstellen" }, @@ -849,7 +876,13 @@ "type_select": "Aktionstyp", "type": { "choose": { - "label": "Auswählen" + "add_option": "Option hinzufügen", + "conditions": "Bedingungen", + "default": "Standard-Aktionen", + "label": "Auswählen", + "option": "Option {number}", + "remove_option": "Option entfernen", + "sequence": "Aktionen" }, "condition": { "label": "Bedingung" @@ -872,6 +905,7 @@ }, "repeat": { "label": "Wiederholen", + "sequence": "Aktionen", "type_select": "Wiederholungstyp", "type": { "count": { @@ -1044,6 +1078,7 @@ "value_template": "Wert-Template (optional)" }, "state": { + "attribute": "Attribut (Optional)", "for": "Für", "from": "Von", "label": "Zustand", @@ -1068,7 +1103,8 @@ }, "time": { "at": "Um", - "label": "Zeit" + "label": "Zeit", + "type_value": "Feste Zeit" }, "webhook": { "label": "Webhook", @@ -1091,6 +1127,8 @@ "add_automation": "Automatisierung hinzufügen", "delete_automation": "Automatisierung löschen", "delete_confirm": "Bist du sicher, dass du diese Automatisierung löschen möchtest?", + "duplicate": "Duplizieren", + "duplicate_automation": "Automatisierung kopieren", "edit_automation": "Automatisierung bearbeiten", "header": "Automatisierungseditor", "headers": { @@ -1314,6 +1352,7 @@ } }, "devices": { + "add_prompt": "Mit diesem Gerät wurden noch keine {name} hinzugefügt. Du kannst eins hinzufügen, indem du auf den + Knopf drückst.", "automation": { "actions": { "caption": "Wenn etwas ausgelöst wird ..." @@ -1477,6 +1516,7 @@ }, "integrations": { "add_integration": "Integration hinzufügen", + "attention": "Aufmerksamkeit erforderlich", "caption": "Integrationen", "config_entry": { "area": "In {area}", @@ -1495,6 +1535,8 @@ "no_device": "Entitäten ohne Geräte", "no_devices": "Diese Integration hat keine Geräte.", "options": "Optionen", + "reload": "Neu laden", + "reload_confirm": "Die Integration wurde neu geladen", "rename": "Umbenennen", "restart_confirm": "Starte Home Assistant neu, um das Entfernen dieser Integration abzuschließen", "settings_button": "Einstellungen für {integration} bearbeiten", @@ -1543,6 +1585,7 @@ "none_found_detail": "Passe deine Suchkriterien an.", "note_about_integrations": "Nicht alle Integrationen können über die Benutzeroberfläche konfiguriert werden.", "note_about_website_reference": "Weitere Informationen findest du auf der ", + "reconfigure": "Neu konfigurieren", "rename_dialog": "Bearbeite den Namen dieses Konfigurationseintrags", "rename_input_label": "Eintragsname", "search": "Such-Integrationen" @@ -1653,8 +1696,11 @@ "topic": "Topic" }, "ozw": { + "button": "Konfigurieren", "common": { - "instance": "Instanz" + "instance": "Instanz", + "network": "Netzwerk", + "zwave": "Z-Wave" }, "device_info": { "zwave_info": "Z-Wave Infos" @@ -1664,12 +1710,27 @@ "select_instance": "Instanz auswählen" }, "network_status": { + "details": { + "driverremoved": "Der Treiber wurde entfernt", + "driverreset": "Der Treiber wurde zurückgesetzt", + "ready": "Bereit zum Verbinden", + "started": "Verbindung mit MQTT hergestellt", + "starting": "Verbinde zu MQTT" + }, "offline": "Offline", "online": "Online", + "starting": "Startet", "unknown": "Unbekannt" }, "network": { "header": "Netzwerkverwaltung" + }, + "nodes_table": { + "failed": "Fehlgeschlagen", + "manufacturer": "Hersteller" + }, + "refresh_node": { + "start_refresh_button": "Aktualisierung starten" } }, "person": { @@ -1798,6 +1859,7 @@ "core": "Ort & Anpassungen neu laden", "group": "Gruppen neu laden", "heading": "Neuladen der YAML-Konfiguration", + "homekit": "HomeKit neu laden", "input_boolean": "Eingabe-Booleans neu laden", "input_datetime": "Eingabe-Datums- und Zeitfelder neu laden", "input_number": "Eingabenummern neu laden", @@ -1807,6 +1869,7 @@ "person": "Personen neu laden", "scene": "Szenen neu laden", "script": "Skripte neu laden", + "telegram": "Telegram-Benachrichtigungsdienste neu laden", "zone": "Zonen neu laden" }, "server_management": { @@ -1828,12 +1891,18 @@ }, "tags": { "detail": { - "name": "Name" + "delete": "Löschen", + "description": "Beschreibung", + "name": "Name", + "update": "Aktualisieren" }, + "edit": "Bearbeiten", "headers": { + "last_scanned": "Zuletzt gescannt", "name": "Name" }, - "never_scanned": "Nie gescannt" + "never_scanned": "Nie gescannt", + "write": "Schreiben" }, "users": { "add_user": { @@ -1841,6 +1910,7 @@ "create": "Benutzerkonto anlegen", "name": "Name", "password": "Passwort", + "password_confirm": "Passwort bestätigen", "username": "Benutzername" }, "caption": "Benutzer", @@ -1857,7 +1927,9 @@ "group": "Gruppe", "id": "ID", "name": "Name", + "new_password": "Neues Passwort", "owner": "Besitzer", + "password_changed": "Das Passwort wurde geändert!", "system_generated": "System generiert", "system_generated_users_not_editable": "Systemgenerierte Benutzer können nicht aktualisiert werden.", "system_generated_users_not_removable": "Vom System generierte Benutzer können nicht entfernt werden.", @@ -2728,6 +2800,9 @@ "submit": "Absenden" }, "current_user": "Sie sind derzeit als {fullName} angemeldet.", + "customize_sidebar": { + "button": "Bearbeiten" + }, "dashboard": { "description": "Wähle ein Standard-Dashboard für dieses Gerät.", "dropdown_label": "Dashboard", @@ -2750,6 +2825,7 @@ "confirm_delete": "Möchten Sie den Zugriffs-Token für {name} wirklich löschen?", "create": "Token erstellen", "create_failed": "Das Zugriffs-Token konnte nicht erstellt werden.", + "created": "Erstellt am {date}", "created_at": "Erstellt am {date}", "delete_failed": "Fehler beim Löschen des Zugriffs-Tokens.", "description": "Erstelle langlebige Zugriffstoken, damit deine Skripte mit deiner Home Assistant-Instanz interagieren können. Jedes Token ist ab der Erstellung für 10 Jahre gültig. Die folgenden langlebigen Zugriffstoken sind derzeit aktiv.", @@ -2757,6 +2833,7 @@ "header": "Langlebige Zugangs-Token", "last_used": "Zuletzt verwendet am {date} in {location}", "learn_auth_requests": "Erfahre, wie du authentifizierte Anfragen stellen kannst.", + "name": "Name", "not_used": "Wurde noch nie benutzt", "prompt_copy_token": "Kopiere deinen Zugangs-Token. Er wird nicht wieder angezeigt werden.", "prompt_name": "Name?" @@ -2808,7 +2885,8 @@ "error_no_theme": "Keine Themen verfügbar.", "header": "Thema", "link_promo": "Erfahre mehr über Themen", - "primary_color": "Primärfarbe" + "primary_color": "Primärfarbe", + "reset": "zurücksetzen" }, "vibrate": { "description": "Aktiviere oder deaktiviere die Vibration an diesem Gerät, wenn du Geräte steuerst.", @@ -2822,6 +2900,7 @@ } }, "sidebar": { + "done": "fertig", "external_app_configuration": "App-Konfiguration", "sidebar_toggle": "Seitenleiste umschalten" } diff --git a/translations/frontend/en.json b/translations/frontend/en.json index c4308bd7a5..6d3537f927 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2692,7 +2692,7 @@ }, "raw_editor": { "confirm_remove_config_text": "We will automatically generate your Lovelace UI views with your areas and devices if you remove your Lovelace UI configuration.", - "confirm_remove_config_title": "Are you sure you want to remove your Lovelace UI configuration? We will automatically generate your Lovelace UI views with your areas and devices.", + "confirm_remove_config_title": "Are you sure you want to remove your Lovelace UI configuration?", "confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?", "confirm_unsaved_comments": "Your configuration contains comment(s), these will not be saved. Do you want to continue?", "error_invalid_config": "Your configuration is not valid: {error}", diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 10b6151b0d..45f8523416 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -507,6 +507,7 @@ "cancel": "Cancelar", "close": "Cerrar", "continue": "Continuar", + "copied": "Copiado", "delete": "Eliminar", "error_required": "Obligatorio", "loading": "Cargando", @@ -567,11 +568,27 @@ "loading_history": "Cargando historial de estado...", "no_history_found": "No se encontró historial de estado." }, + "logbook": { + "entries_not_found": "No se han encontrado entradas en el registro." + }, "media-browser": { "audio_not_supported": "Tu navegador no es compatible con el elemento de audio.", "choose_player": "Elige reproductor", "choose-source": "Elige la fuente", "class": { + "album": "Álbum", + "app": "Aplicación", + "artist": "Artista", + "channel": "Canal", + "composer": "Compositor", + "contributing_artist": "Artista colaborador", + "directory": "Biblioteca", + "episode": "Episodio", + "game": "Juego", + "genre": "Género", + "image": "Imagen", + "movie": "Película", + "music": "Música", "playlist": "Lista de reproducción", "podcast": "Podcast", "season": "Temporada", @@ -712,6 +729,7 @@ }, "more_info_control": { "controls": "Controles", + "details": "Detalles", "dismiss": "Descartar diálogo", "edit": "Editar entidad", "history": "Historial", @@ -2357,9 +2375,14 @@ "title": "Estados" }, "templates": { + "all_listeners": "Esta plantilla escucha todos los eventos de cambio de estado.", "description": "Las plantillas se muestran utilizando el motor de plantillas Jinja2 con algunas extensiones específicas de Home Assistant.", + "domain": "Dominio", "editor": "Editor de plantillas", + "entity": "Entidad", "jinja_documentation": "Documentación de plantilla Jinja2", + "listeners": "Esta plantilla escucha los siguientes eventos de cambio de estado:", + "no_listeners": "Esta plantilla no escucha ningún evento de cambio de estado y no se actualizará automáticamente.", "reset": "Reiniciar a la plantilla de demostración", "template_extensions": "Extensiones de plantilla de Home Assistant", "title": "Plantillas", @@ -2946,6 +2969,11 @@ "submit": "Enviar" }, "current_user": "Has iniciado sesión como {fullName}.", + "customize_sidebar": { + "button": "Editar", + "description": "También puedes mantener pulsada la cabecera de la barra lateral para activar el modo de edición.", + "header": "Cambiar el orden y ocultar elementos de la barra lateral" + }, "dashboard": { "description": "Selecciona un panel de control predeterminado para este dispositivo.", "dropdown_label": "Panel de control", @@ -2968,6 +2996,7 @@ "confirm_delete": "¿Estás seguro de que quieres eliminar el token de acceso para {name}?", "create": "Crear Token", "create_failed": "No se ha podido crear el token de acceso.", + "created": "Creado en {date}", "created_at": "Creado el {date}", "delete_failed": "Error al eliminar el token de acceso.", "description": "Crea tokens de acceso de larga duración para permitir que tus scripts interactúen con tu instancia de Home Assistant. Cada token será válido por 10 años desde la creación. Los siguientes tokens de acceso de larga duración están actualmente activos.", @@ -2975,9 +3004,10 @@ "header": "Tokens de acceso de larga duración", "last_used": "Último uso el {date} desde {location}", "learn_auth_requests": "Aprende cómo realizar solicitudes autenticadas.", + "name": "Nombre", "not_used": "Nunca ha sido usado", "prompt_copy_token": "Copia tu token de acceso. No se mostrará de nuevo.", - "prompt_name": "¿Nombre?" + "prompt_name": "Dale un nombre al token" }, "mfa_setup": { "close": "Cerrar", @@ -3041,6 +3071,7 @@ } }, "sidebar": { + "done": "Hecho", "external_app_configuration": "Configuración de la aplicación", "sidebar_toggle": "Alternar barra lateral" } diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 714fc4320a..0634745577 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -507,6 +507,7 @@ "cancel": "Annuler", "close": "Fermer", "continue": "Continuer", + "copied": "Copié", "delete": "Supprimer", "error_required": "Obligatoire", "loading": "Chargement", @@ -567,10 +568,35 @@ "loading_history": "Chargement de l'historique des valeurs ...", "no_history_found": "Aucun historique des valeurs trouvé." }, + "logbook": { + "entries_not_found": "Aucune entrée trouvée dans le journal." + }, "media-browser": { "audio_not_supported": "Votre navigateur ne prend pas en charge l'élément audio.", "choose_player": "Choisissez le lecteur", "choose-source": "Choisissez la source", + "class": { + "album": "Album", + "app": "App", + "artist": "Artiste", + "channel": "Canal", + "composer": "Compositeur", + "contributing_artist": "Artiste collaborateur", + "directory": "Bibliothèque", + "episode": "Épisode", + "game": "Jeu", + "genre": "Genre", + "image": "Image", + "movie": "Film", + "music": "Musique", + "playlist": "Liste de lecture", + "podcast": "Podcast", + "season": "Saison", + "track": "Piste", + "tv_show": "Émission de télévision", + "url": "Url", + "video": "Vidéo" + }, "content-type": { "album": "Album", "artist": "Artiste", @@ -703,6 +729,7 @@ }, "more_info_control": { "controls": "Contrôles", + "details": "Détails", "dismiss": "Fermer la fenêtre de dialogue", "edit": "Modifier l'entité", "history": "Historique", @@ -934,9 +961,12 @@ "service_data": "Données du service" }, "wait_for_trigger": { - "label": "Attendre le déclencheur" + "continue_timeout": "Continuer à l'expiration du délai", + "label": "Attendre le déclencheur", + "timeout": "Délai d'expiration (optionnel)" }, "wait_template": { + "continue_timeout": "Continuer à l'expiration du délai", "label": "Attendre", "timeout": "Délai d'expiration (optionnel)", "wait_template": "Template d'attente" @@ -1000,7 +1030,9 @@ "time": { "after": "Après", "before": "Avant", - "label": "Heure" + "label": "Heure", + "type_input": "Valeur d'une aide de date/heure", + "type_value": "Temps fixe" }, "zone": { "entity": "Entité avec localisation", @@ -1567,6 +1599,7 @@ "reload_restart_confirm": "Redémarrer Home Assistant pour finaliser le rechargement de cette intégration", "rename": "Renommer", "restart_confirm": "Redémarrer Home Assistant pour terminer la suppression de cette intégration", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Modifier les paramètres pour {integration}", "system_options": "Options système", "system_options_button": "Options système pour {integration}", @@ -2342,9 +2375,14 @@ "title": "États" }, "templates": { + "all_listeners": "Ce modèle écoute tous les événements de changement d'état.", "description": "Les modèles sont rendus à l'aide du moteur de modèles Jinja2 avec certaines extensions spécifiques de Home Assistant.", + "domain": "Domaine", "editor": "Éditeur de modèles", + "entity": "Entité", "jinja_documentation": "Documentation de modèle Jinja2", + "listeners": "Ce modèle écoute les événements de changement d'état suivants:", + "no_listeners": "Ce modèle n'écoute aucun événement de changement d'état et ne sera pas mis à jour automatiquement.", "reset": "Réinitialiser au modèle de démonstration", "template_extensions": "Extensions de modèles de Home Assistant", "title": "Modèle", @@ -2931,6 +2969,11 @@ "submit": "Envoyer" }, "current_user": "Vous êtes actuellement connecté en tant que {fullName}.", + "customize_sidebar": { + "button": "Modifier", + "description": "Vous pouvez également appuyer et maintenir l'en-tête de la barre latérale pour activer le mode d'édition.", + "header": "Modifier l'ordre et masquer les éléments de la barre latérale" + }, "dashboard": { "description": "Choisissez un tableau de bord par défaut pour cet appareil.", "dropdown_label": "Tableau de bord", @@ -2953,6 +2996,7 @@ "confirm_delete": "Êtes-vous sûr de vouloir supprimer le jeton d'accès de {name} ?", "create": "Créer un jeton", "create_failed": "Impossible de créer le jeton d'accès.", + "created": "Créé le {date}", "created_at": "Créé le {date}", "delete_failed": "Impossible de supprimer le jeton d'accès.", "description": "Créez des jetons d'accès de longue durée pour permettre à vos scripts d'interagir avec votre instance de Home Assistant. Chaque jeton sera valable 10 ans à compter de sa création. Les jetons d'accès longue durée suivants sont actuellement actifs.", @@ -2960,6 +3004,7 @@ "header": "Jetons d'accès de longue durée", "last_used": "Dernière utilisation le {date} à partir de {location}", "learn_auth_requests": "Apprenez comment faire des demandes authentifiées.", + "name": "Nom", "not_used": "N'a jamais été utilisé", "prompt_copy_token": "Copiez votre jeton d'accès. Il ne sera plus affiché à nouveau.", "prompt_name": "Nom ?" @@ -3026,6 +3071,7 @@ } }, "sidebar": { + "done": "Terminé", "external_app_configuration": "Configuration de l'application", "sidebar_toggle": "Activer la barre latérale" } diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index f835f9e909..dcb6211a8a 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -507,6 +507,7 @@ "cancel": "Avbryt", "close": "Lukk", "continue": "Fortsette", + "copied": "Kopiert", "delete": "Slett", "error_required": "Nødvendig", "loading": "Laster", @@ -2374,11 +2375,14 @@ "title": "Tilstander" }, "templates": { + "all_listeners": "Denne malen lytter etter alle tilstandsfornede hendelser.", "description": "Maler blir rendret ved hjelp av Jinja2-malmotoren med noen spesifikke utvidelser for Home Assistant.", "domain": "Domene", "editor": "Maleditor", "entity": "Entitet", "jinja_documentation": "Jinja2 mal dokumentasjon", + "listeners": "Denne malen lytter etter følgende tilstandsfor endrede hendelser:", + "no_listeners": "Denne malen lytter ikke etter eventuelle tilstandsfornedne hendelser og oppdateres ikke automatisk.", "reset": "Tilbakestill til demomal", "template_extensions": "Mal utvidelser for Home Assistant", "title": "Mal", @@ -2966,6 +2970,8 @@ }, "current_user": "Du er logget inn som {fullName}.", "customize_sidebar": { + "button": "Redigere", + "description": "Du kan også trykke på og holde nede overskriften på sidefeltet for å aktivere redigeringsmodus.", "header": "Endre rekkefølgen og skjul elementer fra sidepanelet" }, "dashboard": { @@ -3001,7 +3007,7 @@ "name": "Navn", "not_used": "Har aldri blitt brukt", "prompt_copy_token": "Kopier tilgangstoken. Det blir ikke vist igjen.", - "prompt_name": "Navn?" + "prompt_name": "Gi tokenet et navn" }, "mfa_setup": { "close": "Lukk", diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index b4f6f2fbce..f11bf23dca 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -507,6 +507,7 @@ "cancel": "Annuleren", "close": "Sluiten", "continue": "Ga verder", + "copied": "Gekopieerd", "delete": "Verwijderen", "error_required": "Verplicht", "loading": "Bezig met laden", @@ -2374,11 +2375,14 @@ "title": "Toestanden" }, "templates": { + "all_listeners": "Deze template luistert naar alle gebeurtenissen met gewijzigde status.", "description": "Sjablonen worden weergegeven met de Jinja2-sjabloonediter samen met enkele extensies van Home Assistant.", "domain": "Domein", "editor": "Sjabloonediter", "entity": "Entiteit", "jinja_documentation": "Jinja2-sjabloondocumentatie", + "listeners": "Deze template luistert naar de volgende gebeurtenissen met gewijzigde status:", + "no_listeners": "Deze template luistert niet naar gebeurtenissen met statuswijziging en wordt niet automatisch bijgewerkt.", "reset": "Resetten naar demosjabloon", "template_extensions": "Home Assistant sjabloon extensiesHome Assistant", "title": "Sjablonen", @@ -3003,7 +3007,7 @@ "name": "Naam", "not_used": "Is nog nooit gebruikt", "prompt_copy_token": "Kopieer je toegangstoken. Het wordt niet meer getoond.", - "prompt_name": "Naam?" + "prompt_name": "Geef het token een naam" }, "mfa_setup": { "close": "Sluiten", diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index ffee82cabd..9bc262d724 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -507,6 +507,7 @@ "cancel": "Anuluj", "close": "Zamknij", "continue": "Kontynuuj", + "copied": "Skopiowano", "delete": "Usuń", "error_required": "To pole jest wymagane", "loading": "Ładowanie", diff --git a/translations/frontend/ro.json b/translations/frontend/ro.json index b0bcdb48cd..0262cb7b0d 100644 --- a/translations/frontend/ro.json +++ b/translations/frontend/ro.json @@ -489,6 +489,7 @@ "back": "Înapoi", "cancel": "Revocare", "close": "Închide", + "copied": "Copiat", "delete": "Șterge", "error_required": "Necesar", "loading": "Se încarcă", @@ -544,6 +545,32 @@ "loading_history": "Încărcarea istoricului de stare ...", "no_history_found": "Nici un istoric de stare nu a fost găsit." }, + "logbook": { + "entries_not_found": "Nu s-au găsit intrări în jurnal." + }, + "media-browser": { + "class": { + "album": "Album", + "app": "Aplicație", + "artist": "Artist", + "channel": "Canal", + "composer": "Compozitor", + "contributing_artist": "Artist contribuitor", + "directory": "Bibliotecă", + "episode": "Episod", + "game": "Joc", + "genre": "Gen", + "image": "Imagine", + "movie": "Film", + "music": "Muzică", + "playlist": "Playlist", + "podcast": "Podcast", + "season": "Sezon", + "tv_show": "Emisiune TV", + "url": "Url", + "video": "Video" + } + }, "related-items": { "area": "Zonă", "automation": "Parte din următoarele automatizări", @@ -647,6 +674,7 @@ "yaml_not_editable": "Setările acestei entități nu se pot edita din interfața grafica. Numai entitățile configurate in interfața grafica sunt configurabile din interfața grafica." }, "more_info_control": { + "details": "Detalii", "dismiss": "Se respinge dialogul", "edit": "Editează entitatea", "person": { @@ -850,7 +878,13 @@ "label": "Cheama serviciu", "service_data": "Date serviciu" }, + "wait_for_trigger": { + "continue_timeout": "Continua la timeout", + "label": "Așteptați declanșatorul", + "timeout": "Timeout (optional)" + }, "wait_template": { + "continue_timeout": "Continuați la expirare", "label": "Asteptare", "timeout": "Timeout (opțional)", "wait_template": "Sablon Asteptare" @@ -1260,6 +1294,7 @@ }, "integrations": { "add_integration": "Adăugați integrare", + "attention": "Atenție necesară", "caption": "Integrări", "config_entry": { "delete": "Șterge", @@ -1317,6 +1352,7 @@ "none": "Nimic nu a fost configurat încă", "none_found": "Nu s-au găsit integrări", "none_found_detail": "Ajustați criteriile de căutare.", + "reconfigure": "Reconfigurați", "rename_dialog": "Editați numele acestei intrări de configurare", "rename_input_label": "Introdu nume", "search": "Căutare integrari" @@ -1848,9 +1884,14 @@ "title": "Status" }, "templates": { + "all_listeners": "Acest șablon ascultă următoarele evenimente modificate de stare:", "description": "Șabloanele sunt redate utilizând motorul de șablon Jinja2 cu unele extensii specifice Home Assistant.", + "domain": "Domeniu", "editor": "Editor șabloane", + "entity": "Entitate", "jinja_documentation": "Șablon documentație Jinja2", + "listeners": "Acest șablon ascultă următoarele evenimente modificate de stare:", + "no_listeners": "Acest șablon ascultă următoarele evenimente modificate de stare:", "template_extensions": "Șabloane de extensie pentru Home Assistant", "title": "Sabloane", "unknown_error_template": "Sa produs o eroare de randare necunoscută." @@ -2353,6 +2394,11 @@ "submit": "Trimite" }, "current_user": "În prezent sunteți conectat ca {fullName}.", + "customize_sidebar": { + "button": "Editeaza", + "description": "De asemenea, puteți apăsa și ține apăsat antetul barei laterale pentru a activa modul de editare.", + "header": "Schimbați ordinea și ascundeți elementele din bara laterală" + }, "dashboard": { "description": "Alegeți un tablou de bord implicit pentru acest dispozitiv.", "dropdown_label": "Tablou de bord", @@ -2375,6 +2421,7 @@ "confirm_delete": "Sigur doriti sa stergeti tokenul de acces pentru {name}?", "create": "Creaza un Token", "create_failed": "Crearea tokenului de acces eşuată", + "created": "Creat in {data}", "created_at": "Creat in {date}", "delete_failed": "Ştergerea tokenului de acces eşuată", "description": "Creați tokenuri de acces cu durată lungă de viață pentru a permite script-urilor dvs. să interacționeze cu instanța dvs. Home Assistant. Fiecare token va fi valabil timp de 10 ani de la creatie. Următoarele tokenuri de acces de lungă durată sunt active la ora actuala.", @@ -2382,6 +2429,7 @@ "header": "Tokenuri de acces de lunga durata", "last_used": "Ultima utilizare la {date} din {location}", "learn_auth_requests": "Aflați cum să faceți cereri autentificate.", + "name": "Nume", "not_used": "Nu a fost utilizat niciodata", "prompt_copy_token": "Copia token-ul de acces. Acesta nu va fi afișat din nou.", "prompt_name": "Nume?" @@ -2443,6 +2491,7 @@ } }, "sidebar": { + "done": "Terminat", "external_app_configuration": "Configurație aplicație", "sidebar_toggle": "Schimbati bara laterală" } diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 476f5bdb03..04aa43066e 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -507,6 +507,7 @@ "cancel": "Отменить", "close": "Закрыть", "continue": "Продолжить", + "copied": "Скопировано", "delete": "Удалить", "error_required": "Обязательное поле", "loading": "Загрузка", @@ -580,6 +581,7 @@ "artist": "Исполнитель", "channel": "Канал", "composer": "Композитор", + "contributing_artist": "Соисполнитель", "directory": "Библиотека", "episode": "Эпизод", "game": "Игра", @@ -2373,11 +2375,14 @@ "title": "Состояния" }, "templates": { + "all_listeners": "Этот шаблон отслеживает все события изменения состояния.", "description": "Здесь Вы можете протестировать поведение шаблонов. В Home Assistant используется шаблонизатор Jinja2 с некоторыми специальными расширениями.", "domain": "Домен", "editor": "Редактор шаблонов", "entity": "Объект", "jinja_documentation": "Узнайте больше о шаблонизаторе Jinja2", + "listeners": "Этот шаблон отслеживает следующие события изменения состояния:", + "no_listeners": "Этот шаблон не отслеживает события изменения состояния и не обновляется автоматически.", "reset": "Вернуться к демонстрационному шаблону", "template_extensions": "Узнайте больше о шаблонах Home Assistant", "title": "Шаблоны", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 5343241299..452cf1c2e5 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -507,6 +507,7 @@ "cancel": "取消", "close": "關閉", "continue": "繼續", + "copied": "已複製", "delete": "刪除", "error_required": "必填", "loading": "讀取中", From 3bca32c6d552cbbefb28165a2f4a8aad8f34cb83 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 08:42:09 +0200 Subject: [PATCH 136/146] Disable cloud expose controls when yaml filter (#6990) --- src/components/ha-button-menu.ts | 28 ++-- src/panels/config/cloud/alexa/cloud-alexa.ts | 125 +++++++++--------- .../cloud-google-assistant.ts | 125 +++++++++--------- 3 files changed, 144 insertions(+), 134 deletions(-) diff --git a/src/components/ha-button-menu.ts b/src/components/ha-button-menu.ts index e325925d57..7adc266a56 100644 --- a/src/components/ha-button-menu.ts +++ b/src/components/ha-button-menu.ts @@ -1,17 +1,16 @@ -import { - customElement, - html, - TemplateResult, - LitElement, - CSSResult, - css, - query, - property, -} from "lit-element"; import "@material/mwc-button"; import "@material/mwc-menu"; -import type { Menu, Corner } from "@material/mwc-menu"; - +import type { Corner, Menu } from "@material/mwc-menu"; +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + query, + TemplateResult, +} from "lit-element"; import "./ha-icon-button"; @customElement("ha-button-menu") @@ -22,6 +21,8 @@ export class HaButtonMenu extends LitElement { @property({ type: Boolean }) public activatable = false; + @property({ type: Boolean }) public disabled = false; + @query("mwc-menu") private _menu?: Menu; public get items() { @@ -48,6 +49,9 @@ export class HaButtonMenu extends LitElement { } private _handleClick(): void { + if (this.disabled) { + return; + } this._menu!.anchor = this; this._menu!.show(); } diff --git a/src/panels/config/cloud/alexa/cloud-alexa.ts b/src/panels/config/cloud/alexa/cloud-alexa.ts index 948d542680..9fb80369d9 100644 --- a/src/panels/config/cloud/alexa/cloud-alexa.ts +++ b/src/panels/config/cloud/alexa/cloud-alexa.ts @@ -124,6 +124,26 @@ class CloudAlexa extends LitElement { ? exposedCards : notExposedCards; + const iconButton = html` + + `; + target.push(html`
    @@ -139,67 +159,50 @@ class CloudAlexa extends LitElement { .map((ifc) => ifc.replace(/(Alexa.|Controller)/g, "")) .join(", ")} - - - - - - ${this.hass!.localize( - "ui.panel.config.cloud.alexa.expose_entity" - )} - - - - ${this.hass!.localize( - "ui.panel.config.cloud.alexa.dont_expose_entity" - )} - - - - ${this.hass!.localize( - "ui.panel.config.cloud.alexa.follow_domain" - )} - - - + ${!emptyFilter + ? html`${iconButton}` + : html` + ${iconButton} + + ${this.hass!.localize( + "ui.panel.config.cloud.google.expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.dont_expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.follow_domain" + )} + + + `}
    diff --git a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts index 49b56ea32c..40067a0eaf 100644 --- a/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts +++ b/src/panels/config/cloud/google-assistant/cloud-google-assistant.ts @@ -130,6 +130,26 @@ class CloudGoogleAssistant extends LitElement { ? exposedCards : notExposedCards; + const iconButton = html` + + `; + target.push(html`
    @@ -144,67 +164,50 @@ class CloudGoogleAssistant extends LitElement { .map((trait) => trait.substr(trait.lastIndexOf(".") + 1)) .join(", ")} - - - - - - ${this.hass!.localize( - "ui.panel.config.cloud.google.expose_entity" - )} - - - - ${this.hass!.localize( - "ui.panel.config.cloud.google.dont_expose_entity" - )} - - - - ${this.hass!.localize( - "ui.panel.config.cloud.google.follow_domain" - )} - - - + ${!emptyFilter + ? html`${iconButton}` + : html` + ${iconButton} + + ${this.hass!.localize( + "ui.panel.config.cloud.google.expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.dont_expose_entity" + )} + + + + ${this.hass!.localize( + "ui.panel.config.cloud.google.follow_domain" + )} + + + `}
    ${entity.might_2fa ? html` From 239f5f1a2ff37c44355301835b48fef2d2eddca7 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 15 Sep 2020 11:14:25 +0200 Subject: [PATCH 137/146] Add snapshot support for new media folder (#6889) --- hassio/src/snapshots/hassio-snapshots.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/hassio/src/snapshots/hassio-snapshots.ts b/hassio/src/snapshots/hassio-snapshots.ts index 3bc9bfcc45..1f594a125a 100644 --- a/hassio/src/snapshots/hassio-snapshots.ts +++ b/hassio/src/snapshots/hassio-snapshots.ts @@ -79,6 +79,7 @@ class HassioSnapshots extends LitElement { }, { slug: "ssl", name: "SSL", checked: true }, { slug: "share", name: "Share", checked: true }, + { slug: "media", name: "Media", checked: true }, { slug: "addons/local", name: "Local add-ons", checked: true }, ]; From 19862159196c6cdbe6abda2fb9501ddb8059a7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 15 Sep 2020 11:15:22 +0200 Subject: [PATCH 138/146] Remove version check (#6984) --- hassio/src/system/hassio-host-info.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index 5f44703ac3..94400402ae 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -14,7 +14,6 @@ import { TemplateResult, } from "lit-element"; import memoizeOne from "memoize-one"; -import { atLeastVersion } from "../../../src/common/config/version"; import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-card"; @@ -85,8 +84,7 @@ class HassioHostInfo extends LitElement { ` : ""} - ${this.hostInfo.features.includes("network") && - atLeastVersion(this.hass.config.version, 0, 115) + ${this.hostInfo.features.includes("network") ? html` IP address From 239e817779e2ed71036cd3ee0ddd56be82b1c2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 15 Sep 2020 11:15:58 +0200 Subject: [PATCH 139/146] Rename upgrade -> update (#7013) --- hassio/src/dashboard/hassio-update.ts | 2 +- hassio/src/system/hassio-supervisor-info.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index eec4097e54..a95eafbd76 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -153,7 +153,7 @@ export class HassioUpdate extends LitElement { item.progress = true; const confirmed = await showConfirmationDialog(this, { title: `Update ${item.name}`, - text: `Are you sure you want to upgrade ${item.name} to version ${item.version}?`, + text: `Are you sure you want to update ${item.name} to version ${item.version}?`, confirmText: "update", dismissText: "cancel", }); diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index 12af46d091..fcec51d607 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -213,7 +213,7 @@ class HassioSupervisorInfo extends LitElement { const confirmed = await showConfirmationDialog(this, { title: "Update supervisor", - text: `Are you sure you want to upgrade supervisor to version ${this.supervisorInfo.version_latest}?`, + text: `Are you sure you want to update supervisor to version ${this.supervisorInfo.version_latest}?`, confirmText: "update", dismissText: "cancel", }); From d5cd288fe84ca4866e597ce017ef825f06cf7f2b Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 15 Sep 2020 19:18:11 +0800 Subject: [PATCH 140/146] Temporarily remove exoplayer (#7015) --- src/components/ha-hls-player.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/ha-hls-player.ts b/src/components/ha-hls-player.ts index b513413d23..b613a5ad05 100644 --- a/src/components/ha-hls-player.ts +++ b/src/components/ha-hls-player.ts @@ -12,7 +12,6 @@ import { } from "lit-element"; import { fireEvent } from "../common/dom/fire_event"; import { nextRender } from "../common/util/render-status"; -import { getExternalConfig } from "../external_app/external_config"; import type { HomeAssistant } from "../types"; type HLSModule = typeof import("hls.js"); @@ -94,11 +93,7 @@ class HaHLSPlayer extends LitElement { } private async _getUseExoPlayer(): Promise { - if (!this.hass!.auth.external || !this.allowExoPlayer) { - return false; - } - const externalConfig = await getExternalConfig(this.hass!.auth.external); - return externalConfig && externalConfig.hasExoPlayer; + return false; } private async _startHls(): Promise { @@ -140,7 +135,10 @@ class HaHLSPlayer extends LitElement { this._videoEl.style.visibility = "hidden"; await this.hass!.auth.external!.sendMessage({ type: "exoplayer/play_hls", - payload: new URL(url, window.location.href).toString(), + payload: { + url: new URL(url, window.location.href).toString(), + muted: this.muted, + }, }); } From 9c80776d8c7c39ceebc1ef18a0fca80a83c629a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 15 Sep 2020 13:20:17 +0200 Subject: [PATCH 141/146] Refresh snapshots on first update (#7014) --- hassio/src/snapshots/hassio-snapshots.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/src/snapshots/hassio-snapshots.ts b/hassio/src/snapshots/hassio-snapshots.ts index 1f594a125a..18f2b1d0ed 100644 --- a/hassio/src/snapshots/hassio-snapshots.ts +++ b/hassio/src/snapshots/hassio-snapshots.ts @@ -242,7 +242,7 @@ class HassioSnapshots extends LitElement { protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); - this._updateSnapshots(); + this.refreshData(); } protected updated(changedProps: PropertyValues) { From 7d862d6f2ac0c47d057e4d5fb3ccdf3a4be41a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 15 Sep 2020 13:20:43 +0200 Subject: [PATCH 142/146] Use helper to generate documentation URL (#7012) --- .../media-player/ha-media-player-browse.ts | 8 ++- .../config-flow/step-flow-pick-handler.ts | 3 +- .../config/automation/ha-automation-editor.ts | 21 ++++++-- .../dialog-manage-cloudhook.ts | 8 ++- .../config/cloud/register/cloud-register.js | 13 ++++- .../config/customize/ha-form-customize.js | 16 ++++-- src/panels/config/info/ha-config-info.ts | 5 +- .../integration-panels/zwave/zwave-network.ts | 12 +++-- .../config/person/dialog-person-detail.ts | 14 +++-- src/panels/config/scene/ha-scene-dashboard.ts | 3 +- src/panels/config/script/ha-script-editor.ts | 54 ++++++++++--------- src/panels/config/script/ha-script-picker.ts | 3 +- .../event/developer-tools-event.js | 7 ++- .../template/developer-tools-template.ts | 6 ++- .../card-editor/hui-dialog-edit-card.ts | 5 +- .../editor/get-card-documentation-url.ts | 13 +++-- .../lovelace/editor/hui-dialog-save-config.ts | 25 +++++---- src/panels/lovelace/hui-root.ts | 5 +- src/panels/profile/ha-pick-theme-row.ts | 26 +++++---- .../profile/ha-push-notifications-row.js | 7 ++- src/util/documentation-url.ts | 7 +++ .../util/generate-documentation-url.spec.ts | 19 +++++++ 22 files changed, 193 insertions(+), 87 deletions(-) create mode 100644 src/util/documentation-url.ts create mode 100644 test-mocha/util/generate-documentation-url.spec.ts diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 0b14444536..f63f3592e4 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -22,6 +22,7 @@ import { styleMap } from "lit-html/directives/style-map"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; +import type { MediaPlayerItem } from "../../data/media-player"; import { browseLocalMediaPlayer, browseMediaPlayer, @@ -30,11 +31,11 @@ import { MediaPickedEvent, MediaPlayerBrowseAction, } from "../../data/media-player"; -import type { MediaPlayerItem } from "../../data/media-player"; import { showAlertDialog } from "../../dialogs/generic/show-dialog-box"; import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer"; import { haStyle } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; +import { documentationUrl } from "../../util/documentation-url"; import "../entity/ha-entity-picker"; import "../ha-button-menu"; import "../ha-card"; @@ -511,7 +512,10 @@ export class HaMediaPlayerBrowse extends LitElement {

    Check the documentation${this.hass.localize( diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index f76f44ac67..5a39e7eac8 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -39,6 +39,7 @@ import "../../../layouts/ha-app-layout"; import "../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; import "../ha-config-section"; import { configSections } from "../ha-panel-config"; import "./action/ha-automation-action"; @@ -159,7 +160,10 @@ export class HaAutomationEditor extends LitElement { "ui.panel.config.automation.editor.modes.description", "documentation_link", html`${this.hass.localize( @@ -247,7 +251,10 @@ export class HaAutomationEditor extends LitElement { )}

    @@ -276,7 +283,10 @@ export class HaAutomationEditor extends LitElement { )}

    @@ -305,7 +315,10 @@ export class HaAutomationEditor extends LitElement { )}

    diff --git a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts index b590848f99..1561ed7420 100644 --- a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts +++ b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts @@ -14,6 +14,7 @@ import type { HaPaperDialog } from "../../../../components/dialog/ha-paper-dialo import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; +import { documentationUrl } from "../../../../util/documentation-url"; import { WebhookDialogParams } from "./show-dialog-manage-cloudhook"; const inputLabel = "Public URL – Click to copy to clipboard"; @@ -37,8 +38,11 @@ export class DialogManageCloudhook extends LitElement { const { webhook, cloudhook } = this._params; const docsUrl = webhook.domain === "automation" - ? "https://www.home-assistant.io/docs/automation/trigger/#webhook-trigger" - : `https://www.home-assistant.io/integrations/${webhook.domain}/`; + ? documentationUrl( + this.hass!, + "/docs/automation/trigger/#webhook-trigger" + ) + : documentationUrl(this.hass!, `/integrations/${webhook.domain}/`); return html`

    diff --git a/src/panels/config/cloud/register/cloud-register.js b/src/panels/config/cloud/register/cloud-register.js index 8b5b04c5b1..382effb34f 100644 --- a/src/panels/config/cloud/register/cloud-register.js +++ b/src/panels/config/cloud/register/cloud-register.js @@ -8,6 +8,7 @@ import "../../../../layouts/hass-subpage"; import { EventsMixin } from "../../../../mixins/events-mixin"; import LocalizeMixin from "../../../../mixins/localize-mixin"; import "../../../../styles/polymer-ha-style"; +import { documentationUrl } from "../../../../util/documentation-url"; import "../../ha-config-section"; /* @@ -70,8 +71,8 @@ class CloudRegister extends LocalizeMixin(EventsMixin(PolymerElement)) {

    [[localize('ui.panel.config.cloud.register.information4')]]

    @@ -137,6 +138,14 @@ class CloudRegister extends LocalizeMixin(EventsMixin(PolymerElement)) { } } + _computeDocumentationUrlTos(hass) { + return documentationUrl(hass, "/tos/"); + } + + _computeDocumentationUrlPrivacy(hass) { + return documentationUrl(hass, "/privacy/"); + } + _handleRegister() { let invalid = false; diff --git a/src/panels/config/customize/ha-form-customize.js b/src/panels/config/customize/ha-form-customize.js index 4c85d7b27f..2ad523125d 100644 --- a/src/panels/config/customize/ha-form-customize.js +++ b/src/panels/config/customize/ha-form-customize.js @@ -6,10 +6,11 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; import { PolymerElement } from "@polymer/polymer/polymer-element"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import LocalizeMixin from "../../../mixins/localize-mixin"; -import hassAttributeUtil from "../../../util/hass-attributes-util"; -import "./ha-form-customize-attributes"; -import "../ha-form-style"; import "../../../styles/polymer-ha-style"; +import { documentationUrl } from "../../../util/documentation-url"; +import hassAttributeUtil from "../../../util/hass-attributes-util"; +import "../ha-form-style"; +import "./ha-form-customize-attributes"; class HaFormCustomize extends LocalizeMixin(PolymerElement) { static get template() { @@ -30,7 +31,7 @@ class HaFormCustomize extends LocalizeMixin(PolymerElement) {
    [[localize('ui.panel.config.customize.warning.include_sentence')]] [[localize('ui.panel.config.customize.warning.include_link')]]

    diff --git a/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts b/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts index 7d220ba93c..b9860c0589 100644 --- a/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts +++ b/src/panels/config/integrations/integration-panels/zwave/zwave-network.ts @@ -1,20 +1,20 @@ -import "../../../../../components/ha-icon-button"; -import "../../../../../components/ha-circular-progress"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import "../../../../../components/buttons/ha-call-api-button"; import "../../../../../components/buttons/ha-call-service-button"; import "../../../../../components/ha-card"; +import "../../../../../components/ha-circular-progress"; import "../../../../../components/ha-icon"; +import "../../../../../components/ha-icon-button"; import "../../../../../components/ha-service-description"; import { fetchNetworkStatus, @@ -26,6 +26,7 @@ import { } from "../../../../../data/zwave"; import { haStyle } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; +import { documentationUrl } from "../../../../../util/documentation-url"; import "../../../ha-config-section"; @customElement("zwave-network") @@ -71,7 +72,10 @@ export class ZwaveNetwork extends LitElement { )}

    diff --git a/src/panels/config/person/dialog-person-detail.ts b/src/panels/config/person/dialog-person-detail.ts index 94c50807c1..043d407607 100644 --- a/src/panels/config/person/dialog-person-detail.ts +++ b/src/panels/config/person/dialog-person-detail.ts @@ -4,23 +4,24 @@ import { css, CSSResult, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import memoizeOne from "memoize-one"; -import "../../../components/ha-picture-upload"; -import type { HaPictureUpload } from "../../../components/ha-picture-upload"; import "../../../components/entity/ha-entities-picker"; import { createCloseHeading } from "../../../components/ha-dialog"; +import "../../../components/ha-picture-upload"; +import type { HaPictureUpload } from "../../../components/ha-picture-upload"; import "../../../components/user/ha-user-picker"; import { PersonMutableParams } from "../../../data/person"; +import { CropOptions } from "../../../dialogs/image-cropper-dialog/show-image-cropper-dialog"; import { PolymerChangedEvent } from "../../../polymer-types"; import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; import { PersonDetailDialogParams } from "./show-dialog-person-detail"; -import { CropOptions } from "../../../dialogs/image-cropper-dialog/show-image-cropper-dialog"; const includeDomains = ["device_tracker"]; @@ -153,7 +154,10 @@ class DialogPersonDetail extends LitElement {

    ${this.scriptEntityId ? html` -
    - - - ${this.hass.localize("ui.card.script.execute")} - -
    + + + ${this.hass.localize("ui.card.script.execute")} + +
    ` : ``} @@ -227,7 +233,7 @@ export class HaScriptEditor extends LitElement { )}

    diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 99b6dce17e..d7b713596c 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -25,6 +25,7 @@ import { configSections } from "../ha-panel-config"; import "../../../components/ha-svg-icon"; import { mdiPlus } from "@mdi/js"; import { stateIcon } from "../../../common/entity/state_icon"; +import { documentationUrl } from "../../../util/documentation-url"; @customElement("ha-script-picker") class HaScriptPicker extends LitElement { @@ -187,7 +188,7 @@ class HaScriptPicker extends LitElement { ${this.hass.localize("ui.panel.config.script.picker.introduction")}

    diff --git a/src/panels/developer-tools/event/developer-tools-event.js b/src/panels/developer-tools/event/developer-tools-event.js index 4466d48f10..ec7526fee9 100644 --- a/src/panels/developer-tools/event/developer-tools-event.js +++ b/src/panels/developer-tools/event/developer-tools-event.js @@ -9,6 +9,7 @@ import "../../../components/ha-code-editor"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; import { EventsMixin } from "../../../mixins/events-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin"; +import { documentationUrl } from "../../../util/documentation-url"; import "../../../styles/polymer-ha-style"; import "./event-subscribe-card"; import "./events-list"; @@ -61,7 +62,7 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) {

    [[localize( 'ui.panel.developer-tools.tabs.events.description' )]] @@ -149,6 +150,10 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) { } } + _computeDocumentationUrl(hass) { + return documentationUrl(hass, "/docs/configuration/events/"); + } + _computeValidJSON(parsedJSON) { return parsedJSON !== ERROR_SENTINEL; } diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index c9ca775fbf..d41e8fed1e 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -19,6 +19,7 @@ import { } from "../../../data/ws-templates"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; const DEMO_TEMPLATE = `{## Imitate available variables: ##} {% set my_test_json = { @@ -107,7 +108,10 @@ class HaPanelDevTemplate extends LitElement {

  • diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index 87d5ea3115..e700cd097b 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -115,7 +115,10 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { const oldConfig = changedProps.get("_cardConfig") as LovelaceCardConfig; if (oldConfig?.type !== this._cardConfig!.type) { - this._documentationURL = getCardDocumentationURL(this._cardConfig!.type); + this._documentationURL = getCardDocumentationURL( + this.hass, + this._cardConfig!.type + ); } } diff --git a/src/panels/lovelace/editor/get-card-documentation-url.ts b/src/panels/lovelace/editor/get-card-documentation-url.ts index 4d1e989a1d..e810f6bb4f 100644 --- a/src/panels/lovelace/editor/get-card-documentation-url.ts +++ b/src/panels/lovelace/editor/get-card-documentation-url.ts @@ -1,14 +1,17 @@ import { - getCustomCardEntry, CUSTOM_TYPE_PREFIX, + getCustomCardEntry, } from "../../../data/lovelace_custom_cards"; +import { HomeAssistant } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; -const coreDocumentationURLBase = "https://www.home-assistant.io/lovelace/"; - -export const getCardDocumentationURL = (type: string): string | undefined => { +export const getCardDocumentationURL = ( + hass: HomeAssistant, + type: string +): string | undefined => { if (type.startsWith(CUSTOM_TYPE_PREFIX)) { return getCustomCardEntry(type)?.documentationURL; } - return `${coreDocumentationURLBase}${type}`; + return `${documentationUrl(hass, "/lovelace/")}${type}`; }; diff --git a/src/panels/lovelace/editor/hui-dialog-save-config.ts b/src/panels/lovelace/editor/hui-dialog-save-config.ts index f056413258..3fd5997ffa 100644 --- a/src/panels/lovelace/editor/hui-dialog-save-config.ts +++ b/src/panels/lovelace/editor/hui-dialog-save-config.ts @@ -1,33 +1,32 @@ import "@material/mwc-button"; import "@material/mwc-icon-button/mwc-icon-button"; +import { mdiHelpCircle } from "@mdi/js"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; -import { mdiHelpCircle } from "@mdi/js"; import { fireEvent } from "../../../common/dom/fire_event"; +import { computeRTLDirection } from "../../../common/util/compute_rtl"; +import "../../../components/ha-circular-progress"; +import "../../../components/ha-dialog"; +import "../../../components/ha-formfield"; +import "../../../components/ha-svg-icon"; +import "../../../components/ha-switch"; +import "../../../components/ha-yaml-editor"; +import type { HassDialog } from "../../../dialogs/make-dialog-manager"; import { haStyleDialog } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; import type { SaveDialogParams } from "./show-save-config-dialog"; -import { computeRTLDirection } from "../../../common/util/compute_rtl"; -import type { HassDialog } from "../../../dialogs/make-dialog-manager"; -import "../../../components/ha-switch"; -import "../../../components/ha-formfield"; -import "../../../components/ha-yaml-editor"; -import "../../../components/ha-svg-icon"; -import "../../../components/ha-dialog"; -import "../../../components/ha-circular-progress"; const EMPTY_CONFIG = { views: [] }; -const coreDocumentationURLBase = "https://www.home-assistant.io/lovelace/"; - @customElement("hui-dialog-save-config") export class HuiSaveConfig extends LitElement implements HassDialog { @property({ attribute: false }) public hass?: HomeAssistant; @@ -68,7 +67,7 @@ export class HuiSaveConfig extends LitElement implements HassDialog { "ui.panel.lovelace.editor.save_config.header" )} diff --git a/src/panels/profile/ha-push-notifications-row.js b/src/panels/profile/ha-push-notifications-row.js index 7f0bbc0af0..528a447298 100644 --- a/src/panels/profile/ha-push-notifications-row.js +++ b/src/panels/profile/ha-push-notifications-row.js @@ -7,6 +7,7 @@ import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { pushSupported } from "../../components/ha-push-notifications-toggle"; import LocalizeMixin from "../../mixins/localize-mixin"; import "../../components/ha-settings-row"; +import { documentationUrl } from "../../util/documentation-url"; /* * @appliesMixin LocalizeMixin @@ -26,7 +27,7 @@ class HaPushNotificationsRow extends LocalizeMixin(PolymerElement) { [[_description(_platformLoaded, _pushSupported)]] [[localize('ui.panel.profile.push_notifications.link_promo')]] { + return `https://${ + hass.config.version.includes("b") ? "rc" : "www" + }.home-assistant.io${path}`; +}; diff --git a/test-mocha/util/generate-documentation-url.spec.ts b/test-mocha/util/generate-documentation-url.spec.ts new file mode 100644 index 0000000000..9e34e61448 --- /dev/null +++ b/test-mocha/util/generate-documentation-url.spec.ts @@ -0,0 +1,19 @@ +import * as assert from "assert"; +import { documentationUrl } from "../../src/util/documentation-url"; + +describe("Generate documentation URL", function () { + it("Generate documentation url for stable", function () { + assert.strictEqual( + // @ts-ignore + documentationUrl({ config: { version: "1.0.0" } }, "/blog"), + "https://www.home-assistant.io/blog" + ); + }); + it("Generate documentation url for rc", function () { + assert.strictEqual( + // @ts-ignore + documentationUrl({ config: { version: "1.0.0b0" } }, "/blog"), + "https://rc.home-assistant.io/blog" + ); + }); +}); From 9659c97978dcb19533cbfa3a67e74826f03e0e3d Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 15 Sep 2020 07:32:02 -0500 Subject: [PATCH 143/146] Update Error handling (#7007) Co-authored-by: Bram Kragten --- .../media-player/ha-media-player-browse.ts | 16 ++++---- src/data/media-player.ts | 38 +++++++++---------- .../media-browser/ha-panel-media-browser.ts | 8 ++-- .../hui-dialog-select-media-player.ts | 30 ++++++++------- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index f63f3592e4..62fe5d1236 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -26,7 +26,7 @@ import type { MediaPlayerItem } from "../../data/media-player"; import { browseLocalMediaPlayer, browseMediaPlayer, - BROWSER_SOURCE, + BROWSER_PLAYER, MediaClassBrowserSettings, MediaPickedEvent, MediaPlayerBrowseAction, @@ -109,9 +109,11 @@ export class HaMediaPlayerBrowse extends LitElement { text: this._renderError(this._error), }); } else { - return html`
    - ${this._renderError(this._error)} -
    `; + return html` +
    + ${this._renderError(this._error)} +
    + `; } } @@ -236,7 +238,7 @@ export class HaMediaPlayerBrowse extends LitElement { ${this._error ? html` -
    +
    ${this._renderError(this._error)}
    ` @@ -457,7 +459,7 @@ export class HaMediaPlayerBrowse extends LitElement { mediaContentType?: string ): Promise { const itemData = - this.entityId !== BROWSER_SOURCE + this.entityId !== BROWSER_PLAYER ? await browseMediaPlayer( this.hass, this.entityId, @@ -524,7 +526,7 @@ export class HaMediaPlayerBrowse extends LitElement {

    `; } - return html`err.message`; + return html`${err.message}`; } static get styles(): CSSResultArray { diff --git a/src/data/media-player.ts b/src/data/media-player.ts index 70b29c2d63..393c06cb71 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -1,23 +1,23 @@ +import { + mdiAccountMusic, + mdiAccountMusicOutline, + mdiAlbum, + mdiApplication, + mdiDramaMasks, + mdiFileMusic, + mdiFolder, + mdiGamepadVariant, + mdiImage, + mdiMovie, + mdiMusic, + mdiPlaylistMusic, + mdiPodcast, + mdiTelevisionClassic, + mdiVideo, + mdiWeb, +} from "@mdi/js"; import type { HassEntity } from "home-assistant-js-websocket"; import type { HomeAssistant } from "../types"; -import { - mdiFolder, - mdiPlaylistMusic, - mdiFileMusic, - mdiAlbum, - mdiMusic, - mdiTelevisionClassic, - mdiMovie, - mdiVideo, - mdiImage, - mdiWeb, - mdiGamepadVariant, - mdiAccountMusic, - mdiPodcast, - mdiApplication, - mdiAccountMusicOutline, - mdiDramaMasks, -} from "@mdi/js"; export const SUPPORT_PAUSE = 1; export const SUPPORT_SEEK = 2; @@ -38,7 +38,7 @@ export const CONTRAST_RATIO = 4.5; export type MediaPlayerBrowseAction = "pick" | "play"; -export const BROWSER_SOURCE = "browser"; +export const BROWSER_PLAYER = "browser"; export type MediaClassBrowserSetting = { icon: string; diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index 8fa84cba67..32e8661d52 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -17,7 +17,7 @@ import { supportsFeature } from "../../common/entity/supports-feature"; import "../../components/ha-menu-button"; import "../../components/media-player/ha-media-player-browse"; import { - BROWSER_SOURCE, + BROWSER_PLAYER, MediaPickedEvent, SUPPORT_BROWSE_MEDIA, } from "../../data/media-player"; @@ -36,7 +36,7 @@ class PanelMediaBrowser extends LitElement { // @ts-ignore @LocalStorage("mediaBrowseEntityId", true) - private _entityId = BROWSER_SOURCE; + private _entityId = BROWSER_PLAYER; protected render(): TemplateResult { const stateObj = this._entityId @@ -44,7 +44,7 @@ class PanelMediaBrowser extends LitElement { : undefined; const title = - this._entityId === BROWSER_SOURCE + this._entityId === BROWSER_PLAYER ? `${this.hass.localize("ui.components.media-browser.web-browser")}` : stateObj?.attributes.friendly_name ? `${stateObj?.attributes.friendly_name}` @@ -95,7 +95,7 @@ class PanelMediaBrowser extends LitElement { ev: HASSDomEvent ): Promise { const item = ev.detail.item; - if (this._entityId === BROWSER_SOURCE) { + if (this._entityId === BROWSER_PLAYER) { const resolvedUrl: any = await this.hass.callWS({ type: "media_source/resolve_media", media_content_id: item.media_content_id, diff --git a/src/panels/media-browser/hui-dialog-select-media-player.ts b/src/panels/media-browser/hui-dialog-select-media-player.ts index 03b53b6a2d..46865415da 100644 --- a/src/panels/media-browser/hui-dialog-select-media-player.ts +++ b/src/panels/media-browser/hui-dialog-select-media-player.ts @@ -1,3 +1,5 @@ +import "@material/mwc-list/mwc-list"; +import "@material/mwc-list/mwc-list-item"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import { @@ -13,7 +15,8 @@ import { fireEvent } from "../../common/dom/fire_event"; import { computeStateName } from "../../common/entity/compute_state_name"; import { compare } from "../../common/string/compare"; import { createCloseHeading } from "../../components/ha-dialog"; -import { BROWSER_SOURCE } from "../../data/media-player"; +import { UNAVAILABLE_STATES } from "../../data/entity"; +import { BROWSER_PLAYER } from "../../data/media-player"; import { haStyleDialog } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; import type { SelectMediaPlayerDialogParams } from "./show-select-media-source-dialog"; @@ -49,30 +52,31 @@ export class HuiDialogSelectMediaPlayer extends LitElement { )} @closed=${this.closeDialog} > - + ${this.hass.localize( "ui.components.media-browser.web-browser" - )} ${this._params.mediaSources .sort((a, b) => compare(computeStateName(a), computeStateName(b))) .map( (source) => html` - ${computeStateName(source)}${computeStateName(source)} ` )} - + `; } - private _selectSource(ev: CustomEvent): void { - const entityId = ev.detail.item.itemName; + private _selectPlayer(ev: CustomEvent): void { + const entityId = (ev.currentTarget as any).player; this._params!.sourceSelectedCallback(entityId); this.closeDialog(); } @@ -84,8 +88,8 @@ export class HuiDialogSelectMediaPlayer extends LitElement { ha-dialog { --dialog-content-padding: 0 24px 20px; } - paper-item { - cursor: pointer; + mwc-list-item[disabled] { + --mdc-theme-text-primary-on-background: var(--disabled-text-color); } `, ]; From c917b67cbdb4955f06fd8a14dc0aff4c063d3218 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 14:43:13 +0200 Subject: [PATCH 144/146] Adjust local media error messages (#7017) --- .../media-player/ha-media-player-browse.ts | 73 +++++++++++++------ src/translations/en.json | 6 ++ src/util/documentation-url.ts | 6 +- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 62fe5d1236..1f6af63c66 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -22,7 +22,6 @@ import { styleMap } from "lit-html/directives/style-map"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; -import type { MediaPlayerItem } from "../../data/media-player"; import { browseLocalMediaPlayer, browseMediaPlayer, @@ -31,6 +30,7 @@ import { MediaPickedEvent, MediaPlayerBrowseAction, } from "../../data/media-player"; +import type { MediaPlayerItem } from "../../data/media-player"; import { showAlertDialog } from "../../dialogs/generic/show-dialog-box"; import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer"; import { haStyle } from "../../resources/styles"; @@ -357,7 +357,31 @@ export class HaMediaPlayerBrowse extends LitElement { ` : html`
    - ${this.hass.localize("ui.components.media-browser.no_items")} + ${this.hass.localize("ui.components.media-browser.no_items")}
    + + ${currentItem.media_content_id.startsWith( + "media-source://media_source/local_source" + ) + ? html`${this.hass.localize( + "ui.components.media-browser.learn_adding_local_media", + "documentation", + html`${this.hass.localize( + "ui.components.media-browser.documentation" + )}` + )} +
    + ${this.hass.localize( + "ui.components.media-browser.local_media_files" + )}.` + : ""}
    `} `; @@ -501,28 +525,31 @@ export class HaMediaPlayerBrowse extends LitElement { private _renderError(err: { message: string; code: string }) { if (err.message === "Media directory does not exist.") { return html` -

    No local media found.

    +

    + ${this.hass.localize( + "ui.components.media-browser.no_local_media_found" + )} +

    - It looks like you have not yet created a media directory. -
    Create a directory with the name "media" in the - configuration directory of Home Assistant - (${this.hass.config.config_dir}).
    Place your video, audio and - image files in this directory to be able to browse and play them in - the browser or on supported media players. -

    - -

    - Check the - documentation - for more info + ${this.hass.localize("ui.components.media-browser.no_media_folder")} +
    + ${this.hass.localize( + "ui.components.media-browser.setup_local_help", + "documentation", + html`${this.hass.localize( + "ui.components.media-browser.documentation" + )}` + )} +
    + ${this.hass.localize("ui.components.media-browser.local_media_files")}

    `; } diff --git a/src/translations/en.json b/src/translations/en.json index fff67d34fb..5504fd17df 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -374,6 +374,12 @@ "video_not_supported": "Your browser does not support the video element.", "media_not_supported": "The Browser Media Player does not support this type of media", "media_browsing_error": "Media Browsing Error", + "learn_adding_local_media": "Learn more about adding media in the {documentation}.", + "local_media_files": "Place your video, audio and image files in the media directory to be able to browse and play them in the browser or on supported media players.", + "documentation": "documentation", + "no_local_media_found": "No local media found", + "no_media_folder": "It looks like you have not yet created a media directory.", + "setup_local_help": "Check the {documentation} on how to setup local media.", "class": { "album": "Album", "app": "App", diff --git a/src/util/documentation-url.ts b/src/util/documentation-url.ts index 693e9437d8..3ad016be72 100644 --- a/src/util/documentation-url.ts +++ b/src/util/documentation-url.ts @@ -2,6 +2,10 @@ import { HomeAssistant } from "../types"; export const documentationUrl = (hass: HomeAssistant, path: string) => { return `https://${ - hass.config.version.includes("b") ? "rc" : "www" + hass.config.version.includes("b") + ? "rc" + : hass.config.version.includes("dev") + ? "next" + : "www" }.home-assistant.io${path}`; }; From ca4757db5b6b356a49e3ce46f00470c4cfe07465 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 14:44:55 +0200 Subject: [PATCH 145/146] Bumped version to 20200915.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7ee304d284..b7b90326ef 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200912.0", + version="20200915.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From d6aba040dde366249ba10c5149a5fbc68a1d98bc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 15 Sep 2020 14:45:38 +0200 Subject: [PATCH 146/146] revert remove tap action from button stub (#7018) --- src/panels/lovelace/cards/hui-button-card.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 65c045f8ab..2d75c7e33b 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -63,6 +63,9 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { return { type: "button", + tap_action: { + action: "toggle", + }, entity: foundEntities[0] || "", }; }