diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index 5cbb12bfea..08d419c446 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -2,6 +2,7 @@ const webpack = require("webpack"); const path = require("path"); const TerserPlugin = require("terser-webpack-plugin"); const ManifestPlugin = require("webpack-manifest-plugin"); +const WorkerPlugin = require("worker-plugin"); const paths = require("./paths.js"); const env = require("./env.js"); const { babelLoaderConfig } = require("./babel.js"); @@ -51,6 +52,7 @@ const createWebpackConfig = ({ ], }, plugins: [ + new WorkerPlugin(), new ManifestPlugin(), new webpack.DefinePlugin({ __DEV__: !isProdBuild, @@ -105,7 +107,7 @@ const createWebpackConfig = ({ latestBuild ? "frontend_latest" : "frontend_es5" ), publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/", - // For workerize loader + // To silence warning in worker plugin globalObject: "self", }, }; diff --git a/demo/src/configs/teachingbirds/lovelace.ts b/demo/src/configs/teachingbirds/lovelace.ts index 76fb7f959a..f7684fde6d 100644 --- a/demo/src/configs/teachingbirds/lovelace.ts +++ b/demo/src/configs/teachingbirds/lovelace.ts @@ -63,7 +63,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ elements: [ { style: { - "--mdc-icon-size": "100px", + "--mdc-icon-size": "100%", top: "50%", left: "50%", }, diff --git a/package.json b/package.json index de6b2e6d4d..193aefcd0f 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "chart.js": "~2.8.0", "chartjs-chart-timeline": "^0.3.0", "codemirror": "^5.49.0", + "comlink": "^4.3.0", "cpx": "^1.5.0", "deep-clone-simple": "^1.1.1", "deep-freeze": "^0.0.1", @@ -108,6 +109,7 @@ "memoize-one": "^5.0.2", "moment": "^2.24.0", "node-vibrant": "^3.1.5", + "proxy-polyfill": "^0.3.1", "regenerator-runtime": "^0.13.2", "resize-observer": "^1.0.0", "roboto-fontface": "^0.10.0", @@ -195,7 +197,7 @@ "webpack-dev-server": "^3.10.3", "webpack-manifest-plugin": "^2.0.4", "workbox-build": "^5.1.3", - "workerize-loader": "^1.1.0" + "worker-plugin": "^4.0.3" }, "_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page", "_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569", diff --git a/setup.py b/setup.py index d4289a61a5..2b0dd2baf3 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200515.0", + version="20200518.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index be9b5c246c..cf8407ed66 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -14,9 +14,6 @@ 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 { scroll } from "lit-virtualizer"; -// @ts-ignore -// eslint-disable-next-line import/no-webpack-loader-syntax -import sortFilterWorker from "workerize-loader!./sort_filter_worker"; import { fireEvent } from "../../common/dom/fire_event"; import "../../common/search/search-input"; import { debounce } from "../../common/util/debounce"; @@ -24,6 +21,7 @@ import { nextRender } from "../../common/util/render-status"; import "../ha-checkbox"; import type { HaCheckbox } from "../ha-checkbox"; import "../ha-icon"; +import { filterSortData } from "./sort-filter"; declare global { // for fire event @@ -117,8 +115,6 @@ export class HaDataTable extends LitElement { private curRequest = 0; - private _worker: any | undefined; - private _debounceSearch = debounce( (value: string) => { this._filter = value; @@ -140,11 +136,6 @@ export class HaDataTable extends LitElement { } } - protected firstUpdated(properties: PropertyValues) { - super.firstUpdated(properties); - this._worker = sortFilterWorker(); - } - protected updated(properties: PropertyValues) { super.updated(properties); @@ -383,7 +374,7 @@ export class HaDataTable extends LitElement { this.curRequest++; const curRequest = this.curRequest; - const filterProm = this._worker.filterSortData( + const filterProm = filterSortData( this.data, this._sortColumns, this._filter, diff --git a/src/components/data-table/sort-filter.ts b/src/components/data-table/sort-filter.ts new file mode 100644 index 0000000000..766a167fba --- /dev/null +++ b/src/components/data-table/sort-filter.ts @@ -0,0 +1,26 @@ +import { wrap } from "comlink"; + +type FilterSortDataType = typeof import("./sort_filter_worker").api["filterSortData"]; +type filterSortDataParamTypes = Parameters; + +let worker: any | undefined; + +export const filterSortData = async ( + data: filterSortDataParamTypes[0], + columns: filterSortDataParamTypes[1], + filter: filterSortDataParamTypes[2], + direction: filterSortDataParamTypes[3], + sortColumn: filterSortDataParamTypes[4] +): Promise> => { + if (!worker) { + worker = wrap(new Worker("./sort_filter_worker", { type: "module" })); + } + + return await worker.filterSortData( + data, + columns, + filter, + direction, + sortColumn + ); +}; diff --git a/src/components/data-table/sort_filter_worker.ts b/src/components/data-table/sort_filter_worker.ts index 1bae239169..f7b71bdd2c 100644 --- a/src/components/data-table/sort_filter_worker.ts +++ b/src/components/data-table/sort_filter_worker.ts @@ -1,57 +1,34 @@ -import memoizeOne from "memoize-one"; -// eslint-disable-next-line import/no-cycle -import { - DataTableColumnContainer, - DataTableColumnData, +// To use comlink under ES5 +import "proxy-polyfill"; +import { expose } from "comlink"; +import type { + DataTableSortColumnData, DataTableRowData, SortingDirection, + HaDataTable, } from "./ha-data-table"; -export const filterSortData = memoizeOne( - async ( - data: DataTableRowData[], - columns: DataTableColumnContainer, - filter: string, - direction: SortingDirection, - sortColumn?: string - ) => - sortColumn - ? _memSortData( - await _memFilterData(data, columns, filter), - columns, - direction, - sortColumn - ) - : _memFilterData(data, columns, filter) -); +type SortableColumnContainer = HaDataTable["_sortColumns"]; -const _memFilterData = memoizeOne( - async ( - data: DataTableRowData[], - columns: DataTableColumnContainer, - filter: string - ) => { - if (!filter) { - return data; - } - return filterData(data, columns, filter.toUpperCase()); - } -); - -const _memSortData = memoizeOne( - ( - data: DataTableRowData[], - columns: DataTableColumnContainer, - direction: SortingDirection, - sortColumn: string - ) => { - return sortData(data, columns[sortColumn], direction, sortColumn); - } -); - -export const filterData = ( +const filterSortData = ( data: DataTableRowData[], - columns: DataTableColumnContainer, + columns: SortableColumnContainer, + filter: string, + direction: SortingDirection, + sortColumn?: string +) => { + const filteredData = filter ? filterData(data, columns, filter) : data; + + if (!sortColumn) { + return filteredData; + } + + return sortData(filteredData, columns, direction, sortColumn); +}; + +const filterData = ( + data: DataTableRowData[], + columns: SortableColumnContainer, filter: string ) => data.filter((row) => { @@ -70,9 +47,9 @@ export const filterData = ( }); }); -export const sortData = ( +const sortData = ( data: DataTableRowData[], - column: DataTableColumnData, + column: DataTableSortColumnData, direction: SortingDirection, sortColumn: string ) => @@ -105,3 +82,10 @@ export const sortData = ( } return 0; }); + +// Export for types +export const api = { + filterSortData, +}; + +expose(api); diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 0cafed4191..231914cecf 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -38,15 +38,14 @@ const rowRenderer = ( } - + -
[[_computeStateName(item)]]
-
[[item.entity_id]]
+
+
`; } - root.querySelector("state-badge")!.stateObj = model.item; root.querySelector(".name")!.textContent = computeStateName(model.item); root.querySelector("[secondary]")!.textContent = model.item.entity_id; @@ -148,6 +147,10 @@ class HaEntityPicker extends LitElement { } ); + protected shouldUpdate(changedProps: PropertyValues) { + return !(!changedProps.has("_opened") && this._opened); + } + protected updated(changedProps: PropertyValues) { if (changedProps.has("_opened") && this._opened) { const states = this._getStates( diff --git a/src/components/ha-markdown.ts b/src/components/ha-markdown.ts index ee96a2b219..b5ba2e6697 100644 --- a/src/components/ha-markdown.ts +++ b/src/components/ha-markdown.ts @@ -1,10 +1,6 @@ import { customElement, property, UpdatingElement } from "lit-element"; -// @ts-ignore -// eslint-disable-next-line import/no-webpack-loader-syntax -import markdownWorker from "workerize-loader!../resources/markdown_worker"; import { fireEvent } from "../common/dom/fire_event"; - -let worker: any | undefined; +import { renderMarkdown } from "../resources/render-markdown"; @customElement("ha-markdown") class HaMarkdown extends UpdatingElement { @@ -16,16 +12,11 @@ class HaMarkdown extends UpdatingElement { protected update(changedProps) { super.update(changedProps); - - if (!worker) { - worker = markdownWorker(); - } - this._render(); } private async _render() { - this.innerHTML = await worker.renderMarkdown( + this.innerHTML = await renderMarkdown( this.content, { breaks: this.breaks, diff --git a/src/data/lovelace_custom_cards.ts b/src/data/lovelace_custom_cards.ts index 2e021e7a6f..d669b50880 100644 --- a/src/data/lovelace_custom_cards.ts +++ b/src/data/lovelace_custom_cards.ts @@ -3,6 +3,7 @@ export interface CustomCardEntry { name?: string; description?: string; preview?: boolean; + documentationURL?: string; } export interface CustomCardsWindow { diff --git a/src/entrypoints/compatibility.ts b/src/entrypoints/compatibility.ts index e97a4ebc4a..9b54cfeef2 100644 --- a/src/entrypoints/compatibility.ts +++ b/src/entrypoints/compatibility.ts @@ -2,6 +2,8 @@ import objAssign from "es6-object-assign"; import "mdn-polyfills/Array.prototype.includes"; import "regenerator-runtime/runtime"; import "unfetch/polyfill"; +// To use comlink under ES5 +import "proxy-polyfill"; objAssign.polyfill(); diff --git a/src/panels/config/automation/ha-config-automation.ts b/src/panels/config/automation/ha-config-automation.ts index c5ff6861f5..8ee8267f0b 100644 --- a/src/panels/config/automation/ha-config-automation.ts +++ b/src/panels/config/automation/ha-config-automation.ts @@ -76,7 +76,7 @@ class HaConfigAutomation extends HassRouterPage { if (this.hass) { if (!pageEl.automations || !changedProps) { pageEl.automations = this._getAutomations(this.hass.states); - } else if (changedProps && changedProps.has("hass")) { + } else if (changedProps.has("hass")) { this._debouncedUpdateAutomations(pageEl); } } diff --git a/src/panels/config/integrations/ha-config-integrations.ts b/src/panels/config/integrations/ha-config-integrations.ts index 2b6268ca59..9fb28efcb8 100644 --- a/src/panels/config/integrations/ha-config-integrations.ts +++ b/src/panels/config/integrations/ha-config-integrations.ts @@ -147,7 +147,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { filter?: string ): ConfigEntryExtended[] => { if (!filter) { - return configEntries; + return [...configEntries]; } const options: Fuse.FuseOptions = { keys: ["domain", "localized_domain_name", "title"], @@ -170,11 +170,11 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) { filter ); const ignored: ConfigEntryExtended[] = []; - filteredConfigEnties.forEach((item, index) => { - if (item.source === "ignore") { - ignored.push(filteredConfigEnties.splice(index, 1)[0]); + for (let i = filteredConfigEnties.length - 1; i >= 0; i--) { + if (filteredConfigEnties[i].source === "ignore") { + ignored.push(filteredConfigEnties.splice(i, 1)[0]); } - }); + } return [groupByIntegration(filteredConfigEnties), ignored]; } ); diff --git a/src/panels/config/scene/ha-config-scene.ts b/src/panels/config/scene/ha-config-scene.ts index 1849969716..1ac455b676 100644 --- a/src/panels/config/scene/ha-config-scene.ts +++ b/src/panels/config/scene/ha-config-scene.ts @@ -68,7 +68,7 @@ class HaConfigScene extends HassRouterPage { if (this.hass) { if (!pageEl.scenes || !changedProps) { pageEl.scenes = this._getScenes(this.hass.states); - } else if (changedProps && changedProps.has("hass")) { + } else if (changedProps.has("hass")) { this._debouncedUpdateScenes(pageEl); } } diff --git a/src/panels/config/script/ha-config-script.ts b/src/panels/config/script/ha-config-script.ts index 417add088a..0f3ba479bf 100644 --- a/src/panels/config/script/ha-config-script.ts +++ b/src/panels/config/script/ha-config-script.ts @@ -73,7 +73,7 @@ class HaConfigScript extends HassRouterPage { if (this.hass) { if (!pageEl.scripts || !changedProps) { pageEl.scripts = this._getScripts(this.hass.states); - } else if (changedProps && changedProps.has("hass")) { + } else if (changedProps.has("hass")) { this._debouncedUpdateScripts(pageEl); } } diff --git a/src/panels/developer-tools/event/developer-tools-event.js b/src/panels/developer-tools/event/developer-tools-event.js index b861cb77e9..2ccf0a9a8f 100644 --- a/src/panels/developer-tools/event/developer-tools-event.js +++ b/src/panels/developer-tools/event/developer-tools-event.js @@ -51,6 +51,10 @@ class HaPanelDevEvent extends EventsMixin(LocalizeMixin(PolymerElement)) { max-width: 800px; margin: 16px auto; } + + a { + color: var(--primary-color); + }
diff --git a/src/panels/developer-tools/event/events-list.js b/src/panels/developer-tools/event/events-list.js index 86cb30bee4..b5cc807842 100644 --- a/src/panels/developer-tools/event/events-list.js +++ b/src/panels/developer-tools/event/events-list.js @@ -24,7 +24,7 @@ class EventsList extends EventsMixin(LocalizeMixin(PolymerElement)) { } a { - color: var(--dark-primary-color); + color: var(--primary-color); } diff --git a/src/panels/developer-tools/info/developer-tools-info.ts b/src/panels/developer-tools/info/developer-tools-info.ts index ba5fc85dd3..74649f2f12 100644 --- a/src/panels/developer-tools/info/developer-tools-info.ts +++ b/src/panels/developer-tools/info/developer-tools-info.ts @@ -84,7 +84,7 @@ class HaPanelDevInfo extends LitElement { Google - and + ${this.hass.localize("ui.common.and")} MaterialDesignIcons.com. @@ -165,7 +165,7 @@ class HaPanelDevInfo extends LitElement { } .about a { - color: var(--dark-primary-color); + color: var(--primary-color); } system-health-card, diff --git a/src/panels/developer-tools/template/developer-tools-template.js b/src/panels/developer-tools/template/developer-tools-template.js index 2475a7b256..5f7c0ae198 100644 --- a/src/panels/developer-tools/template/developer-tools-template.js +++ b/src/panels/developer-tools/template/developer-tools-template.js @@ -29,7 +29,7 @@ class HaPanelDevTemplate extends LocalizeMixin(PolymerElement) { } .edit-pane a { - color: var(--dark-primary-color); + color: var(--primary-color); } .horizontal .edit-pane { diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.ts b/src/panels/lovelace/cards/hui-entity-filter-card.ts index 16e141e058..cc6dcc82f7 100644 --- a/src/panels/lovelace/cards/hui-entity-filter-card.ts +++ b/src/panels/lovelace/cards/hui-entity-filter-card.ts @@ -6,26 +6,28 @@ import { createCardElement } from "../create-element/create-card-element"; import { EntityFilterEntityConfig } from "../entity-rows/types"; import { LovelaceCard } from "../types"; import { EntityFilterCardConfig } from "./types"; +import { property, PropertyValues, UpdatingElement } from "lit-element"; +import { computeCardSize } from "../common/compute-card-size"; -class EntityFilterCard extends HTMLElement implements LovelaceCard { - public isPanel?: boolean; +class EntityFilterCard extends UpdatingElement implements LovelaceCard { + @property() public hass?: HomeAssistant; - private _editMode = false; + @property() public isPanel = false; + + @property() public editMode = false; + + @property() private _config?: EntityFilterCardConfig; private _element?: LovelaceCard; - private _config?: EntityFilterCardConfig; - private _configEntities?: EntityFilterEntityConfig[]; private _baseCardConfig?: LovelaceCardConfig; - private _hass?: HomeAssistant; - private _oldEntities?: EntityFilterEntityConfig[]; public getCardSize(): number { - return this._element ? this._element.getCardSize() : 1; + return this._element ? computeCardSize(this._element) : 1; } public setConfig(config: EntityFilterCardConfig): void { @@ -45,8 +47,8 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard { throw new Error("Incorrect filter config."); } + this._configEntities = processConfigEntities(config.entities); this._config = config; - this._configEntities = undefined; this._baseCardConfig = { type: "entities", entities: [], @@ -59,32 +61,32 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard { } } - set editMode(editMode: boolean) { - this._editMode = editMode; - if (!this._element) { - return; + protected shouldUpdate(changedProps: PropertyValues): boolean { + if (this._element) { + this._element.hass = this.hass; + this._element.editMode = this.editMode; + this._element.isPanel = this.isPanel; } - this._element.editMode = editMode; + + if (changedProps.has("_config")) { + return true; + } + if (changedProps.has("hass")) { + return this._haveEntitiesChanged( + changedProps.get("hass") as HomeAssistant | null + ); + } + return false; } - set hass(hass: HomeAssistant) { - if (!hass || !this._config) { + protected update(changedProps: PropertyValues) { + super.update(changedProps); + if (!this.hass || !this._config || !this._configEntities) { return; } - if (!this.haveEntitiesChanged(hass)) { - this._hass = hass; - return; - } - - this._hass = hass; - - if (!this._configEntities) { - this._configEntities = processConfigEntities(this._config.entities); - } - const entitiesList = this._configEntities.filter((entityConf) => { - const stateObj = hass.states[entityConf.entity]; + const stateObj = this.hass!.states[entityConf.entity]; if (!stateObj) { return false; @@ -112,13 +114,13 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard { return; } - const element = this._cardElement(); - - if (!element) { - return; - } - - if (element.tagName !== "HUI-ERROR-CARD") { + if (!this._element) { + this._element = this._createCardElement({ + ...this._baseCardConfig!, + entities: entitiesList, + }); + this._oldEntities = entitiesList; + } else if (this._element.tagName !== "HUI-ERROR-CARD") { const isSame = this._oldEntities && entitiesList.length === this._oldEntities.length && @@ -126,24 +128,23 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard { if (!isSame) { this._oldEntities = entitiesList; - element.setConfig({ ...this._baseCardConfig!, entities: entitiesList }); + this._element.setConfig({ + ...this._baseCardConfig!, + entities: entitiesList, + }); } - - element.isPanel = this.isPanel; - element.editMode = this._editMode; - element.hass = hass; } // Attach element if it has never been attached. if (!this.lastChild) { - this.appendChild(element); + this.appendChild(this._element); } this.style.display = "block"; } - private haveEntitiesChanged(hass: HomeAssistant): boolean { - if (!this._hass) { + private _haveEntitiesChanged(oldHass: HomeAssistant | null): boolean { + if (!this.hass || !oldHass) { return true; } @@ -151,11 +152,12 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard { return true; } + if (this.hass.localize !== oldHass.localize) { + return true; + } + for (const config of this._configEntities) { - if ( - this._hass.states[config.entity] !== hass.states[config.entity] || - this._hass.localize !== hass.localize - ) { + if (this.hass.states[config.entity] !== oldHass.states[config.entity]) { return true; } } @@ -163,13 +165,33 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard { return false; } - private _cardElement(): LovelaceCard | undefined { - if (!this._element && this._config) { - const element = createCardElement(this._baseCardConfig!); - this._element = element; + private _createCardElement(cardConfig: LovelaceCardConfig) { + const element = createCardElement(cardConfig) as LovelaceCard; + if (this.hass) { + element.hass = this.hass; } + element.isPanel = this.isPanel; + element.editMode = this.editMode; + element.addEventListener( + "ll-rebuild", + (ev) => { + ev.stopPropagation(); + this._rebuildCard(element, cardConfig); + }, + { once: true } + ); + return element; + } - return this._element; + private _rebuildCard( + cardElToReplace: LovelaceCard, + config: LovelaceCardConfig + ): void { + const newCardEl = this._createCardElement(config); + if (cardElToReplace.parentElement) { + cardElToReplace.parentElement!.replaceChild(newCardEl, cardElToReplace); + } + this._element = newCardEl; } } customElements.define("hui-entity-filter-card", EntityFilterCard); diff --git a/src/panels/lovelace/cards/hui-picture-card.ts b/src/panels/lovelace/cards/hui-picture-card.ts index f07f18fa5b..bda16ed813 100644 --- a/src/panels/lovelace/cards/hui-picture-card.ts +++ b/src/panels/lovelace/cards/hui-picture-card.ts @@ -38,7 +38,7 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { }; } - public hass?: HomeAssistant; + @property() public hass?: HomeAssistant; @property() protected _config?: PictureCardConfig; @@ -54,6 +54,13 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { this._config = config; } + protected shouldUpdate(changedProps: PropertyValues): boolean { + if (changedProps.size === 1 && changedProps.has("hass")) { + return !changedProps.get("hass"); + } + return true; + } + protected updated(changedProps: PropertyValues): void { super.updated(changedProps); if (!this._config || !this.hass) { diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts index 5f2eac9b6f..a1fb3bec86 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.ts +++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts @@ -250,17 +250,17 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { hasAction(entityConf.tap_action) ? "0" : undefined )} .config=${entityConf} - class="${classMap({ + class=${classMap({ "state-on": !STATES_OFF.has(stateObj.state), - })}" - .icon="${entityConf.icon || stateIcon(stateObj)}" - title="${` + })} + .icon=${entityConf.icon || stateIcon(stateObj)} + title=${` ${computeStateName(stateObj)} : ${computeStateDisplay( this.hass!.localize, stateObj, this.hass!.language )} - `}" + `} > ${this._config!.show_state !== true && entityConf.show_state !== true ? html`
` @@ -328,19 +328,12 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { ha-icon { cursor: pointer; - padding: 8px; color: #a9a9a9; } ha-icon.state-on { color: white; } - ha-icon.show-state { - width: 20px; - height: 20px; - padding-bottom: 4px; - padding-top: 4px; - } ha-icon:focus { outline: none; background: var(--divider-color); diff --git a/src/panels/lovelace/cards/hui-stack-card.ts b/src/panels/lovelace/cards/hui-stack-card.ts index 85f155c26f..547aad6357 100644 --- a/src/panels/lovelace/cards/hui-stack-card.ts +++ b/src/panels/lovelace/cards/hui-stack-card.ts @@ -111,7 +111,9 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard { config: LovelaceCardConfig ): void { const newCardEl = this._createCardElement(config); - cardElToReplace.parentElement!.replaceChild(newCardEl, cardElToReplace); + if (cardElToReplace.parentElement) { + cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace); + } this._cards = this._cards!.map((curCardEl) => curCardEl === cardElToReplace ? newCardEl : curCardEl ); 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 3d4be3394e..cf2870801e 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -30,52 +30,14 @@ import { import { createCardElement } from "../../create-element/create-card-element"; import { LovelaceCard } from "../../types"; import { getCardStubConfig } from "../get-card-stub-config"; -import { CardPickTarget } from "../types"; - -interface Card { - type: string; - name?: string; - description?: string; - noElement?: boolean; - isCustom?: boolean; -} +import { CardPickTarget, Card } from "../types"; +import { coreCards } from "../lovelace-cards"; interface CardElement { card: Card; element: TemplateResult; } -const previewCards: string[] = [ - "alarm-panel", - "button", - "entities", - "entity", - "gauge", - "glance", - "history-graph", - "light", - "map", - "markdown", - "media-control", - "picture", - "picture-elements", - "picture-entity", - "picture-glance", - "plant-status", - "sensor", - "thermostat", - "weather-forecast", -]; - -const nonPreviewCards: string[] = [ - "conditional", - "entity-filter", - "horizontal-stack", - "iframe", - "vertical-stack", - "shopping-list", -]; - @customElement("hui-card-picker") export class HuiCardPicker extends LitElement { @property() public hass?: HomeAssistant; @@ -137,9 +99,9 @@ export class HuiCardPicker extends LitElement {
${this.hass!.localize( @@ -192,33 +154,22 @@ export class HuiCardPicker extends LitElement { } private _loadCards() { - let cards: Card[] = previewCards - .map((type: string) => ({ - type, - name: this.hass!.localize(`ui.panel.lovelace.editor.card.${type}.name`), - description: this.hass!.localize( - `ui.panel.lovelace.editor.card.${type}.description` - ), - })) - .concat( - nonPreviewCards.map((type: string) => ({ - type, - name: this.hass!.localize( - `ui.panel.lovelace.editor.card.${type}.name` - ), - description: this.hass!.localize( - `ui.panel.lovelace.editor.card.${type}.description` - ), - noElement: true, - })) - ); + let cards: Card[] = coreCards.map((card: Card) => ({ + name: this.hass!.localize( + `ui.panel.lovelace.editor.card.${card.type}.name` + ), + description: this.hass!.localize( + `ui.panel.lovelace.editor.card.${card.type}.description` + ), + ...card, + })); if (customCards.length > 0) { cards = cards.concat( customCards.map((ccard: CustomCardEntry) => ({ type: ccard.type, name: ccard.name, description: ccard.description, - noElement: true, + showElement: ccard.preview, isCustom: true, })) ); @@ -311,6 +262,10 @@ export class HuiCardPicker extends LitElement { height: 100%; z-index: 1; } + + .manual { + max-width: none; + } `, ]; } @@ -341,7 +296,7 @@ export class HuiCardPicker extends LitElement { private async _renderCardElement(card: Card): Promise { let { type } = card; - const { noElement, isCustom, name, description } = card; + const { showElement, isCustom, name, description } = card; const customCard = isCustom ? getCustomCardEntry(type) : undefined; if (isCustom) { type = `${CUSTOM_TYPE_PREFIX}${type}`; @@ -358,7 +313,7 @@ export class HuiCardPicker extends LitElement { this._usedEntities! ); - if (!noElement || customCard?.preview) { + if (showElement) { element = this._createCardElement(cardConfig); } } 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 1a7434bcd3..0c832dc73d 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 @@ -1,3 +1,4 @@ +import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; import deepFreeze from "deep-freeze"; import { css, @@ -8,6 +9,7 @@ import { property, query, TemplateResult, + PropertyValues, } from "lit-element"; import type { HASSDomEvent } from "../../../../common/dom/fire_event"; import "../../../../components/dialog/ha-paper-dialog"; @@ -25,7 +27,7 @@ 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 "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; +import { getCardDocumentationURL } from "../get-card-documentation-url"; declare global { // for fire event @@ -58,6 +60,8 @@ export class HuiDialogEditCard extends LitElement { @property() private _GUImode = true; + @property() private _documentationURL?: string; + public async showDialog(params: EditCardDialogParams): Promise { this._params = params; this._GUImode = true; @@ -71,6 +75,22 @@ export class HuiDialogEditCard extends LitElement { } } + protected updated(changedProps: PropertyValues): void { + if ( + !this._cardConfig || + this._documentationURL !== undefined || + !changedProps.has("_cardConfig") + ) { + return; + } + + const oldConfig = changedProps.get("_cardConfig") as LovelaceCardConfig; + + if (oldConfig?.type !== this._cardConfig!.type) { + this._documentationURL = getCardDocumentationURL(this._cardConfig!.type); + } + } + protected render(): TemplateResult { if (!this._params) { return html``; @@ -97,9 +117,26 @@ export class HuiDialogEditCard extends LitElement { return html` -

- ${heading} -

+
+

+ ${heading} +

+ ${this._documentationURL !== undefined + ? html` + + + + ` + : ""} +
${this._cardConfig === undefined ? html` @@ -275,6 +312,15 @@ export class HuiDialogEditCard extends LitElement { .gui-mode-button { margin-right: auto; } + .header { + display: flex; + align-items: center; + justify-content: space-between; + } + .help-icon { + text-decoration: none; + color: inherit; + } `, ]; } @@ -318,6 +364,7 @@ export class HuiDialogEditCard extends LitElement { this._params = undefined; this._cardConfig = undefined; this._error = undefined; + this._documentationURL = undefined; } private get _canSave(): boolean { diff --git a/src/panels/lovelace/editor/get-card-documentation-url.ts b/src/panels/lovelace/editor/get-card-documentation-url.ts new file mode 100644 index 0000000000..4d1e989a1d --- /dev/null +++ b/src/panels/lovelace/editor/get-card-documentation-url.ts @@ -0,0 +1,14 @@ +import { + getCustomCardEntry, + CUSTOM_TYPE_PREFIX, +} from "../../../data/lovelace_custom_cards"; + +const coreDocumentationURLBase = "https://www.home-assistant.io/lovelace/"; + +export const getCardDocumentationURL = (type: string): string | undefined => { + if (type.startsWith(CUSTOM_TYPE_PREFIX)) { + return getCustomCardEntry(type)?.documentationURL; + } + + return `${coreDocumentationURLBase}${type}`; +}; diff --git a/src/panels/lovelace/editor/lovelace-cards.ts b/src/panels/lovelace/editor/lovelace-cards.ts new file mode 100644 index 0000000000..210f3f834d --- /dev/null +++ b/src/panels/lovelace/editor/lovelace-cards.ts @@ -0,0 +1,98 @@ +import { Card } from "./types"; + +export const coreCards: Card[] = [ + { + type: "alarm-panel", + showElement: true, + }, + { + type: "button", + showElement: true, + }, + { + type: "entities", + showElement: true, + }, + { + type: "entity", + showElement: true, + }, + { + type: "gauge", + showElement: true, + }, + { + type: "glance", + showElement: true, + }, + { + type: "history-graph", + showElement: true, + }, + { + type: "light", + showElement: true, + }, + { + type: "map", + showElement: true, + }, + { + type: "markdown", + showElement: true, + }, + { + type: "media-control", + showElement: true, + }, + { + type: "picture", + showElement: true, + }, + { + type: "picture-elements", + showElement: true, + }, + { + type: "picture-entity", + showElement: true, + }, + { + type: "picture-glance", + showElement: true, + }, + { + type: "plant-status", + showElement: true, + }, + { + type: "sensor", + showElement: true, + }, + { + type: "thermostat", + showElement: true, + }, + { + type: "weather-forecast", + showElement: true, + }, + { + type: "conditional", + }, + { + type: "entity-filter", + }, + { + type: "horizontal-stack", + }, + { + type: "iframe", + }, + { + type: "vertical-stack", + }, + { + type: "shopping-list", + }, +]; diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts index b9a79c53f1..31a3186d96 100644 --- a/src/panels/lovelace/editor/types.ts +++ b/src/panels/lovelace/editor/types.ts @@ -54,6 +54,14 @@ export interface EditorTarget extends EventTarget { config: ActionConfig; } +export interface Card { + type: string; + name?: string; + description?: string; + showElement?: boolean; + isCustom?: boolean; +} + export interface CardPickTarget extends EventTarget { config: LovelaceCardConfig; } diff --git a/src/resources/markdown_worker.ts b/src/resources/markdown_worker.ts index f3bdbc1ee1..70507bb9fa 100644 --- a/src/resources/markdown_worker.ts +++ b/src/resources/markdown_worker.ts @@ -1,3 +1,6 @@ +// To use comlink under ES5 +import "proxy-polyfill"; +import { expose } from "comlink"; import marked from "marked"; // @ts-ignore import filterXSS from "xss"; @@ -9,14 +12,14 @@ interface WhiteList { let whiteListNormal: WhiteList | undefined; let whiteListSvg: WhiteList | undefined; -export const renderMarkdown = ( +const renderMarkdown = ( content: string, markedOptions: object, hassOptions: { // Do not allow SVG on untrusted content, it allows XSS. allowSvg?: boolean; } = {} -) => { +): string => { if (!whiteListNormal) { whiteListNormal = { ...filterXSS.whiteList, @@ -45,3 +48,10 @@ export const renderMarkdown = ( whiteList, }); }; + +// Export for types +export const api = { + renderMarkdown, +}; + +expose(api); diff --git a/src/resources/render-markdown.ts b/src/resources/render-markdown.ts new file mode 100644 index 0000000000..d4a4397f21 --- /dev/null +++ b/src/resources/render-markdown.ts @@ -0,0 +1,18 @@ +import { wrap } from "comlink"; + +type RenderMarkdownType = typeof import("./markdown_worker").api["renderMarkdown"]; +type renderMarkdownParamTypes = Parameters; + +let worker: any | undefined; + +export const renderMarkdown = async ( + content: renderMarkdownParamTypes[0], + markedOptions: renderMarkdownParamTypes[1], + hassOptions?: renderMarkdownParamTypes[2] +): Promise> => { + if (!worker) { + worker = wrap(new Worker("./markdown_worker", { type: "module" })); + } + + return await worker.renderMarkdown(content, markedOptions, hassOptions); +}; diff --git a/translations/frontend/ar.json b/translations/frontend/ar.json index c3b895042a..ecfac54d61 100644 --- a/translations/frontend/ar.json +++ b/translations/frontend/ar.json @@ -998,7 +998,7 @@ }, "views": { "confirm_delete_existing_cards": "سيؤدي حذف هذا العرض إلى إزالة البطاقات أيضًا", - "confirm_delete_existing_cards_text": "هل أنت متأكد من أنك تريد حذف طريقة عرض '{name}'؟ تحتوي طريقة العرض على {number} بطاقات سيتم حذفها. لا يمكن التراجع عن هذا الإجراء.", + "confirm_delete_existing_cards_text": "هل أنت متأكد من أنك تريد حذف طريقة عرض ''{name}''؟ تحتوي طريقة العرض على {number} بطاقات سيتم حذفها. لا يمكن التراجع عن هذا الإجراء.", "confirm_delete_text": "هل أنت متأكد من أنك تريد حذف عرض \"{name}\" ؟" }, "warning": { diff --git a/translations/frontend/bg.json b/translations/frontend/bg.json index 0cbf0fc228..5515cdf8df 100644 --- a/translations/frontend/bg.json +++ b/translations/frontend/bg.json @@ -511,7 +511,7 @@ "max": "Максимална дължина", "min": "Минимална дължина" }, - "platform_not_loaded": "{platform} интеграцията не е заредена. Моля, добавете я във вашата конфигурация като добавите 'default_config:' или '{platform}:'.", + "platform_not_loaded": "{platform} интеграцията не е заредена. Моля, добавете я във вашата конфигурация като добавите 'default_config:' или ''{platform}:''.", "yaml_not_editable": "Настройките на този обект не могат да бъдат редактирани. Могат да се конфигурират само обектите, създадени от потребителския интерфейс." }, "more_info_control": { diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index b80dd442ed..38b2b25f94 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -617,7 +617,7 @@ "pattern": "Patró Regex per a la validació de client", "text": "Text" }, - "platform_not_loaded": "La integració {platform} no està carregada, afegeix-la a la teva configuració mitjançant 'default_config:' o bé '{platform}:'.", + "platform_not_loaded": "La integració {platform} no està carregada, afegeix-la a la teva configuració mitjançant 'default_config:' o bé ''{platform}:''.", "required_error_msg": "Aquest camp és obligatori", "yaml_not_editable": "La configuració d'aquesta entitat no es pot editar des de la interfície d'usuari. Només es poden editar des de la interfície aquelles entitats que s'han configurat des d'ella." }, @@ -2285,7 +2285,7 @@ "views": { "confirm_delete": "Esborrar visualització?", "confirm_delete_existing_cards": "Si elimines aquesta vista, també s'eliminaran les targetes que conté", - "confirm_delete_existing_cards_text": "Estàs segur que vols eliminar la vista '{name}'? La vista conté {number} targetes que també s'eliminaran. Aquesta acció no és reversible.", + "confirm_delete_existing_cards_text": "Estàs segur que vols eliminar la vista ''{name}''? La vista conté {number} targetes que també s'eliminaran. Aquesta acció no és reversible.", "confirm_delete_text": "Estàs segur que vols eliminar la vista '{name}'?" }, "warning": { diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index d8c9819371..5b8fe10e52 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -611,7 +611,7 @@ "pattern": "Vzor regex pro ověření na straně klienta", "text": "Text" }, - "platform_not_loaded": "Integrace {platform} není načtena. Přidejte ji do své konfigurace buďto přidáním 'default_config:' nebo '{platform}:'.", + "platform_not_loaded": "Integrace {platform} není načtena. Přidejte ji do své konfigurace buďto přidáním 'default_config:' nebo ''{platform}:''.", "required_error_msg": "Toto pole je povinné", "yaml_not_editable": "Nastavení této entity nelze upravovat z uživatelského rozhraní. Pouze entity nastavené z uživatelského rozhraní lze konfigurovat v uživatelském rozhraní." }, @@ -2261,8 +2261,8 @@ "views": { "confirm_delete": "Odstranit zobrazení?", "confirm_delete_existing_cards": "Odstraněním tohoto pohledu dojde také k odebrání karet", - "confirm_delete_existing_cards_text": "Opravdu chcete smazat pohled '{name}'? Pohled obsahuje {number} karty, které budou smazány. Tuto akci nelze vrátit zpět.", - "confirm_delete_text": "Opravdu chcete smazat pohled '{name}'?" + "confirm_delete_existing_cards_text": "Opravdu chcete smazat pohled ''{name}''? Pohled obsahuje {number} karty, které budou smazány. Tuto akci nelze vrátit zpět.", + "confirm_delete_text": "Opravdu chcete smazat pohled ''{name}''?" }, "warning": { "attribute_not_found": "Atribut {attribute} není k dispozici v: {entity}", diff --git a/translations/frontend/cy.json b/translations/frontend/cy.json index f8ecf5c9e8..4e5abc4440 100644 --- a/translations/frontend/cy.json +++ b/translations/frontend/cy.json @@ -400,7 +400,7 @@ "max": "Hyd mwyaf", "min": "Hyd lleiaf" }, - "platform_not_loaded": "Nid yw'r integreiddiad {platform} wedi'i lwytho. Ychwanegwch eich cyfluniad ato naill ai trwy ychwanegu 'default_config:' neu '{platform}:'.", + "platform_not_loaded": "Nid yw'r integreiddiad {platform} wedi'i lwytho. Ychwanegwch eich cyfluniad ato naill ai trwy ychwanegu 'default_config:' neu ''{platform}:''.", "yaml_not_editable": "Ni ellir olygu gosodiadau'r endid hwn o'r UI. Dim ond endidau a sefydlwyd o'r UI y gellir eu ffurfweddu o'r UI." }, "more_info_control": { diff --git a/translations/frontend/da.json b/translations/frontend/da.json index 929e393783..61ffa87ab4 100644 --- a/translations/frontend/da.json +++ b/translations/frontend/da.json @@ -617,7 +617,7 @@ "pattern": "Regex-mønster til validering på klientsiden", "text": "Tekst" }, - "platform_not_loaded": "{platform}-integrationen er ikke indlæst. Tilføj den til din konfiguration. Enten ved at tilføje 'default_config:' eller '{platform}:'.", + "platform_not_loaded": "{platform}-integrationen er ikke indlæst. Tilføj den til din konfiguration. Enten ved at tilføje 'default_config:' eller ''{platform}:''.", "required_error_msg": "Dette felt er påkrævet", "yaml_not_editable": "Indstillingerne for denne entitet kan ikke redigeres fra brugerfladen. Det er kun entiteter, der er konfigureret fra brugerfladen, der kan konfigureres herfra." }, @@ -1374,7 +1374,8 @@ "restart_confirm": "Genstart Home Assistant for at fuldføre fjernelsen af denne integration", "settings_button": "Ret indstillinger for {integration}", "system_options": "Systemindstillinger", - "system_options_button": "Systemindstillinger for {integration}" + "system_options_button": "Systemindstillinger for {integration}", + "unnamed_entry": "Unavngivet post" }, "config_flow": { "aborted": "Afbrudt", @@ -2284,8 +2285,8 @@ "views": { "confirm_delete": "Slet visning?", "confirm_delete_existing_cards": "Sletning af denne visning fjerner også kortene", - "confirm_delete_existing_cards_text": "Er du sikker på, at du vil slette visningen '{name}'? Visningen indeholder {number} kort, der vil blive slettet. Denne handling kan ikke fortrydes.", - "confirm_delete_text": "Er du sikker på, at du vil slette visningen '{name}'?" + "confirm_delete_existing_cards_text": "Er du sikker på, at du vil slette visningen ''{name}''? Visningen indeholder {number} kort, der vil blive slettet. Denne handling kan ikke fortrydes.", + "confirm_delete_text": "Er du sikker på, at du vil slette visningen ''{name}''?" }, "warning": { "attribute_not_found": "Egenskaben {attribute} er ikke tilgængelig i: {entity}", diff --git a/translations/frontend/de.json b/translations/frontend/de.json index 307d5175ee..900c7724d4 100644 --- a/translations/frontend/de.json +++ b/translations/frontend/de.json @@ -617,7 +617,7 @@ "pattern": "Regex-Muster für die clientseitige Validierung", "text": "Text" }, - "platform_not_loaded": "Die {platform} -Komponente ist nicht geladen. Füge sie bitte deiner Konfiguration hinzu. Entweder durch Hinzufügen von 'default_config:' oder '{platform}:'.", + "platform_not_loaded": "Die {platform} -Komponente ist nicht geladen. Füge sie bitte deiner Konfiguration hinzu. Entweder durch Hinzufügen von 'default_config:' oder ''{platform}:''.", "required_error_msg": "Dieses Feld ist erforderlich", "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." }, @@ -1374,7 +1374,8 @@ "restart_confirm": "Starte Home Assistant neu, um das Entfernen dieser Integration abzuschließen", "settings_button": "Einstellungen für {integration} bearbeiten", "system_options": "Systemoptionen", - "system_options_button": "Systemoptionen für {integration}" + "system_options_button": "Systemoptionen für {integration}", + "unnamed_entry": "Unbenannter Eintrag" }, "config_flow": { "aborted": "Abgebrochen", @@ -2284,8 +2285,8 @@ "views": { "confirm_delete": "Möchten Sie diese Ansicht wirklich löschen?", "confirm_delete_existing_cards": "Durch Löschen dieser Ansicht werden auch die Karten entfernt", - "confirm_delete_existing_cards_text": "Möchtest du deine Ansicht '{name}' wirklich löschen? Die Ansicht enthält {number} Karten, die gelöscht werden. Diese Aktion kann nicht rückgängig gemacht werden.", - "confirm_delete_text": "Möchtest du deine Ansicht '{name}' wirklich löschen?" + "confirm_delete_existing_cards_text": "Möchtest du deine Ansicht ''{name}'' wirklich löschen? Die Ansicht enthält {number} Karten, die gelöscht werden. Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm_delete_text": "Möchtest du deine Ansicht ''{name}'' wirklich löschen?" }, "warning": { "attribute_not_found": "Attribut {attribute} nicht verfügbar in: {entity}", diff --git a/translations/frontend/es-419.json b/translations/frontend/es-419.json index 1f5743e73f..e0511c0a46 100644 --- a/translations/frontend/es-419.json +++ b/translations/frontend/es-419.json @@ -617,7 +617,7 @@ "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. Agregue su 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." }, @@ -2284,8 +2284,8 @@ "views": { "confirm_delete": "¿Eliminar vista?", "confirm_delete_existing_cards": "Eliminar esta vista también eliminará las tarjetas", - "confirm_delete_existing_cards_text": "¿Está seguro de que desea eliminar su vista '{name}'? La vista contiene {number} tarjetas que se eliminarán. Esta acción no se puede deshacer.", - "confirm_delete_text": "¿Está seguro de que desea eliminar su vista '{name}'?" + "confirm_delete_existing_cards_text": "¿Está seguro de que desea eliminar su vista ''{name}''? La vista contiene {number} tarjetas que se eliminarán. Esta acción no se puede deshacer.", + "confirm_delete_text": "¿Está seguro de que desea eliminar su vista ''{name}''?" }, "warning": { "attribute_not_found": "El atributo {attribute} no está disponible en: {entity}", diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 31558a169c..7f8986282b 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -617,7 +617,7 @@ "pattern": "Patrón Regex para la validación del lado del cliente", "text": "Texto" }, - "platform_not_loaded": "La integración {platform} no se ha cargado. Por favor añádela a tu configuración, ya sea agregando 'default_config:' o '{platform}:'.", + "platform_not_loaded": "La integración {platform} no se ha cargado. Por favor añádela a tu configuración, ya sea agregando 'default_config:' o ''{platform}:''.", "required_error_msg": "Este campo es obligatorio", "yaml_not_editable": "La configuración de esta entidad no se puede editar desde la IU. Solo las entidades configuradas desde la IU se pueden configurar desde la IU" }, @@ -742,7 +742,7 @@ }, "panel": { "calendar": { - "my_calendars": "Mis Calendarios", + "my_calendars": "Mis calendarios", "today": "Hoy" }, "config": { @@ -2285,8 +2285,8 @@ "views": { "confirm_delete": "¿Eliminar vista?", "confirm_delete_existing_cards": "Al eliminar esta vista también se eliminarán las tarjetas", - "confirm_delete_existing_cards_text": "¿Estás seguro de que deseas eliminar tu vista '{name}'? La vista contiene {number} tarjetas que se eliminarán. Esta acción no se puede deshacer.", - "confirm_delete_text": "¿Estás seguro de que deseas eliminar tu vista '{name}'?" + "confirm_delete_existing_cards_text": "¿Estás seguro de que deseas eliminar tu vista ''{name}''? La vista contiene {number} tarjetas que se eliminarán. Esta acción no se puede deshacer.", + "confirm_delete_text": "¿Estás seguro de que deseas eliminar tu vista ''{name}''?" }, "warning": { "attribute_not_found": "El atributo {attribute} no está disponible en: {entity}", diff --git a/translations/frontend/fa.json b/translations/frontend/fa.json index b0ce451679..42b8748594 100644 --- a/translations/frontend/fa.json +++ b/translations/frontend/fa.json @@ -503,7 +503,7 @@ "password": "Parola", "text": "Text" }, - "platform_not_loaded": "ادغام {platform} بارگیری نشده است. لطفاً تنظیمات خود را با اضافه کردن 'default_config:' یا '{platform}:' تنظیمات خود را اضافه کنید.", + "platform_not_loaded": "ادغام {platform} بارگیری نشده است. لطفاً تنظیمات خود را با اضافه کردن 'default_config:' یا ''{platform}:'' تنظیمات خود را اضافه کنید.", "yaml_not_editable": "تنظیمات این نهاد نمی تواند از UI ویرایش شود. فقط موجودیت هایی که از UI تنظیم می شوند از UI قابل تنظیم هستند." }, "more_info_control": { diff --git a/translations/frontend/fi.json b/translations/frontend/fi.json index 8fb815992a..6b0fdd780d 100644 --- a/translations/frontend/fi.json +++ b/translations/frontend/fi.json @@ -617,7 +617,7 @@ "pattern": "Regex-malli käyttäjäpuolen validointia varten.", "text": "Teksti" }, - "platform_not_loaded": "Integrointia {platform} ei ole ladattu. Voit ladata sen llisäämällä \"default_config:\" tai {platform}: konfiguraatiotiedostoon.", + "platform_not_loaded": "Integrointia {platform} ei ole ladattu. Voit ladata sen llisäämällä \"default_config:\" tai ''{platform}:'' konfiguraatiotiedostoon.", "required_error_msg": "Tämä kenttä on pakollinen", "yaml_not_editable": "Tämän kohteen asetuksia ei voi muokata käyttöliittymästä. Vain käyttöliittymästä määritetyt kohteet ovat määritettävissä käyttöliittymästä." }, @@ -2285,7 +2285,7 @@ "confirm_delete": "Oletko varma, että haluat poistaa tämän näkymän?", "confirm_delete_existing_cards": "Tämän näkymän poistaminen poistaa myös kortit", "confirm_delete_existing_cards_text": "Haluatko varmasti poistaa näkymän {name}? Näkymä sisältää {number}-kortteja, jotka poistetaan. Tätä toimintoa ei voi kumota.", - "confirm_delete_text": "Haluatko varmasti poistaa '{name}' -näkymäsi?" + "confirm_delete_text": "Haluatko varmasti poistaa ''{name}'' -näkymäsi?" }, "warning": { "attribute_not_found": "Määrite {attribute} ei ole käytettävissä kohteessa {entity}", diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 35712a95b6..a2d09c2c11 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -617,7 +617,7 @@ "pattern": "Modèle d'expression régulière pour la validation côté client", "text": "Texte" }, - "platform_not_loaded": "L'intégration {platform} n'est pas chargée. Veuillez l'ajouter à votre configuration en ajoutant 'default_config:' ou '{platform}:'.", + "platform_not_loaded": "L'intégration {platform} n'est pas chargée. Veuillez l'ajouter à votre configuration en ajoutant 'default_config:' ou ''{platform}:''.", "required_error_msg": "Ce champ est requis", "yaml_not_editable": "Les paramètres de cette entité ne peuvent pas être modifiés à partir de l'interface utilisateur. Seules les entités configurées à partir de l'interface utilisateur sont configurables à partir de l'interface utilisateur." }, @@ -2285,8 +2285,8 @@ "views": { "confirm_delete": "Êtes-vous sûr de vouloir supprimer cette vue?", "confirm_delete_existing_cards": "La suppression de cette vue supprimera également les cartes", - "confirm_delete_existing_cards_text": "Voulez-vous vraiment supprimer votre vue '{name}'? La vue contient {number} cartes qui seront supprimées. Cette action ne peut pas être annulée.", - "confirm_delete_text": "Voulez-vous vraiment supprimer votre vue '{name}'?" + "confirm_delete_existing_cards_text": "Voulez-vous vraiment supprimer votre vue ''{name}''? La vue contient {number} cartes qui seront supprimées. Cette action ne peut pas être annulée.", + "confirm_delete_text": "Voulez-vous vraiment supprimer votre vue ''{name}''?" }, "warning": { "attribute_not_found": "Attribut {attribute} non disponible dans: {entity}", diff --git a/translations/frontend/hu.json b/translations/frontend/hu.json index b665183ee2..cea4ef4bcb 100644 --- a/translations/frontend/hu.json +++ b/translations/frontend/hu.json @@ -473,7 +473,7 @@ } }, "common": { - "and": "És", + "and": "és", "cancel": "Mégse", "close": "Bezárás", "delete": "Törlés", @@ -617,7 +617,7 @@ "pattern": "Regex minta az ügyféloldali érvényesítéshez", "text": "Szöveg" }, - "platform_not_loaded": "A(z) {platform} integráció nincs betöltve. Add hozzá a konfigurációdhoz a 'default_config:' vagy a(z) '{platform}:' hozzáadásával.", + "platform_not_loaded": "A(z) {platform} integráció nincs betöltve. Add hozzá a konfigurációdhoz a 'default_config:' vagy a(z) ''{platform}:'' hozzáadásával.", "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." }, @@ -666,7 +666,7 @@ "no_entities": "Nincsenek entitások", "no_triggers": "Nincsenek eseményindítók", "payload_display": "Payload megjelenítése", - "recent_messages": "{n} legfrissebb fogadott üzenet(ek)", + "recent_messages": "{n} legfrissebb fogadott üzenet", "show_as_yaml": "Megjelenítés YAML-ként", "title": "{device} debug infók", "triggers": "Eseményindítók" @@ -743,7 +743,7 @@ "panel": { "calendar": { "my_calendars": "Saját naptáraim", - "today": "Ma" + "today": "ma" }, "config": { "advanced_mode": { @@ -848,7 +848,7 @@ "label": "Eszköz" }, "not": { - "label": "Nem" + "label": "nem" }, "numeric_state": { "above": "Felett", @@ -1181,9 +1181,9 @@ "edit_requires_storage": "A szerkesztő le van tiltva, mert a konfiguráció a configuration.yaml fájlban van tárolva.", "elevation": "Magasság", "elevation_meters": "méter", - "external_url": "Externe URL", + "external_url": "Külső URL", "imperial_example": "Fahrenheit, font", - "internal_url": "Érvénytelen URL", + "internal_url": "Belső URL", "latitude": "Szélesség", "location_name": "A Home Assistant rendszered neve", "longitude": "Hosszúság", @@ -1374,7 +1374,8 @@ "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", "system_options": "Rendszerbeállítások", - "system_options_button": "{integration} rendszerbeállításai" + "system_options_button": "{integration} rendszerbeállításai", + "unnamed_entry": "Névtelen bejegyzés" }, "config_flow": { "aborted": "Megszakítva", @@ -1878,10 +1879,13 @@ "built_using": "Buildelve:", "custom_uis": "Egyéni felhasználói felületek:", "developed_by": "Egy csomó fantasztikus ember által kifejlesztve.", + "documentation": "Dokumentáció", "frontend": "frontend-ui", "frontend_version": "Frontend verzió: {version} - {type}", "home_assistant_logo": "Home Assistant logó", "icons_by": "Ikonokat készítette:", + "integrations": "Integrációk", + "issues": "Problémák", "license": "Megjelent az Apache 2.0 licenc alatt", "path_configuration": "A configuration.yaml fájl elérési útja: {path}", "server": "server", @@ -2072,6 +2076,7 @@ "name": "Név", "no_theme": "Nincs téma", "refresh_interval": "Frissítési időköz", + "secondary_info_attribute": "Másodlagos információ attribútum", "show_icon": "Ikon megjelenítése?", "show_name": "Név megjelenítése?", "show_state": "Állapot megjelenítése?", @@ -2162,7 +2167,8 @@ }, "weather-forecast": { "description": "Az Időjárás Előrejelzés kártya az időjárást jeleníti meg. Nagyon hasznos hozzáadni a falra felszerelt kezelő interfészek felületéhez.", - "name": "Időjárás előrejelzés" + "name": "Időjárás előrejelzés", + "show_forecast": "Előrejelzés megjelenítése" } }, "cardpicker": { @@ -2279,8 +2285,8 @@ "views": { "confirm_delete": "Nézet törlése?", "confirm_delete_existing_cards": "A nézet törlésével a kártyák is eltávolításra kerülnek", - "confirm_delete_existing_cards_text": "Biztosan törölni szeretnéd a(z) '{name}' nézetet? A nézet {number} kártyát tartalmaz, melyek törlődni fognak. Ez a művelet nem vonható vissza.", - "confirm_delete_text": "Biztosan törölni szeretnéd a(z) {name} nézetet?" + "confirm_delete_existing_cards_text": "Biztosan törölni szeretnéd a(z) ''{name}'' nézetet? A nézet {number} kártyát tartalmaz, melyek törlődni fognak. Ez a művelet nem vonható vissza.", + "confirm_delete_text": "Biztosan törölni szeretnéd a(z) \"{name}\" nézetet?" }, "warning": { "attribute_not_found": "A(z) {attribute} attribútum nem érhető el a(z) {entity} entitásban.", diff --git a/translations/frontend/it.json b/translations/frontend/it.json index 6ab822b611..8f5ee6d880 100644 --- a/translations/frontend/it.json +++ b/translations/frontend/it.json @@ -617,7 +617,7 @@ "pattern": "Schema Regex per la validazione lato cliente", "text": "Testo" }, - "platform_not_loaded": "L'integrazione {platform} non è stata caricata. Si prega di aggiungerla alla propria configurazione, aggiungendo 'default_config:' o '{platform}:'.", + "platform_not_loaded": "L'integrazione {platform} non è stata caricata. Si prega di aggiungerla alla propria configurazione, aggiungendo 'default_config:' o ''{platform}:''.", "required_error_msg": "Questo campo è obbligatorio", "yaml_not_editable": "Le impostazioni di questa entità non possono essere modificate dall'Interfaccia Utente. Solo le entità impostate dall'Interfaccia Utente sono configurabili dall'Interfaccia Utente." }, diff --git a/translations/frontend/ko.json b/translations/frontend/ko.json index 18fa92de81..a894f92a5b 100644 --- a/translations/frontend/ko.json +++ b/translations/frontend/ko.json @@ -591,7 +591,7 @@ "input_datetime": { "date": "날짜", "datetime": "날짜와 시간", - "mode": "무엇을 입력하시겠습니까?", + "mode": "입력할 내용", "time": "시간" }, "input_number": { @@ -617,7 +617,7 @@ "pattern": "클라이언트 측 검증을 위한 정규표현식", "text": "텍스트" }, - "platform_not_loaded": "{platform} 통합 구성요소가 로드되지 않았습니다. 'default_config:' 또는 '{platform} :' 을 추가하여 구성을 추가해주세요.", + "platform_not_loaded": "{platform} 통합 구성요소가 로드되지 않았습니다. 'default_config:' 또는 \"{platform}:\" 을 추가하여 구성을 추가해주세요.", "required_error_msg": "이 입력란은 필수 요소입니다", "yaml_not_editable": "이 구성요소의 설정은 UI 에서 편집할 수 없습니다. UI 에서 설정한 구성요소만 UI 에서 구성할 수 있습니다." }, @@ -2043,7 +2043,7 @@ "name": "구성요소 필터" }, "entity": { - "description": "구성요소 카드는 구성요소의 상태에 대한 빠른 개요를 제공합니다.", + "description": "구성요소 카드는 구성요소의 상태에 대한 빠른 보기를 제공합니다.", "name": "구성요소" }, "gauge": { @@ -2285,8 +2285,8 @@ "views": { "confirm_delete": "이 뷰를 삭제하시겠습니까?", "confirm_delete_existing_cards": "이 뷰를 삭제하면 포함된 카드도 함께 제거됩니다", - "confirm_delete_existing_cards_text": "'{name}' 뷰를 삭제하시겠습니까? 이 뷰에 포함된 {number} 카드도 함께 제거됩니다. 이 작업은 취소할 수 없습니다.", - "confirm_delete_text": "'{name}' 뷰를 삭제하시겠습니까?" + "confirm_delete_existing_cards_text": "\"{name}\" 뷰를 삭제하시겠습니까? 뷰에 포함된 {number}개의 카드도 함께 제거됩니다. 이 작업은 취소할 수 없습니다.", + "confirm_delete_text": "\"{name}\" 뷰를 삭제하시겠습니까?" }, "warning": { "attribute_not_found": "{attribute} 속성을 사용할 수 없습니다: {entity}", diff --git a/translations/frontend/lb.json b/translations/frontend/lb.json index 318036fe13..3eb10b8466 100644 --- a/translations/frontend/lb.json +++ b/translations/frontend/lb.json @@ -617,7 +617,7 @@ "pattern": "Regex Muster fir Client-Säiteg Validatioun", "text": "Text" }, - "platform_not_loaded": "{platform} Integratioun ass net gelueden. Setz et an deng Konfiguration dobäi mat 'default_config:' oder '{platform}:'.", + "platform_not_loaded": "{platform} Integratioun ass net gelueden. Setz et an deng Konfiguration dobäi mat 'default_config:' oder ''{platform}:''.", "required_error_msg": "Dëst Feld ass erfuerderlech", "yaml_not_editable": "D'Astellunge vun dëser Entitéit kënnen net vun vum Benotzer Interface as geännert ginn. Nëmmen Entitéiten déi iwwer den Benotzer Interface aus konfiguréiert sinn kënnen vun do aus geännert ginn." }, @@ -1374,7 +1374,8 @@ "restart_confirm": "Start Home Assistant nei fir dës Integratioun ze läschen", "settings_button": "Astellungen ännere fir {integration}", "system_options": "System Optiounen", - "system_options_button": "System Optioune fir {integration}" + "system_options_button": "System Optioune fir {integration}", + "unnamed_entry": "Entrée ouni Numm" }, "config_flow": { "aborted": "Ofgebrach", @@ -2284,8 +2285,8 @@ "views": { "confirm_delete": "Usiicht läschen?", "confirm_delete_existing_cards": "D'Läschen vun dëser Usiicht läscht och d'Kaarten", - "confirm_delete_existing_cards_text": "Sécher fir d'Usiicht '{name}' ze läsche? Dës Usiicht enthält {number} Kaart(en) déi och geläscht ginn. Dës Aktioun kann net réckgängeg gemaach ginn.", - "confirm_delete_text": "Sécher fir d'Usiicht '{name}' ze läsche?" + "confirm_delete_existing_cards_text": "Sécher fir d'Usiicht ''{name}'' ze läsche? Dës Usiicht enthält {number} Kaart(en) déi och geläscht ginn. Dës Aktioun kann net réckgängeg gemaach ginn.", + "confirm_delete_text": "Sécher fir d'Usiicht ''{name}'' ze läsche?" }, "warning": { "attribute_not_found": "Attribut {attribute} net disponibel an: {entity}", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index de3a92c307..7e032cf358 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -617,7 +617,7 @@ "pattern": "Regex-mønster for validering på klientsiden", "text": "Tekst" }, - "platform_not_loaded": "{platform}-integrasjonen er ikke lastet inn. Legg til konfigurasjonen ved å legge til 'default_config:' eller '{platform}:'.", + "platform_not_loaded": "Integrasjonen {platform} er ikke lastet. Legg til konfigurasjonen din enten ved å legge til 'default_config:' eller '' {platform} : ''.", "required_error_msg": "Dette feltet er påkrevd", "yaml_not_editable": "Innstillingene for denne entiteten kan ikke redigeres fra brukergrensesnittet. Bare entiteter som er satt opp fra brukergrensesnittet, kan konfigureres fra brukergrensesnittet." }, @@ -2285,8 +2285,8 @@ "views": { "confirm_delete": "Slette visning?", "confirm_delete_existing_cards": "Sletting av denne visningen vil også slette kortene", - "confirm_delete_existing_cards_text": "Er du sikker på at du vil slette visningen '{name}'? Visningen inneholder {number} kort som blir slettet. Denne handlingen kan ikke angre.", - "confirm_delete_text": "Er du sikker på at du vil slette visningen '{name}'?" + "confirm_delete_existing_cards_text": "Er du sikker på at du vil slette visningen '' {name} ''? Visningen inneholder {number} kort som blir slettet. Denne handlingen kan ikke angre.", + "confirm_delete_text": "Er du sikker på at du vil slette visningen '' {name} ''?" }, "warning": { "attribute_not_found": "Attributtet {attribute} er ikke tilgjengelig i: {entity}", diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index f9f250905f..048c1c4d0d 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -617,7 +617,7 @@ "pattern": "Regex-patroon voor clientvalidatie", "text": "Tekst" }, - "platform_not_loaded": "De {platform} integratie is niet geladen. Voeg het aan je configuratie toe door 'default_config:' of '{platform}:' toe te voegen.", + "platform_not_loaded": "De {platform} integratie is niet geladen. Voeg het aan je configuratie toe door 'default_config:' of ''{platform}:'' toe te voegen.", "required_error_msg": "Dit veld is verplicht", "yaml_not_editable": "De instellingen van deze entiteit kunnen niet worden bewerkt vanuit de gebruikersinterface. Alleen entiteiten die zijn ingesteld vanuit de gebruikersinterface, kunnen worden geconfigureerd vanuit de gebruikersinterface." }, @@ -2279,8 +2279,8 @@ "views": { "confirm_delete": "Weergave verwijderen?", "confirm_delete_existing_cards": "Als u deze weergave verwijdert, worden ook de kaarten verwijderd", - "confirm_delete_existing_cards_text": "Weet u zeker dat u uw weergave '{name}' wilt verwijderen? De weergave bevat {number} kaarten die worden verwijderd. Deze actie kan niet ongedaan gemaakt worden.", - "confirm_delete_text": "Weet u zeker dat u uw '{naam}'-weergave wilt verwijderen?" + "confirm_delete_existing_cards_text": "Weet u zeker dat u uw weergave ''{name}'' wilt verwijderen? De weergave bevat {number} kaarten die worden verwijderd. Deze actie kan niet ongedaan gemaakt worden.", + "confirm_delete_text": "Weet u zeker dat u uw ''{naam}''-weergave wilt verwijderen?" }, "warning": { "attribute_not_found": "Kenmerk {attribute} niet beschikbaar in: {entity}", diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index d917d6fe3c..3ed6bf40d6 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -617,7 +617,7 @@ "pattern": "Wyrażenie regularne do sprawdzania poprawności po stronie klienta", "text": "Pole tekstowe" }, - "platform_not_loaded": "Komponent {platform} nie jest załadowany, dodaj go do swojej konfiguracji dodając 'default_config:' lub '{platform}:'.", + "platform_not_loaded": "Komponent {platform} nie jest załadowany, dodaj go do swojej konfiguracji dodając 'default_config:' lub ''{platform}:''.", "required_error_msg": "To pole jest wymagane", "yaml_not_editable": "Ustawienia tej encji nie mogą być edytowane z interfejsu użytkownika. Tylko encje dodane z interfejsu użytkownika można konfigurować z poziomu interfejsu użytkownika." }, @@ -2285,8 +2285,8 @@ "views": { "confirm_delete": "Na pewno chcesz usunąć ten widok?", "confirm_delete_existing_cards": "Usunięcie tego widoku spowoduje również usunięcie kart", - "confirm_delete_existing_cards_text": "Czy na pewno chcesz usunąć widok '{name}'? Widok zawiera {number} kart(ę/y), które zostaną usunięte. Tej akcji nie można cofnąć.", - "confirm_delete_text": "Czy na pewno chcesz usunąć widok '{name}'?" + "confirm_delete_existing_cards_text": "Czy na pewno chcesz usunąć widok ''{name}''? Widok zawiera {number} kart(ę/y), które zostaną usunięte. Tej akcji nie można cofnąć.", + "confirm_delete_text": "Czy na pewno chcesz usunąć widok ''{name}''?" }, "warning": { "attribute_not_found": "Atrybut {attribute} jest niedostępny dla: {entity}", diff --git a/translations/frontend/pt-BR.json b/translations/frontend/pt-BR.json index 49326d2ad3..f9589b311b 100644 --- a/translations/frontend/pt-BR.json +++ b/translations/frontend/pt-BR.json @@ -473,11 +473,15 @@ } }, "common": { + "and": "e", "cancel": "Cancelar", "close": "Fechar", "delete": "Eliminar", "loading": "Carregando", + "next": "Próximo", "no": "Não", + "previous": "Anterior", + "refresh": "Atualizar", "save": "Salvar", "successfully_deleted": "Eliminado com sucesso", "successfully_saved": "Salvo com sucesso", @@ -613,7 +617,7 @@ "pattern": "Padrão Regex para validação do lado do cliente", "text": "Texto" }, - "platform_not_loaded": "A integração {platform} não está carregada. Por favor, adicione sua configuração adicionando 'default_config:' ou '{platform}:'.", + "platform_not_loaded": "A integração {platform} não está carregada. Por favor, adicione sua configuração adicionando 'default_config:' ou ''{platform}:''.", "required_error_msg": "Este campo é obrigatório", "yaml_not_editable": "As configurações desta entidade não podem ser editadas a partir da interface do usuário. Somente entidades configuradas a partir da interface do usuário são configuráveis a partir da interface do usuário." }, @@ -737,6 +741,10 @@ "triggered": "{name} disparado" }, "panel": { + "calendar": { + "my_calendars": "Meus Calendários", + "today": "Hoje" + }, "config": { "advanced_mode": { "hint_enable": "Faltam opções de configuração? Ative o modo avançado", @@ -839,6 +847,9 @@ }, "label": "Dispositivo" }, + "not": { + "label": "Não" + }, "numeric_state": { "above": "Acima", "below": "Abaixo", @@ -1170,7 +1181,9 @@ "edit_requires_storage": "Editor desativado porque a configuração está armazenada em configuration.yaml.", "elevation": "Elevação", "elevation_meters": "metros", + "external_url": "URL externa", "imperial_example": "Fahrenheit, libras", + "internal_url": "URL interna", "latitude": "Latitude", "location_name": "Nome da sua instalação do Home Assistant", "longitude": "Longitude", @@ -1237,6 +1250,7 @@ }, "delete": "Eliminar", "description": "Gerenciar dispositivos conectados", + "device_info": "Informação do dispositivo", "device_not_found": "Dispositivo não encontrado.", "entities": { "add_entities_lovelace": "Adicionar ao Lovelace", @@ -1360,7 +1374,8 @@ "restart_confirm": "Reinicie o Home Assistant para concluir a remoção dessa integração", "settings_button": "Editar configurações para {integration}", "system_options": "Opções do sistema", - "system_options_button": "Opções do sistema para {integration}" + "system_options_button": "Opções do sistema para {integration}", + "unnamed_entry": "Entrada sem nome" }, "config_flow": { "aborted": "Abortado", @@ -1864,10 +1879,13 @@ "built_using": "Construído usando", "custom_uis": "UIs personalizadas:", "developed_by": "Desenvolvido por um monte de pessoas incríveis.", + "documentation": "Documentação", "frontend": "frontend-ui", "frontend_version": "Versão do Frontend: {version} - {type}", "home_assistant_logo": "Home Assistant logo", "icons_by": "Ícones por", + "integrations": "Integrações", + "issues": "Problemas", "license": "Publicado sob a licença Apache 2.0", "path_configuration": "Caminho para configuration.yaml: {path}", "server": "servidor", @@ -2058,6 +2076,7 @@ "name": "Nome", "no_theme": "Nenhum tema", "refresh_interval": "Intervalo de atualização", + "secondary_info_attribute": "Atributo de informação secundária", "show_icon": "Mostrar Icone?", "show_name": "Mostrar nome?", "show_state": "Mostrar Estado?", @@ -2148,7 +2167,8 @@ }, "weather-forecast": { "description": "O cartão Previsão do tempo exibe o clima. Muito útil para incluir nas interfaces que as pessoas exibem na parede.", - "name": "Previsão do Tempo" + "name": "Previsão do Tempo", + "show_forecast": "Mostrar previsão" } }, "cardpicker": { @@ -2265,8 +2285,8 @@ "views": { "confirm_delete": "Excluir visualização", "confirm_delete_existing_cards": "Eliminar esta visualização vai também remover os cartões", - "confirm_delete_existing_cards_text": "Tem certeza de que deseja excluir a visualização '{name}'? Ela contém {number} cartões que serão excluídos. Esta ação não pode ser desfeita.", - "confirm_delete_text": "Tem a certeza que quer apagar a visualização '{name}'?" + "confirm_delete_existing_cards_text": "Tem certeza de que deseja excluir a visualização ''{name}''? Ela contém {number} cartões que serão excluídos. Esta ação não pode ser desfeita.", + "confirm_delete_text": "Tem a certeza que quer apagar a visualização ''{name}''?" }, "warning": { "attribute_not_found": "O atributo {attribute} não está disponível em: {entity}", diff --git a/translations/frontend/pt.json b/translations/frontend/pt.json index 39dfc7b25f..414191f2d8 100644 --- a/translations/frontend/pt.json +++ b/translations/frontend/pt.json @@ -616,7 +616,7 @@ "pattern": "Padrão Regex para validação do lado do cliente", "text": "Texto" }, - "platform_not_loaded": "A integração {platform} não foi carregada. Por favor, adicione ao ficheiro configuration.yaml, 'default_config:' ou '{platform}:'.", + "platform_not_loaded": "A integração {platform} não foi carregada. Por favor, adicione ao ficheiro configuration.yaml, 'default_config:' ou ''{platform}:''.", "required_error_msg": "Este campo é obrigatório", "yaml_not_editable": "As configurações desta entidade não podem ser editadas a partir do IU. Apenas as entidades adiccionadas a partir da IU são configuráveis a partir da IU." }, @@ -2275,8 +2275,8 @@ "views": { "confirm_delete": "Eliminar vista?", "confirm_delete_existing_cards": "Eliminar esta vista vai também remover os cartões", - "confirm_delete_existing_cards_text": "Tem certeza de que deseja excluir a sua vista '{name}'? A vista contém {number} cartões que serão excluídos. Essa ação não pode ser desfeita.", - "confirm_delete_text": "Tem certeza de que deseja apagar a sua vista '{name}'?" + "confirm_delete_existing_cards_text": "Tem certeza de que deseja excluir a sua vista ''{name}''? A vista contém {number} cartões que serão excluídos. Essa ação não pode ser desfeita.", + "confirm_delete_text": "Tem certeza de que deseja apagar a sua vista ''{name}''?" }, "warning": { "attribute_not_found": "Atributo {attribute} não disponível em: {entity}", diff --git a/translations/frontend/ro.json b/translations/frontend/ro.json index 9e44c709e8..6e91e0b0be 100644 --- a/translations/frontend/ro.json +++ b/translations/frontend/ro.json @@ -602,7 +602,7 @@ "pattern": "Modelul Regex pentru validarea de partea clientului", "text": "Text" }, - "platform_not_loaded": "Integrarea {platform} nu este încărcată. Vă rugăm să-i adăugați configurația dvs. fie adăugând 'default_config:' sau '{platform}:'.", + "platform_not_loaded": "Integrarea {platform} nu este încărcată. Vă rugăm să-i adăugați configurația dvs. fie adăugând 'default_config:' sau ''{platform}:''.", "required_error_msg": "Acest câmp este obligatoriu", "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." }, diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index e19372d34d..ee22af500a 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -617,7 +617,7 @@ "pattern": "Шаблон регулярного выражения для проверки на стороне клиента", "text": "Текст" }, - "platform_not_loaded": "Интеграция {platform} не загружена. Добавьте в YAML-конфигурацию 'default_config:' или '{platform}:'.", + "platform_not_loaded": "Интеграция \"{platform}\" не загружена. Добавьте в YAML-конфигурацию 'default_config:' или ''{platform}:''.", "required_error_msg": "Обязательное поле", "yaml_not_editable": "Настройки этого объекта нельзя изменить из пользовательского интерфейса. Настраиваться из пользовательского интерфейса могут только те объекты, которые были созданы в нём." }, @@ -2286,7 +2286,7 @@ "confirm_delete": "Удалить эту вкладку?", "confirm_delete_existing_cards": "Удалить вкладку и находящиеся в ней карточки?", "confirm_delete_existing_cards_text": "Вы уверены, что хотите удалить вкладку \"{name}\"? Эта вкладка содержит {number} карточек, которые также будут удалены. Удаление отменить будет невозможно.", - "confirm_delete_text": "Вы уверены, что хотите удалить вкладку {name}?" + "confirm_delete_text": "Вы уверены, что хотите удалить вкладку \"{name}\"?" }, "warning": { "attribute_not_found": "Атрибут {attribute} недоступен в {entity}", diff --git a/translations/frontend/sk.json b/translations/frontend/sk.json index 391429947b..c49cf64314 100644 --- a/translations/frontend/sk.json +++ b/translations/frontend/sk.json @@ -2139,8 +2139,8 @@ "views": { "confirm_delete": "Naozaj chcete odstrániť toto zobrazenie?", "confirm_delete_existing_cards": "Odstránením tohto zobrazenia sa odstránia aj karty", - "confirm_delete_existing_cards_text": "Naozaj chcete odstrániť zobrazenie '{name}'? Toto zobrazenie obsahuje {number} kariet, ktoré budú odstránené. Túto akciu nie je možné vrátiť späť.", - "confirm_delete_text": "Naozaj chcete odstrániť zobrazenie '{name}'?" + "confirm_delete_existing_cards_text": "Naozaj chcete odstrániť zobrazenie ''{name}''? Toto zobrazenie obsahuje {number} kariet, ktoré budú odstránené. Túto akciu nie je možné vrátiť späť.", + "confirm_delete_text": "Naozaj chcete odstrániť zobrazenie ''{name}''?" }, "warning": { "attribute_not_found": "Atribút {attribute} nie je k dispozícii v: {entity}", diff --git a/translations/frontend/sl.json b/translations/frontend/sl.json index d53ad04c17..c46e8a3a53 100644 --- a/translations/frontend/sl.json +++ b/translations/frontend/sl.json @@ -2268,8 +2268,8 @@ "views": { "confirm_delete": "Želite izbrisati pogled?", "confirm_delete_existing_cards": "Če izbrišete ta pogled, boste odstranili tudi kartice", - "confirm_delete_existing_cards_text": "Ali ste prepričani, da želite izbrisati svoj '{name}' pogled? Pogled vsebuje {number} kartice, ki bodo izbrisane. Tega ukrepa ni mogoče razveljaviti.", - "confirm_delete_text": "Ali ste prepričani, da želite izbrisati pogled '{name}' ?" + "confirm_delete_existing_cards_text": "Ali ste prepričani, da želite izbrisati svoj ''{name}'' pogled? Pogled vsebuje {number} kartice, ki bodo izbrisane. Tega ukrepa ni mogoče razveljaviti.", + "confirm_delete_text": "Ali ste prepričani, da želite izbrisati pogled ''{name}'' ?" }, "warning": { "attribute_not_found": "Atribut {attribute} ni na voljo v: {entity}", diff --git a/translations/frontend/sv.json b/translations/frontend/sv.json index 48ae93c4eb..1ba8aac447 100644 --- a/translations/frontend/sv.json +++ b/translations/frontend/sv.json @@ -617,7 +617,7 @@ "pattern": "Regex-mönster för validering på klientsidan", "text": "Text" }, - "platform_not_loaded": "{platform} integrationen har ej lästs in. Lägg till integrationen genom att lägga till 'default_config:' eller '{platform}:'.", + "platform_not_loaded": "{platform} integrationen har ej lästs in. Lägg till integrationen genom att lägga till 'default_config:' eller ''{platform}:''.", "required_error_msg": "Det här fältet krävs", "yaml_not_editable": "Inställningar för denna entitet kan inte ändras från användargränssnittet. Enbart entiteter uppsatta från användargränssnittet är konfigurerbara från användargränssnittet." }, @@ -2267,8 +2267,8 @@ "views": { "confirm_delete": "Radera vyn?", "confirm_delete_existing_cards": "Om du tar bort den här vyn tas även korten bort", - "confirm_delete_existing_cards_text": "Vill du ta bort vyn '{name}'? Vyn innehåller {number}-kort som ska tas bort. Det går inte att ångra den här åtgärden.", - "confirm_delete_text": "Är du säker på att du vill ta bort vyn '{namn}'?" + "confirm_delete_existing_cards_text": "Vill du ta bort vyn ''{name}''? Vyn innehåller {number}-kort som ska tas bort. Det går inte att ångra den här åtgärden.", + "confirm_delete_text": "Är du säker på att du vill ta bort vyn ''{name}''?" }, "warning": { "attribute_not_found": "Attribut {attribute} inte tillgängligt i: {entity}", diff --git a/translations/frontend/tr.json b/translations/frontend/tr.json index d394aca44d..190527c379 100644 --- a/translations/frontend/tr.json +++ b/translations/frontend/tr.json @@ -603,7 +603,7 @@ "password": "Şifre", "text": "Metin" }, - "platform_not_loaded": "{platform} entegrasyonu yüklenmedi. Lütfen 'default_config:' veya '{platform}:' ekleyerek onu yapılandırmanıza dahil edin.", + "platform_not_loaded": "{platform} entegrasyonu yüklenmedi. Lütfen 'default_config:' veya ''{platform}:'' ekleyerek onu yapılandırmanıza dahil edin.", "required_error_msg": "Bu alan gereklidir", "yaml_not_editable": "Bu varlığın ayarları kullanıcı arayüzünden düzenlenemez. Yalnızca kullanıcı arayüzünden tanımlanan varlıklar kullanıcı arayüzünden yapılandırılabilir." }, @@ -2052,8 +2052,8 @@ "views": { "confirm_delete": "Görünüm silinsin mi?", "confirm_delete_existing_cards": "Bu görünüm silindiğinde kartlar da kaldırılacak", - "confirm_delete_existing_cards_text": "'{name}' görünümünüzü silmek istediğinizden emin misiniz? Görünüm, silinecek {number} adet kart içeriyor. Bu eylem geri alınamaz.", - "confirm_delete_text": "'{name}' görünümünüzü silmek istediğinizden emin misiniz?" + "confirm_delete_existing_cards_text": "''{name}'' görünümünüzü silmek istediğinizden emin misiniz? Görünüm, silinecek {number} adet kart içeriyor. Bu eylem geri alınamaz.", + "confirm_delete_text": "''{name}'' görünümünüzü silmek istediğinizden emin misiniz?" }, "warning": { "attribute_not_found": "Nitelik {attribute} {entity} içinde mevcut değil", diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index d328847303..dce48ff3d2 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -617,7 +617,7 @@ "pattern": "用于客户端验证的正则表达式模式", "text": "文本" }, - "platform_not_loaded": "{platform} 组件尚未加载。请在 'default_config:' 或 '{platform}:' 中选择一种方式,将其写入配置中。", + "platform_not_loaded": "{platform} 组件尚未加载。请在 'default_config:' 或 ''{platform}:'' 中选择一种方式,将其写入配置中。", "required_error_msg": "此字段为必填字段", "yaml_not_editable": "无法从 UI 编辑此实体的设置。只有通过 UI 设置的实体可以从 UI 配置。" }, @@ -1374,7 +1374,8 @@ "restart_confirm": "重启 Home Assistant 以完成此集成的删除", "settings_button": "编辑{integration}设置", "system_options": "系统选项", - "system_options_button": "{integration}系统选项" + "system_options_button": "{integration}系统选项", + "unnamed_entry": "未命名条目" }, "config_flow": { "aborted": "中止", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 6add0ed670..655ceb66cb 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -617,7 +617,7 @@ "pattern": "客戶端驗證模式", "text": "文字" }, - "platform_not_loaded": "{platform} 元件未載入。請於設定檔進行添加、新增 'default_config:' 或 '{platform}:'。", + "platform_not_loaded": "{platform} 元件未載入。請於設定檔進行添加、新增 'default_config:' 或 ''{platform}:''。", "required_error_msg": "必填欄位", "yaml_not_editable": "此物件的設定無法藉由 UI 編輯、僅有透過 UI 設定的物件可於 UI 進行設定。" }, @@ -2285,8 +2285,8 @@ "views": { "confirm_delete": "刪除視圖?", "confirm_delete_existing_cards": "刪除此視圖同時將移除面板。", - "confirm_delete_existing_cards_text": "確定要刪除「{name}」視圖?視圖內包含 {number} 張面板也同時將被刪除。此動作將無法回復。", - "confirm_delete_text": "確定要刪除「{name}」視圖?" + "confirm_delete_existing_cards_text": "確定要刪除 \"{name}\" 視圖?視圖內包含 {number} 張面板也同時將被刪除。此動作將無法回復。", + "confirm_delete_text": "確定要刪除 \"{name}\"視圖?" }, "warning": { "attribute_not_found": "無法使用屬性 {attribute} 之物件:{entity}", diff --git a/yarn.lock b/yarn.lock index 2f05c6bbd9..0b41c7c7d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5691,6 +5691,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +comlink@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.3.0.tgz#80b3366baccd87897dab3638ebfcfae28b2f87c7" + integrity sha512-mu4KKKNuW8TvkfpW/H88HBPeILubBS6T94BdD1VWBXNXfiyqVtwUCVNO1GeNOBTsIswzsMjWlycYr+77F5b84g== + command-line-args@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.1.0.tgz#632d3d3df35c8f0cc4365e442a3fd6d63b65621b" @@ -12146,6 +12151,11 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.0" +proxy-polyfill@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/proxy-polyfill/-/proxy-polyfill-0.3.1.tgz#163d5283cf928dd8ddb5c5e88528e4ccd233496f" + integrity sha512-jywE1NIksgIGqZc4uF0QLbXGz2RcHQobsCkAW+8F0nr/6agap+TWksEAKyLnIBafPD88HT9qZR2ec0oomHdjcQ== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -15517,12 +15527,12 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -workerize-loader@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/workerize-loader/-/workerize-loader-1.1.0.tgz#d3a634390dcb685cc1ee292cd1fffeef0a646044" - integrity sha512-cU2jPVE3AzzVxOonBe9lCCO//qwE9s/K4a9njFVRLueznzNDNND5vGHVorGuzK6xvamdDOZ9+g7CPIc7QKzucQ== +worker-plugin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-4.0.3.tgz#7c42e600d5931ad154d3d5f187a32446df64db0f" + integrity sha512-7hFDYWiKcE3yHZvemsoM9lZis/PzurHAEX1ej8PLCu818Rt6QqUAiDdxHPCKZctzmhqzPpcFSgvMCiPbtooqAg== dependencies: - loader-utils "^1.2.3" + loader-utils "^1.1.0" wrap-ansi@^2.0.0: version "2.1.0"