Merge pull request #5436 from home-assistant/dev

20200403.0
This commit is contained in:
Bram Kragten 2020-04-03 18:04:07 +02:00 committed by GitHub
commit a438439ce0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 753 additions and 307 deletions

View File

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20200401.0",
version="20200403.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",

View File

@ -27,7 +27,7 @@ class SearchInput extends LitElement {
protected render(): TemplateResult {
return html`
<style>
.no-underline {
.no-underline:not(.focused) {
--paper-input-container-underline: {
display: none;
height: 0;

View File

@ -86,6 +86,7 @@ export class HaDataTable extends LitElement {
@property({ type: Object }) public columns: DataTableColumnContainer = {};
@property({ type: Array }) public data: DataTableRowData[] = [];
@property({ type: Boolean }) public selectable = false;
@property({ type: Boolean }) public hasFab = false;
@property({ type: Boolean, attribute: "auto-height" })
public autoHeight = false;
@property({ type: String }) public id = "id";
@ -98,6 +99,7 @@ export class HaDataTable extends LitElement {
@property({ type: Array }) private _filteredData: DataTableRowData[] = [];
@query("slot[name='header']") private _header!: HTMLSlotElement;
@query(".mdc-data-table__table") private _table!: HTMLDivElement;
private _checkableRowsCount?: number;
private _checkedRows: string[] = [];
private _sortColumns: {
@ -281,75 +283,84 @@ export class HaDataTable extends LitElement {
: html`
<div class="mdc-data-table__content scroller">
${scroll({
items: this._filteredData,
renderItem: (row: DataTableRowData) => html`
<div
.rowId="${row[this.id]}"
@click=${this._handleRowClick}
class="mdc-data-table__row ${classMap({
"mdc-data-table__row--selected": this._checkedRows.includes(
String(row[this.id])
),
})}"
aria-selected=${ifDefined(
this._checkedRows.includes(String(row[this.id]))
? true
: undefined
)}
.selectable=${row.selectable !== false}
>
${this.selectable
? html`
<div
class="mdc-data-table__cell mdc-data-table__cell--checkbox"
>
<ha-checkbox
class="mdc-data-table__row-checkbox"
@change=${this._handleRowCheckboxClick}
.disabled=${row.selectable === false}
.checked=${this._checkedRows.includes(
String(row[this.id])
)}
items: !this.hasFab
? this._filteredData
: [...this._filteredData, ...[{ empty: true }]],
renderItem: (row: DataTableRowData) => {
if (row.empty) {
return html`
<div class="mdc-data-table__row"></div>
`;
}
return html`
<div
.rowId="${row[this.id]}"
@click=${this._handleRowClick}
class="mdc-data-table__row ${classMap({
"mdc-data-table__row--selected": this._checkedRows.includes(
String(row[this.id])
),
})}"
aria-selected=${ifDefined(
this._checkedRows.includes(String(row[this.id]))
? true
: undefined
)}
.selectable=${row.selectable !== false}
>
${this.selectable
? html`
<div
class="mdc-data-table__cell mdc-data-table__cell--checkbox"
>
</ha-checkbox>
<ha-checkbox
class="mdc-data-table__row-checkbox"
@change=${this._handleRowCheckboxClick}
.disabled=${row.selectable === false}
.checked=${this._checkedRows.includes(
String(row[this.id])
)}
>
</ha-checkbox>
</div>
`
: ""}
${Object.entries(this.columns).map((columnEntry) => {
const [key, column] = columnEntry;
return html`
<div
class="mdc-data-table__cell ${classMap({
"mdc-data-table__cell--numeric": Boolean(
column.type === "numeric"
),
"mdc-data-table__cell--icon": Boolean(
column.type === "icon"
),
"mdc-data-table__cell--icon-button": Boolean(
column.type === "icon-button"
),
grows: Boolean(column.grows),
})}"
style=${column.width
? styleMap({
[column.grows
? "minWidth"
: "width"]: column.width,
maxWidth: column.maxWidth
? column.maxWidth
: "",
})
: ""}
>
${column.template
? column.template(row[key], row)
: row[key]}
</div>
`
: ""}
${Object.entries(this.columns).map((columnEntry) => {
const [key, column] = columnEntry;
return html`
<div
class="mdc-data-table__cell ${classMap({
"mdc-data-table__cell--numeric": Boolean(
column.type === "numeric"
),
"mdc-data-table__cell--icon": Boolean(
column.type === "icon"
),
"mdc-data-table__cell--icon-button": Boolean(
column.type === "icon-button"
),
grows: Boolean(column.grows),
})}"
style=${column.width
? styleMap({
[column.grows
? "minWidth"
: "width"]: column.width,
maxWidth: column.maxWidth
? column.maxWidth
: "",
})
: ""}
>
${column.template
? column.template(row[key], row)
: row[key]}
</div>
`;
})}
</div>
`,
`;
})}
</div>
`;
},
})}
</div>
`}

View File

@ -40,6 +40,11 @@ export class HaTabsSubpageDataTable extends LitElement {
* @type {Boolean}
*/
@property({ type: Boolean }) public selectable = false;
/**
* Do we need to add padding for a fab.
* @type {Boolean}
*/
@property({ type: Boolean }) public hasFab = false;
/**
* Field with a unique id per entry in data.
* @type {String}
@ -95,6 +100,8 @@ export class HaTabsSubpageDataTable extends LitElement {
<slot name="header">
<div class="search-toolbar">
<search-input
.filter=${this.filter}
class="header"
no-label-float
no-underline
@value-changed=${this._handleSearchChange}
@ -109,6 +116,7 @@ export class HaTabsSubpageDataTable extends LitElement {
.data=${this.data}
.filter=${this.filter}
.selectable=${this.selectable}
.hasFab=${this.hasFab}
.id=${this.id}
.noDataText=${this.noDataText}
>
@ -119,6 +127,7 @@ export class HaTabsSubpageDataTable extends LitElement {
<slot name="header">
<div class="table-header">
<search-input
.filter=${this.filter}
no-label-float
no-underline
@value-changed=${this._handleSearchChange}
@ -153,13 +162,16 @@ export class HaTabsSubpageDataTable extends LitElement {
border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
}
.search-toolbar {
margin-left: -24px;
color: var(--secondary-text-color);
}
search-input {
position: relative;
top: 2px;
}
search-input.header {
left: -8px;
top: -7px;
}
`;
}
}

View File

@ -45,7 +45,8 @@ class HassTabsSubpage extends LitElement {
activeTab: PageNavigation | undefined,
showAdvanced: boolean | undefined,
_components,
_language
_language,
_narrow
) => {
const shownTabs = tabs.filter(
(page) =>
@ -101,7 +102,8 @@ class HassTabsSubpage extends LitElement {
this._activeTab,
this.hass.userData?.showAdvanced,
this.hass.config.components,
this.hass.language
this.hass.language,
this.narrow
);
return html`
@ -113,7 +115,7 @@ class HassTabsSubpage extends LitElement {
></ha-paper-icon-button-arrow-prev>
${this.narrow
? html`
<div main-title><slot name="header"></slot></div>
<div class="main-title"><slot name="header"></slot></div>
`
: ""}
${tabs.length > 1 || !this.narrow
@ -190,10 +192,8 @@ class HassTabsSubpage extends LitElement {
}
#tabbar:not(.bottom-bar) {
margin: auto;
left: 50%;
position: absolute;
transform: translate(-50%, 0);
flex: 1;
justify-content: center;
}
.tab {
@ -228,14 +228,17 @@ class HassTabsSubpage extends LitElement {
ha-menu-button,
ha-paper-icon-button-arrow-prev,
::slotted([slot="toolbar-icon"]) {
flex-shrink: 0;
pointer-events: auto;
color: var(--sidebar-icon-color);
}
[main-title] {
margin: 0 0 0 24px;
.main-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
max-height: 40px;
line-height: 20px;
flex-grow: 1;
}
.content {
@ -247,11 +250,6 @@ class HassTabsSubpage extends LitElement {
-webkit-overflow-scrolling: touch;
}
#toolbar-icon {
position: absolute;
right: 16px;
}
:host([narrow]) .content {
height: calc(100% - 128px);
}

View File

@ -82,9 +82,10 @@ class NotificationManager extends LitElement {
static get styles(): CSSResult {
return css`
:host {
ha-toast {
display: flex;
align-items: center;
justify-content: space-between;
}
mwc-button {
color: var(--primary-color);

View File

@ -108,6 +108,7 @@ export class HaConfigAreasDashboard extends LitElement {
"ui.panel.config.areas.picker.no_areas"
)}
id="area_id"
hasFab
>
<paper-icon-button
slot="toolbar-icon"

View File

@ -160,6 +160,7 @@ class HaAutomationPicker extends LitElement {
.noDataText=${this.hass.localize(
"ui.panel.config.automation.picker.no_automations"
)}
hasFab
>
</hass-tabs-subpage-data-table>
<ha-fab

View File

@ -108,7 +108,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
type: "icon",
sortable: true,
filterable: true,
width: "55px",
width: "68px",
template: (_status, entity: any) =>
entity.unavailable || entity.disabled_by || entity.readonly
? html`
@ -169,7 +169,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
),
sortable: true,
filterable: true,
width: "20%",
width: "25%",
};
columns.platform = {
title: this.hass.localize(

View File

@ -152,8 +152,6 @@ class HaPanelConfig extends HassRouterPage {
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
cacheAll: true,
preloadAll: true,
routes: {
areas: {
tag: "ha-config-areas",

View File

@ -148,6 +148,7 @@ export class HaConfigHelpers extends LitElement {
.columns=${this._columns(this.narrow, this.hass.language)}
.data=${this._getItems(this._stateItems)}
@row-click=${this._openEditDialog}
hasFab
>
</hass-tabs-subpage-data-table>
<ha-fab

View File

@ -104,7 +104,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
),
sortable: true,
filterable: true,
width: "15%",
width: "20%",
template: (mode) =>
html`
${this.hass.localize(
@ -143,7 +143,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
"ui.panel.config.lovelace.dashboards.picker.headers.sidebar"
),
type: "icon",
width: "100px",
width: "121px",
template: (sidebar) =>
sidebar
? html`
@ -229,6 +229,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
.data=${this._getItems(this._dashboards)}
@row-click=${this._editDashboard}
id="url_path"
hasFab
>
</hass-tabs-subpage-data-table>
<ha-fab

View File

@ -97,6 +97,7 @@ export class HaConfigLovelaceRescources extends LitElement {
"ui.panel.config.lovelace.resources.picker.no_resources"
)}
@row-click=${this._editResource}
hasFab
>
</hass-tabs-subpage-data-table>
<ha-fab

View File

@ -131,6 +131,7 @@ class HaSceneDashboard extends LitElement {
.noDataText=${this.hass.localize(
"ui.panel.config.scene.picker.no_scenes"
)}
hasFab
>
<paper-icon-button
slot="toolbar-icon"

View File

@ -126,6 +126,7 @@ class HaScriptPicker extends LitElement {
.noDataText=${this.hass.localize(
"ui.panel.config.script.picker.no_scripts"
)}
hasFab
>
<paper-icon-button
slot="toolbar-icon"

View File

@ -94,6 +94,7 @@ export class HaConfigUsers extends LitElement {
.columns=${this._columns(this.hass.language)}
.data=${this._users}
@row-click=${this._editUser}
hasFab
>
</hass-tabs-subpage-data-table>
<ha-fab

View File

@ -19,7 +19,7 @@ import {
addGroup,
ZHAGroup,
} from "../../../data/zha";
import { ZHADevicesDataTable } from "./zha-devices-data-table";
import "./zha-devices-data-table";
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
import { navigate } from "../../../common/navigate";
import { PolymerChangedEvent } from "../../../polymer-types";
@ -27,6 +27,8 @@ import "@polymer/paper-spinner/paper-spinner";
import "@material/mwc-button";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { HASSDomEvent } from "../../../common/dom/fire_event";
// tslint:disable-next-line: no-duplicate-imports
import { ZHADevicesDataTable } from "./zha-devices-data-table";
@customElement("zha-add-group-page")
export class ZHAAddGroupPage extends LitElement {

View File

@ -13,8 +13,6 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
cacheAll: true,
preloadAll: true,
showLoading: true,
routes: {
dashboard: {

View File

@ -27,13 +27,15 @@ import {
Cluster,
fetchClustersForZhaNode,
} from "../../../data/zha";
import { ZHAClustersDataTable } from "./zha-clusters-data-table";
import "./zha-clusters-data-table";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { ItemSelectedEvent } from "./types";
import "@polymer/paper-item/paper-item";
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
import { HASSDomEvent } from "../../../common/dom/fire_event";
// tslint:disable-next-line: no-duplicate-imports
import { ZHAClustersDataTable } from "./zha-clusters-data-table";
@customElement("zha-group-binding-control")
export class ZHAGroupBindingControl extends LitElement {
@ -48,7 +50,7 @@ export class ZHAGroupBindingControl extends LitElement {
@property() private _clusters: Cluster[] = [];
private _groupToBind?: ZHAGroup;
private _clustersToBind?: Cluster[];
@query("zha-devices-data-table")
@query("zha-clusters-data-table")
private _zhaClustersDataTable!: ZHAClustersDataTable;
protected updated(changedProperties: PropertyValues): void {

View File

@ -5,6 +5,7 @@ import { createCardElement } from "../create-element/create-card-element";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { computeCardSize } from "../common/compute-card-size";
import { ConditionalCardConfig } from "./types";
import { LovelaceCardConfig } from "../../../data/lovelace";
@customElement("hui-conditional-card")
class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard {
@ -19,7 +20,8 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard {
return {
type: "conditional",
conditions: [],
card: { type: "" },
// @ts-ignore
card: {},
};
}
@ -30,17 +32,35 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard {
throw new Error("No card configured.");
}
if (this._element && this._element.parentElement) {
this.removeChild(this._element);
}
this._element = createCardElement(config.card) as LovelaceCard;
this.appendChild(this._element);
this._element = this._createCardElement(config.card);
}
public getCardSize(): number {
return computeCardSize(this._element as LovelaceCard);
}
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = createCardElement(cardConfig) as LovelaceCard;
if (this.hass) {
element.hass = this.hass;
}
element.addEventListener(
"ll-rebuild",
(ev) => {
ev.stopPropagation();
this._rebuildCard(cardConfig);
},
{ once: true }
);
return element;
}
private _rebuildCard(config: LovelaceCardConfig): void {
this._element = this._createCardElement(config);
if (this.lastChild) {
this.replaceChild(this._element, this.lastChild);
}
}
}
declare global {

View File

@ -187,6 +187,9 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
return css`
ha-card {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.card-header {
display: flex;
@ -199,6 +202,10 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
text-overflow: ellipsis;
}
#states {
flex: 1;
}
#states > * {
margin: 8px 0;
}

View File

@ -189,6 +189,12 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
static get styles(): CSSResult {
return css`
ha-card {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
ha-card > div {
cursor: pointer;
}

View File

@ -9,6 +9,7 @@ import { evaluateFilter } from "../common/evaluate-filter";
class EntityFilterCard extends HTMLElement implements LovelaceCard {
public isPanel?: boolean;
private _editMode = false;
private _element?: LovelaceCard;
private _config?: EntityFilterCardConfig;
private _configEntities?: EntityFilterEntityConfig[];
@ -51,6 +52,14 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard {
}
}
set editMode(editMode: boolean) {
this._editMode = editMode;
if (!this._element) {
return;
}
this._element.editMode = editMode;
}
set hass(hass: HomeAssistant) {
if (!hass || !this._config) {
return;
@ -114,6 +123,7 @@ class EntityFilterCard extends HTMLElement implements LovelaceCard {
}
element.isPanel = this.isPanel;
element.editMode = this._editMode;
element.hass = hass;
}

View File

@ -125,16 +125,17 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
<div class="content">
<div id="controls">
<div id="slider">
${supportsFeature(stateObj, SUPPORT_BRIGHTNESS)
? html`
<round-slider
min="0"
.value=${brightness}
@value-changing=${this._dragEvent}
@value-changed=${this._setBrightness}
></round-slider>
`
: ""}
<round-slider
min="0"
.value=${brightness}
@value-changing=${this._dragEvent}
@value-changed=${this._setBrightness}
style=${styleMap({
visibility: supportsFeature(stateObj, SUPPORT_BRIGHTNESS)
? "visible"
: "hidden",
})}
></round-slider>
<paper-icon-button
class="light-button ${classMap({
"slider-center": supportsFeature(
@ -322,6 +323,13 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
color: var(--paper-item-icon-color, #44739e);
width: 60%;
height: auto;
position: absolute;
max-width: calc(100% - 40px);
box-sizing: border-box;
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.light-button.state-on {
@ -332,16 +340,6 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
color: var(--state-icon-unavailable-color);
}
.slider-center {
position: absolute;
max-width: calc(100% - 40px);
box-sizing: border-box;
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#info {
text-align: center;
margin-top: -56px;

View File

@ -79,6 +79,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
@property()
private _history?: HassEntity[][];
private _date?: Date;
private _loaded = false;
@property()
private _config?: MapCardConfig;
@ -282,6 +283,10 @@ class HuiMapCard extends LitElement implements LovelaceCard {
}
private async loadMap(): Promise<void> {
if (this._loaded) {
return;
}
this._loaded = true;
[this._leafletMap, this.Leaflet] = await setupLeafletMap(
this._mapEl,
this._config !== undefined ? this._config.dark_mode === true : false

View File

@ -27,6 +27,7 @@ import {
import { ShoppingListCardConfig, SensorCardConfig } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { classMap } from "lit-html/directives/class-map";
@customElement("hui-shopping-list-card")
class HuiShoppingListCard extends LitElement implements LovelaceCard {
@ -108,7 +109,12 @@ class HuiShoppingListCard extends LitElement implements LovelaceCard {
}
return html`
<ha-card .header="${this._config.title}">
<ha-card
.header=${this._config.title}
class=${classMap({
"has-header": "title" in this._config,
})}
>
<div class="addRow">
<ha-icon
class="addButton"
@ -209,6 +215,15 @@ class HuiShoppingListCard extends LitElement implements LovelaceCard {
static get styles(): CSSResult {
return css`
ha-card {
padding-bottom: 16px;
padding-top: 16px;
}
.has-header {
padding-top: 0;
}
.editRow,
.addRow {
display: flex;

View File

@ -5,6 +5,7 @@ import {
CSSResult,
css,
property,
PropertyValues,
} from "lit-element";
import { createCardElement } from "../create-element/create-card-element";
@ -25,21 +26,10 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard {
return { cards: [] };
}
@property() public hass?: HomeAssistant;
@property() public editMode?: boolean;
@property() protected _cards?: LovelaceCard[];
@property() private _config?: StackCardConfig;
private _hass?: HomeAssistant;
set hass(hass: HomeAssistant) {
this._hass = hass;
if (!this._cards) {
return;
}
for (const element of this._cards) {
element.hass = this._hass;
}
}
public getCardSize(): number {
return 1;
@ -56,6 +46,21 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard {
});
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (
!this._cards ||
(!changedProps.has("hass") && !changedProps.has("editMode"))
) {
return;
}
for (const element of this._cards) {
element.hass = this.hass;
element.editMode = this.editMode;
}
}
protected render(): TemplateResult {
if (!this._config || !this._cards) {
return html``;
@ -87,8 +92,8 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard {
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = createCardElement(cardConfig) as LovelaceCard;
if (this._hass) {
element.hass = this._hass;
if (this.hass) {
element.hass = this.hass;
}
element.addEventListener(
"ll-rebuild",

View File

@ -239,24 +239,26 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
tabindex="0"
></paper-icon-button>
<div id="controls">
<div id="slider">
${slider}
<div id="slider-center">
<div id="temperature">
${currentTemperature} ${setValues}
<div class="content">
<div id="controls">
<div id="slider">
${slider}
<div id="slider-center">
<div id="temperature">
${currentTemperature} ${setValues}
</div>
</div>
</div>
</div>
</div>
<div id="info">
<div id="modes">
${(stateObj.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes)
.map((modeItem) => this._renderIcon(modeItem, mode))}
<div id="info">
<div id="modes">
${(stateObj.attributes.hvac_modes || [])
.concat()
.sort(compareClimateHvacModes)
.map((modeItem) => this._renderIcon(modeItem, mode))}
</div>
${name}
</div>
${name}
</div>
</ha-card>
`;
@ -423,6 +425,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
}
ha-card {
height: 100%;
position: relative;
overflow: hidden;
--name-font-size: 1.2rem;
@ -481,6 +484,13 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
z-index: 25;
}
.content {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
#controls {
display: flex;
justify-content: center;

View File

@ -36,7 +36,7 @@ export class HuiCardOptions extends LitElement {
<ha-card>
<div class="options">
<div class="primary-actions">
<mwc-button @click="${this._editCard}"
<mwc-button @click=${this._editCard}
>${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.edit"
)}</mwc-button
@ -47,17 +47,17 @@ export class HuiCardOptions extends LitElement {
title="Move card down"
class="move-arrow"
icon="hass:arrow-down"
@click="${this._cardDown}"
?disabled="${this.lovelace!.config.views[this.path![0]].cards!
@click=${this._cardDown}
?disabled=${this.lovelace!.config.views[this.path![0]].cards!
.length ===
this.path![1] + 1}"
this.path![1] + 1}
></paper-icon-button>
<paper-icon-button
title="Move card up"
class="move-arrow"
icon="hass:arrow-up"
@click="${this._cardUp}"
?disabled="${this.path![1] === 0}"
@click=${this._cardUp}
?disabled=${this.path![1] === 0}
></paper-icon-button>
<paper-menu-button
horizontal-align="right"
@ -73,7 +73,7 @@ export class HuiCardOptions extends LitElement {
)}
></paper-icon-button>
<paper-listbox slot="dropdown-content">
<paper-item @tap="${this._moveCard}">
<paper-item @tap=${this._moveCard}>
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.move"
)}</paper-item
@ -83,7 +83,7 @@ export class HuiCardOptions extends LitElement {
"ui.panel.lovelace.editor.edit_card.duplicate"
)}</paper-item
>
<paper-item .class="delete-item" @tap="${this._deleteCard}">
<paper-item class="delete-item" @tap=${this._deleteCard}>
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.delete"
)}</paper-item
@ -99,6 +99,7 @@ export class HuiCardOptions extends LitElement {
static get styles(): CSSResult {
return css`
:host(:hover) {
overflow: hidden;
outline: 2px solid var(--primary-color);
}
@ -140,6 +141,10 @@ export class HuiCardOptions extends LitElement {
padding: 0;
}
paper-listbox {
padding: 0;
}
paper-item.header {
color: var(--primary-text-color);
text-transform: uppercase;
@ -149,6 +154,7 @@ export class HuiCardOptions extends LitElement {
paper-item {
cursor: pointer;
white-space: nowrap;
}
paper-item.delete-item {

View File

@ -12,6 +12,7 @@ import { ConditionalCardConfig } from "../cards/types";
@customElement("hui-conditional-base")
export class HuiConditionalBase extends UpdatingElement {
@property() public hass?: HomeAssistant;
@property() public editMode?: boolean;
@property() protected _config?: ConditionalCardConfig | ConditionalRowConfig;
protected _element?: LovelaceCard | LovelaceRow;
@ -30,8 +31,11 @@ export class HuiConditionalBase extends UpdatingElement {
throw new Error("Conditions are invalid.");
}
if (this.lastChild) {
this.removeChild(this.lastChild);
}
this._config = config;
this.style.display = "none";
}
protected update(): void {
@ -39,13 +43,19 @@ export class HuiConditionalBase extends UpdatingElement {
return;
}
const visible = checkConditionsMet(this._config.conditions, this.hass);
this._element.editMode = this.editMode;
const visible =
this.editMode || checkConditionsMet(this._config.conditions, this.hass);
this.style.setProperty("display", visible ? "" : "none");
if (visible) {
this._element.hass = this.hass;
if (!this._element.parentElement) {
this.appendChild(this._element);
}
}
this.style.setProperty("display", visible ? "" : "none");
}
}

View File

@ -25,15 +25,18 @@ import { EntityConfig } from "../../entity-rows/types";
import { getCardElementClass } from "../../create-element/create-card-element";
import { GUIModeChangedEvent } from "../types";
export interface ConfigChangedEvent {
config: LovelaceCardConfig;
error?: string;
guiModeAvailable?: boolean;
}
declare global {
interface HASSDomEvents {
"entities-changed": {
entities: EntityConfig[];
};
"config-changed": {
config: LovelaceCardConfig;
error?: string;
};
"config-changed": ConfigChangedEvent;
"GUImode-changed": GUIModeChangedEvent;
}
}
@ -75,6 +78,7 @@ export class HuiCardEditor extends LitElement {
fireEvent(this, "config-changed", {
config: this.value!,
error: this._error,
guiModeAvailable: !(this.hasWarning || this.hasError),
});
}
@ -101,7 +105,10 @@ export class HuiCardEditor extends LitElement {
public set GUImode(guiMode: boolean) {
this._GUImode = guiMode;
fireEvent(this as HTMLElement, "GUImode-changed", { guiMode });
fireEvent(this as HTMLElement, "GUImode-changed", {
guiMode,
guiModeAvailable: !(this.hasWarning || this.hasError),
});
}
private get _yamlEditor(): HaCodeEditor {
@ -174,6 +181,13 @@ export class HuiCardEditor extends LitElement {
}
fireEvent(this as HTMLElement, "iron-resize");
}
if (this._configElement && changedProperties.has("hass")) {
this._configElement.hass = this.hass;
}
if (this._configElement && changedProperties.has("lovelace")) {
this._configElement.lovelace = this.lovelace;
}
}
private _refreshYamlEditor(focus = false) {
@ -232,6 +246,13 @@ export class HuiCardEditor extends LitElement {
this._configElement = configElement;
this._configElType = cardType;
// Perform final setup
this._configElement.hass = this.hass;
this._configElement.lovelace = this.lovelace;
this._configElement.addEventListener("config-changed", (ev) =>
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
);
}
// Setup GUI editor and check that it can handle the current config
@ -240,16 +261,6 @@ export class HuiCardEditor extends LitElement {
} catch (err) {
throw Error(`WARNING: ${err.message}`);
}
// Perform final setup
this._configElement!.hass = this.hass;
this._configElement!.lovelace = this.lovelace;
this._configElement!.addEventListener("config-changed", (ev) =>
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
);
this.GUImode = true;
return;
} catch (err) {
if (err.message.startsWith("WARNING:")) {
this._warning = err.message.substr(8);

View File

@ -200,6 +200,7 @@ export class HuiCardPicker extends LitElement {
background: var(--primary-background-color, #fafafa);
cursor: pointer;
box-sizing: border-box;
position: relative;
}
.card-header {
@ -242,6 +243,13 @@ export class HuiCardPicker extends LitElement {
align-items: center;
justify-content: center;
}
.overlay {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
`,
];
}
@ -297,7 +305,12 @@ export class HuiCardPicker extends LitElement {
}
return html`
<div class="card" @click="${this._cardPicked}" .config="${cardConfig}">
<div class="card">
<div
class="overlay"
@click=${this._cardPicked}
.config=${cardConfig}
></div>
<div
class="preview ${classMap({
description: !element || element.tagName === "HUI-ERROR-CARD",

View File

@ -19,7 +19,7 @@ import {
} from "../../../../data/lovelace";
import "./hui-card-editor";
// tslint:disable-next-line
import { HuiCardEditor } from "./hui-card-editor";
import { HuiCardEditor, ConfigChangedEvent } from "./hui-card-editor";
import "./hui-card-preview";
import "./hui-card-picker";
import { EditCardDialogParams } from "./show-edit-card-dialog";
@ -52,12 +52,15 @@ export class HuiDialogEditCard extends LitElement {
@property() private _saving: boolean = false;
@property() private _error?: string;
@property() private _guiModeAvailable? = true;
@query("hui-card-editor") private _cardEditorEl?: HuiCardEditor;
@property() private _GUImode?: boolean;
@property() private _GUImode = true;
public async showDialog(params: EditCardDialogParams): Promise<void> {
this._params = params;
this._GUImode = true;
this._guiModeAvailable = true;
const [view, card] = params.path;
this._viewConfig = params.lovelaceConfig.views[view];
this._cardConfig =
@ -139,8 +142,7 @@ export class HuiDialogEditCard extends LitElement {
? html`
<mwc-button
@click=${this._toggleMode}
?disabled=${this._cardEditorEl?.hasWarning ||
this._cardEditorEl?.hasError}
.disabled=${!this._guiModeAvailable}
class="gui-mode-button"
>
${this.hass!.localize(
@ -288,9 +290,10 @@ export class HuiDialogEditCard extends LitElement {
this._error = ev.detail.error;
}
private _handleConfigChanged(ev) {
private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
this._cardConfig = deepFreeze(ev.detail.config);
this._error = ev.detail.error;
this._guiModeAvailable = ev.detail.guiModeAvailable;
}
private _handleKeyUp(ev: KeyboardEvent) {
@ -302,6 +305,7 @@ export class HuiDialogEditCard extends LitElement {
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
ev.stopPropagation();
this._GUImode = ev.detail.guiMode;
this._guiModeAvailable = ev.detail.guiModeAvailable;
}
private _toggleMode(): void {

View File

@ -60,7 +60,7 @@ export class HuiAlarmPanelCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -88,7 +88,7 @@ export class HuiButtonCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -6,18 +6,24 @@ import {
property,
CSSResult,
css,
query,
} from "lit-element";
import "@polymer/paper-tabs";
import { struct } from "../../common/structs/struct";
import { HomeAssistant } from "../../../../types";
import { LovelaceCardEditor } from "../../types";
import { StackCardConfig } from "../../cards/types";
import { fireEvent } from "../../../../common/dom/fire_event";
import { ConditionalCardConfig } from "../../cards/types";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { LovelaceConfig } from "../../../../data/lovelace";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/ha-switch";
import {
HuiCardEditor,
ConfigChangedEvent,
} from "../card-editor/hui-card-editor";
import { GUIModeChangedEvent } from "../types";
const conditionStruct = struct({
entity: "string",
@ -35,10 +41,13 @@ export class HuiConditionalCardEditor extends LitElement
implements LovelaceCardEditor {
@property() public hass?: HomeAssistant;
@property() public lovelace?: LovelaceConfig;
@property() private _config?: StackCardConfig;
@property() private _config?: ConditionalCardConfig;
@property() private _GUImode = true;
@property() private _guiModeAvailable? = true;
@property() private _cardTab: boolean = false;
@query("hui-card-editor") private _cardEditorEl?: HuiCardEditor;
public setConfig(config: StackCardConfig): void {
public setConfig(config: ConditionalCardConfig): void {
this._config = cardConfigStruct(config);
}
@ -66,9 +75,20 @@ export class HuiConditionalCardEditor extends LitElement
${this._cardTab
? html`
<div class="card">
${this._config.card.type
${this._config.card.type !== undefined
? html`
<div class="card-options">
<mwc-button
@click=${this._toggleMode}
.disabled=${!this._guiModeAvailable}
class="gui-mode-button"
>
${this.hass!.localize(
!this._cardEditorEl || this._GUImode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
</mwc-button>
<mwc-button @click=${this._handleReplaceCard}
>${this.hass!.localize(
"ui.panel.lovelace.editor.card.conditional.change_type"
@ -80,13 +100,14 @@ export class HuiConditionalCardEditor extends LitElement
.value=${this._config.card}
.lovelace=${this.lovelace}
@config-changed=${this._handleCardChanged}
@GUImode-changed=${this._handleGUIModeChanged}
></hui-card-editor>
`
: html`
<hui-card-picker
.hass=${this.hass}
.lovelace=${this.lovelace}
@config-changed=${this._handleCardChanged}
@config-changed=${this._handleCardPicked}
></hui-card-picker>
`}
</div>
@ -162,18 +183,49 @@ export class HuiConditionalCardEditor extends LitElement
this._cardTab = parseInt((ev.target! as any).selected!, 10) === 1;
}
private _handleCardChanged(ev: CustomEvent): void {
private _toggleMode(): void {
this._cardEditorEl?.toggleMode();
}
private _setMode(value: boolean): void {
this._GUImode = value;
if (this._cardEditorEl) {
this._cardEditorEl!.GUImode = value;
}
}
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
ev.stopPropagation();
this._GUImode = ev.detail.guiMode;
this._guiModeAvailable = ev.detail.guiModeAvailable;
}
private _handleCardPicked(ev: CustomEvent): void {
ev.stopPropagation();
if (!this._config) {
return;
}
this._setMode(true);
this._guiModeAvailable = true;
this._config.card = ev.detail.config;
fireEvent(this, "config-changed", { config: this._config });
}
private _handleCardChanged(ev: HASSDomEvent<ConfigChangedEvent>): void {
ev.stopPropagation();
if (!this._config) {
return;
}
this._config.card = ev.detail.config;
this._guiModeAvailable = ev.detail.guiModeAvailable;
fireEvent(this, "config-changed", { config: this._config });
}
private _handleReplaceCard(): void {
if (!this._config) {
return;
}
// @ts-ignore
this._config.card = {};
fireEvent(this, "config-changed", { config: this._config });
}
@ -267,6 +319,9 @@ export class HuiConditionalCardEditor extends LitElement
justify-content: flex-end;
width: 100%;
}
.gui-mode-button {
margin-right: auto;
}
`;
}
}

View File

@ -67,7 +67,7 @@ export class HuiEntitiesCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -69,7 +69,7 @@ export class HuiEntityCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -73,7 +73,7 @@ export class HuiGaugeCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -80,7 +80,7 @@ export class HuiGlanceCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -67,7 +67,7 @@ export class HuiHistoryGraphCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -47,7 +47,7 @@ export class HuiIframeCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -72,7 +72,7 @@ export class HuiLightCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -79,7 +79,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -50,7 +50,7 @@ export class HuiMarkdownCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -37,7 +37,7 @@ export class HuiMediaControlCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -60,7 +60,7 @@ export class HuiPictureCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -100,7 +100,7 @@ export class HuiPictureEntityCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -111,7 +111,7 @@ export class HuiPictureGlanceCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -51,7 +51,7 @@ export class HuiPlantStatusCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -78,7 +78,7 @@ export class HuiSensorCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -46,7 +46,7 @@ export class HuiShoppingListEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -16,7 +16,10 @@ import { LovelaceCardEditor } from "../../types";
import { StackCardConfig } from "../../cards/types";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { LovelaceConfig } from "../../../../data/lovelace";
import { HuiCardEditor } from "../card-editor/hui-card-editor";
import {
HuiCardEditor,
ConfigChangedEvent,
} from "../card-editor/hui-card-editor";
import { GUIModeChangedEvent } from "../types";
const cardConfigStruct = struct({
@ -32,7 +35,8 @@ export class HuiStackCardEditor extends LitElement
@property() public lovelace?: LovelaceConfig;
@property() private _config?: StackCardConfig;
@property() private _selectedCard: number = 0;
@property() private _GUImode?: boolean;
@property() private _GUImode = true;
@property() private _guiModeAvailable? = true;
@query("hui-card-editor") private _cardEditorEl?: HuiCardEditor;
public setConfig(config: StackCardConfig): void {
@ -52,7 +56,7 @@ export class HuiStackCardEditor extends LitElement
<paper-tabs
.selected=${selected}
scrollable
@iron-select=${this._handleSelectedCard}
@iron-activate=${this._handleSelectedCard}
>
${this._config.cards.map((_card, i) => {
return html`
@ -65,7 +69,7 @@ export class HuiStackCardEditor extends LitElement
<paper-tabs
id="add-card"
.selected=${selected === numcards ? "0" : undefined}
@iron-select=${this._handleSelectedCard}
@iron-activate=${this._handleSelectedCard}
>
<paper-tab>
<ha-icon icon="hass:plus"></ha-icon>
@ -80,8 +84,7 @@ export class HuiStackCardEditor extends LitElement
<div id="card-options">
<mwc-button
@click=${this._toggleMode}
?disabled=${this._cardEditorEl?.hasWarning ||
this._cardEditorEl?.hasError}
.disabled=${!this._guiModeAvailable}
class="gui-mode-button"
>
${this.hass!.localize(
@ -94,7 +97,7 @@ export class HuiStackCardEditor extends LitElement
id="move-before"
title="Move card before"
icon="hass:arrow-left"
?disabled=${selected === 0}
.disabled=${selected === 0}
@click=${this._handleMove}
></paper-icon-button>
@ -102,7 +105,7 @@ export class HuiStackCardEditor extends LitElement
id="move-after"
title="Move card after"
icon="hass:arrow-right"
?disabled=${selected === numcards - 1}
.disabled=${selected === numcards - 1}
@click=${this._handleMove}
></paper-icon-button>
@ -134,18 +137,22 @@ export class HuiStackCardEditor extends LitElement
}
private _handleSelectedCard(ev) {
this._selectedCard =
ev.target.id === "add-card"
? this._config!.cards.length
: parseInt(ev.target.selected, 10);
if (ev.target.id === "add-card") {
this._selectedCard = this._config!.cards.length;
return;
}
this._setMode(true);
this._guiModeAvailable = true;
this._selectedCard = parseInt(ev.detail.selected, 10);
}
private _handleConfigChanged(ev) {
private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
ev.stopPropagation();
if (!this._config) {
return;
}
this._config.cards[this._selectedCard] = ev.detail.config;
this._guiModeAvailable = ev.detail.guiModeAvailable;
fireEvent(this, "config-changed", { config: this._config });
}
@ -183,12 +190,20 @@ export class HuiStackCardEditor extends LitElement
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
ev.stopPropagation();
this._GUImode = ev.detail.guiMode;
this._guiModeAvailable = ev.detail.guiModeAvailable;
}
private _toggleMode(): void {
this._cardEditorEl?.toggleMode();
}
private _setMode(value: boolean): void {
this._GUImode = value;
if (this._cardEditorEl) {
this._cardEditorEl!.GUImode = value;
}
}
static get styles(): CSSResult {
return css`
.toolbar {

View File

@ -50,7 +50,7 @@ export class HuiThermostatCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -49,7 +49,7 @@ export class HuiWeatherForecastCardEditor extends LitElement
}
protected render(): TemplateResult {
if (!this.hass) {
if (!this.hass || !this._config) {
return html``;
}

View File

@ -119,6 +119,40 @@ export const deleteCard = (
};
};
export const insertCard = (
config: LovelaceConfig,
path: [number, number],
cardConfig: LovelaceCardConfig
) => {
const [viewIndex, cardIndex] = path;
const views: LovelaceViewConfig[] = [];
config.views.forEach((viewConf, index) => {
if (index !== viewIndex) {
views.push(config.views[index]);
return;
}
const cards = viewConf.cards
? [
...viewConf.cards.slice(0, cardIndex),
cardConfig,
...viewConf.cards.slice(cardIndex),
]
: [cardConfig];
views.push({
...viewConf,
cards,
});
});
return {
...config,
views,
};
};
export const swapCard = (
config: LovelaceConfig,
path1: [number, number],

View File

@ -1,5 +1,5 @@
import { Lovelace } from "../types";
import { deleteCard } from "./config-util";
import { deleteCard, insertCard } from "./config-util";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { HomeAssistant } from "../../../types";
import { showDeleteCardDialog } from "./card-editor/show-delete-card-dialog";
@ -16,8 +16,12 @@ export async function confDeleteCard(
cardConfig,
deleteCard: async () => {
try {
await lovelace.saveConfig(deleteCard(lovelace.config, path));
showDeleteSuccessToast(element, hass!);
const newLovelace = deleteCard(lovelace.config, path);
await lovelace.saveConfig(newLovelace);
const action = async () => {
await lovelace.saveConfig(insertCard(newLovelace, path, cardConfig));
};
showDeleteSuccessToast(element, hass!, action);
} catch (err) {
showAlertDialog(element, {
text: `Deleting failed: ${err.message}`,

View File

@ -16,6 +16,7 @@ export interface YamlChangedEvent extends Event {
export interface GUIModeChangedEvent {
guiMode: boolean;
guiModeAvailable: boolean;
}
export interface ViewEditEvent extends Event {

View File

@ -391,7 +391,7 @@ export class HuiEditView extends LitElement {
.preview-badges {
display: flex;
justify-content: center;
margin: 8px 16px;
margin: 12px 16px;
flex-wrap: wrap;
}
`,

View File

@ -84,6 +84,18 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
const stateObj = this.hass.states[this._config.entity];
if (!stateObj) {
return html`
<hui-warning
>${this.hass.localize(
"ui.panel.lovelace.warning.entity_not_found",
"entity",
this._config.entity
)}</hui-warning
>
`;
}
const buttons = html`
${!this._narrow && supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)
? html`
@ -112,18 +124,6 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
: ""}
`;
if (!stateObj) {
return html`
<hui-warning
>${this.hass.localize(
"ui.panel.lovelace.warning.entity_not_found",
"entity",
this._config.entity
)}</hui-warning
>
`;
}
return html`
<hui-generic-entity-row
.hass=${this.hass}

View File

@ -69,6 +69,7 @@ export type LovelaceRowConfig =
export interface LovelaceRow extends HTMLElement {
hass?: HomeAssistant;
editMode?: boolean;
setConfig(config: LovelaceRowConfig);
}

View File

@ -13,12 +13,7 @@ class HuiConditionalRow extends HuiConditionalBase implements LovelaceRow {
throw new Error("No row configured.");
}
if (this._element && this._element.parentElement) {
this.removeChild(this._element);
}
this._element = createRowElement(config.row) as LovelaceRow;
this.appendChild(this._element);
}
}

View File

@ -524,6 +524,7 @@
"cancel": "Cancel",
"delete": "Delete",
"close": "Close",
"undo": "Undo",
"save": "Save",
"yes": "Yes",
"no": "No",

View File

@ -1,7 +1,19 @@
import { showToast } from "./toast";
import { HomeAssistant } from "../types";
import { ShowToastParams } from "../managers/notification-manager";
export const showDeleteSuccessToast = (el: HTMLElement, hass: HomeAssistant) =>
showToast(el, {
export const showDeleteSuccessToast = (
el: HTMLElement,
hass: HomeAssistant,
action?: () => void
) => {
const toastParams: ShowToastParams = {
message: hass!.localize("ui.common.successfully_deleted"),
});
};
if (action) {
toastParams.action = { action, text: hass!.localize("ui.common.undo") };
}
showToast(el, toastParams);
};

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Pressió atmosfèrica",
"humidity": "Humitat",
"precipitation": "Precipitació",
"temperature": "Temperatura",
"visibility": "Visibilitat",
"wind_speed": "Velocitat del vent"
@ -2174,6 +2175,7 @@
"attribute": "Atribut",
"camera_image": "Entitat de càmera",
"camera_view": "Visualització de càmera",
"double_tap_action": "Acció en tocar dues vegades",
"entities": "Entitats",
"entity": "Entitat",
"hold_action": "Acció en mantenir",
@ -2224,6 +2226,7 @@
"default_zoom": "Zoom predeterminat",
"description": "La targeta mapa et permet mostrar diferents entitats sobre un mapa.",
"geo_location_sources": "Fonts de geolocalització",
"hours_to_show": "Hores a mostrar",
"name": "Mapa",
"source": "Font"
},
@ -2280,6 +2283,10 @@
"name": "Previsió meteorològica"
}
},
"cardpicker": {
"custom_card": "Personalitzada",
"no_description": "No hi ha cap descripció disponible."
},
"edit_card": {
"add": "Afegir targeta",
"delete": "Elimina",

View File

@ -40,7 +40,7 @@
"lovelace": "Lovelace",
"mailbox": "Postkasse",
"media_player": "Medieafspiller",
"notify": "Meddelelser",
"notify": "Notifikationer",
"person": "Person",
"plant": "Plante",
"proximity": "Nærhed",
@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Lufttryk",
"humidity": "Luftfugtighed",
"precipitation": "Nedbør",
"temperature": "Temperatur",
"visibility": "Sigtbarhed",
"wind_speed": "Vindhastighed"
@ -531,7 +532,9 @@
"wnw": "VNV",
"wsw": "VSV"
},
"forecast": "Vejrudsigt"
"forecast": "Vejrudsigt",
"high": "Maks.",
"low": "Min."
}
},
"common": {
@ -687,7 +690,7 @@
"not_editable_text": "Denne entitet kan ikke ændres fra brugerfladen, fordi det er defineret i configuration.yaml.",
"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 brugergrænsefladen, der kan konfigureres herfra."
"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."
},
"more_info_control": {
"dismiss": "Afvis dialog",
@ -792,8 +795,8 @@
"notification_drawer": {
"click_to_configure": "Klik på knappen for at konfigurere {entity}",
"close": "Luk",
"empty": "Ingen meddelelser",
"title": "Meddelelser"
"empty": "Ingen notifikationer",
"title": "Notifikationer"
},
"notification_toast": {
"connection_lost": "Forbindelse afbrudt. Opretter forbindelse igen...",
@ -1488,7 +1491,7 @@
},
"configure": "Konfigurer",
"configured": "Konfigureret",
"description": "Administrer tilsluttede enheder og tjenester",
"description": "Administrer og opsæt integrationer",
"details": "Integrationsdetaljer",
"discovered": "Fundet",
"home_assistant_website": "Home Assistants hjemmeside",
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "Aktivér bruger",
"active": "Aktiv",
"admin": "Administrator",
"caption": "Vis bruger",
"change_password": "Skift adgangskode",
"confirm_user_deletion": "Er du sikker på, at du vil slette {name}?",
@ -1744,6 +1748,7 @@
"owner": "Ejer",
"rename_user": "Omdøb bruger",
"system_generated": "Systemgenereret",
"system_generated_users_not_editable": "Systemoprettede brugere kan ikke opdateres.",
"system_generated_users_not_removable": "Kan ikke fjerne systemgenererede brugere.",
"unnamed_user": "Unavngiven bruger",
"update_user": "Opdater",
@ -2175,6 +2180,7 @@
"attribute": "Egenskab",
"camera_image": "Kameraentitet",
"camera_view": "Kameravisning",
"double_tap_action": "Handling ved dobbelttryk",
"entities": "Entiteter",
"entity": "Entitet",
"hold_action": "Handling ved hold",
@ -2225,6 +2231,7 @@
"default_zoom": "Standard-zoom",
"description": "Kort-kortet giver dig mulighed for at vise entiteter på et kort.",
"geo_location_sources": "Lokalitetskilder",
"hours_to_show": "Vis i antal timer",
"name": "Kort",
"source": "Kilde"
},
@ -2281,6 +2288,10 @@
"name": "Vejrudsigt"
}
},
"cardpicker": {
"custom_card": "Brugerdefineret",
"no_description": "Ingen beskrivelse tilgængelig."
},
"edit_card": {
"add": "Tilføj kort",
"delete": "Slet kort",
@ -2642,12 +2653,12 @@
"header": "Multifaktor-godkendelsesmoduler"
},
"push_notifications": {
"description": "Send meddelelser til denne enhed.",
"description": "Send notifikationer til denne enhed.",
"error_load_platform": "Konfigurer notify.html5",
"error_use_https": "Kræver SSL aktiveret til brugerflade.",
"header": "Push-meddelelser",
"header": "Push-notifikationer",
"link_promo": "Lær mere",
"push_notifications": "Push-meddelelser"
"push_notifications": "Push-notifikationer"
},
"refresh_tokens": {
"confirm_delete": "Er du sikker på, at du vil slette opdateringsstoken til {name}?",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Luftdruck",
"humidity": "Luftfeuchtigkeit",
"precipitation": "Niederschlag",
"temperature": "Temperatur",
"visibility": "Sichtweite",
"wind_speed": "Windgeschwindigkeit"
@ -531,7 +532,9 @@
"wnw": "WNW",
"wsw": "WSW"
},
"forecast": "Prognose"
"forecast": "Prognose",
"high": "Hoch",
"low": "Niedrig"
}
},
"common": {
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "Benutzer aktivieren",
"active": "Aktiv",
"admin": "Administrator",
"caption": "Benutzer anzeigen",
"change_password": "Passwort ändern",
"confirm_user_deletion": "Möchten Sie {name} wirklich löschen?",
@ -1744,6 +1748,7 @@
"owner": "Besitzer",
"rename_user": "Benutzer umbenennen",
"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.",
"unnamed_user": "Unbenannter Benutzer",
"update_user": "Aktualisieren",
@ -2175,6 +2180,7 @@
"attribute": "Attribut",
"camera_image": "Kamera-Entität",
"camera_view": "Kameraansicht",
"double_tap_action": "Doppeltipp-Aktion",
"entities": "Ungenutzte Elemente",
"entity": "Entität",
"hold_action": "Halte-Aktion",
@ -2225,6 +2231,7 @@
"default_zoom": "Standard-Zoom",
"description": "Mit der Map-Karte kannst du Objekte auf einer Karte anzeigen lassen.",
"geo_location_sources": "Geolocation-Quellen",
"hours_to_show": "Zu zeigende Stunden",
"name": "Karte",
"source": "Quelle"
},
@ -2281,6 +2288,10 @@
"name": "Wettervorhersage"
}
},
"cardpicker": {
"custom_card": "Benutzerdefiniert",
"no_description": "Keine Beschreibung verfügbar."
},
"edit_card": {
"add": "Karte hinzufügen",
"delete": "Löschen",

View File

@ -546,6 +546,7 @@
"save": "Save",
"successfully_deleted": "Successfully deleted",
"successfully_saved": "Successfully saved",
"undo": "Undo",
"yes": "Yes"
},
"components": {
@ -1734,6 +1735,7 @@
"editor": {
"activate_user": "Activate user",
"active": "Active",
"admin": "Administrator",
"caption": "View user",
"change_password": "Change password",
"confirm_user_deletion": "Are you sure you want to delete {name}?",
@ -1747,6 +1749,7 @@
"owner": "Owner",
"rename_user": "Rename user",
"system_generated": "System generated",
"system_generated_users_not_editable": "Unable to update system generated users.",
"system_generated_users_not_removable": "Unable to remove system generated users.",
"unnamed_user": "Unnamed User",
"update_user": "Update",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Presión del aire",
"humidity": "Humedad",
"precipitation": "Precipitación",
"temperature": "Temperatura",
"visibility": "Visibilidad",
"wind_speed": "Velocidad del viento"
@ -531,7 +532,9 @@
"wnw": "ONO",
"wsw": "OSO"
},
"forecast": "Pronóstico"
"forecast": "Pronóstico",
"high": "Máxima",
"low": "Mínima"
}
},
"common": {
@ -1117,7 +1120,7 @@
"info": "Home Assistant Cloud proporciona una conexión remota segura a tu instancia mientras estás fuera de casa.",
"instance_is_available": "Tu instancia está disponible en",
"instance_will_be_available": "Tu instancia estará disponible en",
"link_learn_how_it_works": "Aprender cómo funciona",
"link_learn_how_it_works": "Aprende cómo funciona",
"title": "Control remoto"
},
"sign_out": "Cerrar sesión",
@ -1517,11 +1520,11 @@
"cant_edit_yaml": "Los paneles de control definidos en YAML no se pueden editar desde la IU. Cámbialos en configuration.yaml.",
"caption": "Paneles de control",
"conf_mode": {
"storage": "Controlado por la interfaz de usuario",
"storage": "Controlado por la IU",
"yaml": "Archivo YAML"
},
"confirm_delete": "¿Estás seguro de que quieres eliminar este panel de control?",
"default_dashboard": "Este es el panel predeterminado",
"default_dashboard": "Este es el panel de control predeterminado",
"detail": {
"create": "Crear",
"delete": "Eliminar",
@ -1649,7 +1652,7 @@
"headers": {
"name": "Nombre"
},
"introduction": "El editor de escenas te permite crear y editar escenas. En el enlace siguiente puedes leer las instrucciones para asegurarte de que has configurado Home Assistant correctamente.",
"introduction": "El editor de escenas te permite crear y editar escenas. Por favor, sigue el siguiente enlace para leer las instrucciones para asegurarte de que has configurado Home Assistant correctamente.",
"learn_more": "Saber más sobre las escenas",
"no_scenes": "No pudimos encontrar ninguna escena editable",
"only_editable": "Solo las escenas definidas en scenes.yaml son editables.",
@ -1679,7 +1682,7 @@
"headers": {
"name": "Nombre"
},
"introduction": "El editor de scripts te permite crear y editar scripts. En el enlace siguiente puedes leer las instrucciones para asegurarte de que has configurado Home Assistant correctamente.",
"introduction": "El editor de scripts te permite crear y editar scripts. Por favor, sigue el siguiente enlace para leer las instrucciones para asegurarte de que has configurado Home Assistant correctamente.",
"learn_more": "Saber más sobre los scripts",
"no_scripts": "No hemos encontrado ningún script editable",
"show_info": "Mostrar información sobre el script",
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "Activar usuario",
"active": "Activo",
"admin": "Administrador",
"caption": "Ver usuario",
"change_password": "Cambiar la contraseña",
"confirm_user_deletion": "¿Seguro que quieres eliminar {name}?",
@ -1744,6 +1748,7 @@
"owner": "Propietario",
"rename_user": "Renombrar usuario",
"system_generated": "Generado por el sistema",
"system_generated_users_not_editable": "No se pueden actualizar los usuarios generados por el sistema.",
"system_generated_users_not_removable": "No se pueden eliminar los usuarios generados por el sistema.",
"unnamed_user": "Usuario sin nombre",
"update_user": "Actualizar",
@ -2175,6 +2180,7 @@
"attribute": "Atributo",
"camera_image": "Entidad de cámara",
"camera_view": "Vista de cámara",
"double_tap_action": "Acción de doble toque",
"entities": "Entidades",
"entity": "Entidad",
"hold_action": "Acción de mantener",
@ -2225,6 +2231,7 @@
"default_zoom": "Zoom predeterminado",
"description": "La tarjeta Mapa que te permite mostrar entidades en un mapa.",
"geo_location_sources": "Fuentes de geolocalización",
"hours_to_show": "Horas para mostrar",
"name": "Mapa",
"source": "Fuente"
},
@ -2281,6 +2288,10 @@
"name": "Pronóstico del tiempo"
}
},
"cardpicker": {
"custom_card": "Personalizado",
"no_description": "No hay descripción disponible."
},
"edit_card": {
"add": "Añadir tarjeta",
"delete": "Eliminar tarjeta",
@ -2608,7 +2619,7 @@
"language": {
"dropdown_label": "Idioma",
"header": "Idioma",
"link_promo": "Ayudar traduciendo"
"link_promo": "Ayuda a traducir"
},
"logout": "Cerrar sesión",
"logout_text": "¿Estás seguro de que quieres cerrar la sesión?",
@ -2623,7 +2634,7 @@
"empty_state": "Aún no tienes tokens de acceso de larga duración.",
"header": "Tokens de acceso de larga duración",
"last_used": "Último uso el {date} desde {location}",
"learn_auth_requests": "Aprender cómo realizar solicitudes autenticadas.",
"learn_auth_requests": "Aprende cómo realizar solicitudes autenticadas.",
"not_used": "Nunca ha sido usado",
"prompt_copy_token": "Copia tu token de acceso. No se mostrará de nuevo.",
"prompt_name": "¿Nombre?"
@ -2664,7 +2675,7 @@
"dropdown_label": "Tema",
"error_no_theme": "No hay temas disponibles",
"header": "Tema",
"link_promo": "Saber más sobre los temas"
"link_promo": "Aprende sobre los temas"
},
"vibrate": {
"description": "Activar o deshabilitar la vibración en este dispositivo al controlar dispositivos.",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Pression atmosphérique",
"humidity": "Humidité",
"precipitation": "Précipitation",
"temperature": "Température",
"visibility": "Visibilité",
"wind_speed": "Vitesse du vent"
@ -531,7 +532,9 @@
"wnw": "O-NO",
"wsw": "O-SO"
},
"forecast": "Prévisions"
"forecast": "Prévisions",
"high": "Haute",
"low": "Faible"
}
},
"common": {
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "Activer l'utilisateur",
"active": "Actif",
"admin": "Administrateur",
"caption": "Voir l'utilisateur",
"change_password": "Changer le mot de passe",
"confirm_user_deletion": "Êtes-vous sûr de vouloir supprimer {name} ?",
@ -1744,6 +1748,7 @@
"owner": "Propriétaire",
"rename_user": "Renommer l'utilisateur",
"system_generated": "Généré par le système",
"system_generated_users_not_editable": "Impossible de mettre à jour les utilisateurs générés par le système.",
"system_generated_users_not_removable": "Impossible de supprimer les utilisateurs générés par le système.",
"unnamed_user": "Utilisateur sans nom",
"update_user": "Mise à jour",
@ -2225,6 +2230,7 @@
"default_zoom": "Zoom par défaut",
"description": "La carte Carte vous permet d'afficher des entités sur une carte.",
"geo_location_sources": "Sources de géolocalisation",
"hours_to_show": "Heures à montrer",
"name": "Carte",
"source": "Source"
},
@ -2281,6 +2287,10 @@
"name": "Prévisions Météo"
}
},
"cardpicker": {
"custom_card": "Personnalisé",
"no_description": "Aucune description disponible."
},
"edit_card": {
"add": "Ajouter une action",
"delete": "Supprimer",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "기압",
"humidity": "습도",
"precipitation": "강수량",
"temperature": "기온",
"visibility": "시정",
"wind_speed": "풍속"
@ -531,7 +532,9 @@
"wnw": "서북서",
"wsw": "서남서"
},
"forecast": "일기 예보"
"forecast": "일기 예보",
"high": "높음",
"low": "낮음"
}
},
"common": {
@ -630,7 +633,7 @@
"entity_id": "구성요소 ID",
"icon": "아이콘 재정의",
"icon_error": "아이콘은 접두사:아이콘이름 형식이어야 합니다. 예: mdi:home",
"name": "대체 이름",
"name": "이름 재정의",
"note": "참고: 아직 모든 통합 구성요소에 적용되지 않을 수 있습니다.",
"unavailable": "이 구성요소는 현재 사용할 수 없습니다.",
"update": "업데이트"
@ -731,7 +734,7 @@
"more_info_settings": {
"back": "뒤로가기",
"entity_id": "구성요소 ID",
"name": "대체 이름",
"name": "이름 재정의",
"save": "저장"
},
"options_flow": {
@ -1370,7 +1373,7 @@
"enabled_description": "비활성화 된 구성요소는 Home Assistant 에 추가되지 않습니다.",
"enabled_label": "구성요소 활성화",
"entity_id": "구성요소 ID",
"name": "대체 이름",
"name": "이름 재정의",
"note": "참고 : 아직 모든 통합 구성요소에 적용되지 않을 수 있습니다.",
"unavailable": "이 구성요소는 현재 사용할 수 없습니다.",
"update": "업데이트"
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "사용자 활성화",
"active": "활성화",
"admin": "관리자",
"caption": "사용자 보기",
"change_password": "비밀번호 변경",
"confirm_user_deletion": "{name} 을(를) 삭제하시겠습니까?",
@ -1744,6 +1748,7 @@
"owner": "소유자",
"rename_user": "사용자 이름 변경",
"system_generated": "시스템 자동 생성",
"system_generated_users_not_editable": "시스템 자동 생성 사용자는 업데이트할 수 없습니다.",
"system_generated_users_not_removable": "시스템 자동 생성 사용자는 제거할 수 없습니다.",
"unnamed_user": "이름이 없는 사용자",
"update_user": "업데이트",
@ -2175,6 +2180,7 @@
"attribute": "속성",
"camera_image": "카메라 구성요소",
"camera_view": "카메라 뷰",
"double_tap_action": "더블 탭 동작",
"entities": "구성요소",
"entity": "구성요소",
"hold_action": "길게 누르기 동작",
@ -2225,6 +2231,7 @@
"default_zoom": "기본 확대",
"description": "지도 카드는 지도에 구성요소를 표시할 수 있습니다.",
"geo_location_sources": "위치정보 소스",
"hours_to_show": "표시 시간",
"name": "지도",
"source": "소스"
},
@ -2281,6 +2288,10 @@
"name": "날씨 예보"
}
},
"cardpicker": {
"custom_card": "사용자 정의",
"no_description": "상세정보가 없습니다"
},
"edit_card": {
"add": "카드 추가하기",
"delete": "카드 삭제",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Loftdrock",
"humidity": "Fiichtegkeet",
"precipitation": "Nidderschlag",
"temperature": "Temperatur",
"visibility": "Visibilitéit",
"wind_speed": "Wandvitesse"
@ -531,7 +532,9 @@
"wnw": "WNW",
"wsw": "WSW"
},
"forecast": "Prognose"
"forecast": "Prognose",
"high": "Héich",
"low": "Niddreg"
}
},
"common": {
@ -807,6 +810,10 @@
"areas": {
"caption": "Lëscht vun de Beräicher",
"create_area": "Beräich erstellen",
"data_table": {
"area": "Beräich",
"devices": "Apparater"
},
"delete": {
"confirmation_text": "All Apparater an dësem Beräich ginn néirens zougewisen.",
"confirmation_title": "Sécher fir dëse Beräich ze läsche?"
@ -1049,6 +1056,9 @@
"delete_confirm": "Sécher fir dës Automatisme ze läschen?",
"edit_automation": "Automatisme änneren",
"header": "Automatismen editéieren",
"headers": {
"name": "Numm"
},
"introduction": "Den Automatismen-Editor erméiglecht et fir Automatismen z'erstellen an ze änneren. Lies w.e.g. [d'Instruktioune](https://home-assistant.io/docs/automation/editor/) fir sécher ze stellen dass den Home Assistant richteg agestallt ass.",
"learn_more": "Méi iwwert Automatioune liesen",
"no_automations": "Keen Automatismus fir ze ännere fonnt",
@ -1329,6 +1339,7 @@
},
"info": "Informatioune vum Apparat",
"name": "Numm",
"no_devices": "Keng Apparater",
"scene": {
"create": "Zeen mat Apparat erstellen",
"no_scenes": "Keng Zeenen",
@ -1631,6 +1642,9 @@
"delete_scene": "Zeen läschen",
"edit_scene": "Zeen änneren",
"header": "Zeen Editeur",
"headers": {
"name": "Numm"
},
"introduction": "De Zeenen Editeur erlaabt Iech Zeenen z'erstellen an z'änneren. Follegt de Link hei ënnendrënner fir d'Instruktiounen ze liese fir sécher ze stellen, datt Dir den Home Assistant richteg konfiguréiert hutt.",
"learn_more": "Méi iwwert Zeene léieren",
"no_scenes": "Keng Zeene fir ze ännere fonnt",
@ -1658,9 +1672,13 @@
"add_script": "Skript dobäisetze",
"edit_script": "Skript änneren",
"header": "Skript Editeur",
"headers": {
"name": "Numm"
},
"introduction": "De Skript Editeur erlaabt Iech Skripten ze erstellen an z'änneren. Follegt de Link hei ënnendrënner fir d'Instruktiounen ze liese fir sécher ze stellen, datt Dir den Home Assistant richteg konfiguréiert hutt",
"learn_more": "Méi iwwert Skripten léieren",
"no_scripts": "Keng Skripte fir ze ännere fonnt",
"show_info": "Informatiounen vum Skript uweisen",
"trigger_script": "Skript ausléisen"
}
},
@ -1718,14 +1736,21 @@
"group": "Gruppe",
"group_update_failed": "Feeler bei der aktualiséiereung vum Gruppe",
"id": "ID",
"name": "Numm",
"owner": "Proprietär",
"rename_user": "Benotzer ëmbenennen",
"system_generated": "Vum System generéiert",
"system_generated_users_not_removable": "Ka keng System generéiert Benotzer läschen.",
"unnamed_user": "Benotzer ouni Numm",
"update_user": "Aktualiséieren",
"user_rename_failed": "Feeler beim ëmbenennen vum Benotzer:"
},
"picker": {
"headers": {
"group": "Grupp",
"name": "Numm",
"system": "System"
},
"system_generated": "Vum System generéiert",
"title": "Benotzer"
}
@ -2126,6 +2151,9 @@
"description": "D'Entity Filter Kaart erlaabt Iech eng Lëscht vun Entitéiten ze definéieren déi nëmmen ugewise gi wann se an engem bestëmmten Zoustand sinn.",
"name": "Entitéite Filter"
},
"entity": {
"name": "Entitéit"
},
"gauge": {
"description": "D'Gauge Kaart ass eng Basis Kaart déi Sensor Date visuell duerstellt.",
"name": "Skala",
@ -2138,8 +2166,10 @@
},
"generic": {
"aspect_ratio": "Säiteverhältnis",
"attribute": "Attribut",
"camera_image": "Kamera Entitéit",
"camera_view": "Kamera Usiicht",
"double_tap_action": "Aktioun beim 2-mol tippen",
"entities": "Entitéiten",
"entity": "Entitéit",
"hold_action": "Aktioun beim unhalen",
@ -2190,6 +2220,7 @@
"default_zoom": "Standard Zoom",
"description": "Kaarte Kaart erméiglecht d'Visualisatioun vun Entitéiten op enger Kaart.",
"geo_location_sources": "Quell vun der Geolokalisatioun",
"hours_to_show": "Stonnen uweisen",
"name": "Kaart",
"source": "Quell"
},
@ -2246,6 +2277,10 @@
"name": "Wiederprevisioune"
}
},
"cardpicker": {
"custom_card": "Personnaliséiert",
"no_description": "Keng Beschreiwung verfügbar"
},
"edit_card": {
"add": "Kaart dobäisetzen",
"delete": "Läschen",
@ -2310,6 +2345,7 @@
"save_config": {
"cancel": "Vergiess et",
"close": "Zoumaachen",
"empty_config": "Mat engem eidelen Tableau de Bord ufänken",
"header": "Kontroll iwwert Loveloce UI iwwerhuelen",
"para": "Standardméisseg verwalt Home Assistant de Benotzer Interface an aktualiséiert en soubal nei Entitéiten oder Lovelace-Komponenten disponibel sinn. Wann dir d'Kontrolle iwwerhuelt, kënne mir keng automatesch Ännerung méi fir iech maachen.",
"para_sure": "Sécher fir d'Kontrolle iwwert de Benotzer Interface z'iwwerhuelen?",
@ -2558,6 +2594,11 @@
"submit": "Ofschécken"
},
"current_user": "Dir sidd aktuell ageloggt als {fullName}.",
"dashboard": {
"description": "Wiel ee Tableau de Bord als Standard op dësem Apparat.",
"dropdown_label": "Tableau de Bord",
"header": "Tableau de Bord"
},
"force_narrow": {
"description": "Dës Optioun verstoppt d'Säite Läischt esou wéi op engem mobillen Apparat.",
"header": "Säite Läischt ëmmer verstoppen"

View File

@ -9,13 +9,13 @@
"config_entry": {
"disabled_by": {
"config_entry": "Konfigurer oppføring",
"integration": "Integrering",
"integration": "Integrasjon",
"user": "Bruker"
}
},
"domain": {
"alarm_control_panel": "Alarm kontrollpanel",
"automation": "Automatisering",
"automation": "Automasjon",
"binary_sensor": "Binær sensor",
"calendar": "Kalender",
"camera": "Kamera",
@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Lufttrykk",
"humidity": "Luftfuktighet",
"precipitation": "Nedbør",
"temperature": "Temperatur",
"visibility": "Sikt",
"wind_speed": "Vindstyrke"
@ -531,7 +532,9 @@
"wnw": "VNV",
"wsw": "VSV"
},
"forecast": "Prognose"
"forecast": "Prognose",
"high": "Høy",
"low": "Lav"
}
},
"common": {
@ -578,11 +581,11 @@
},
"related-items": {
"area": "Område",
"automation": "Del av følgende automatiseringer",
"automation": "Del av følgende automasjoner",
"device": "Enhet",
"entity": "Beslektede entiteter",
"group": "Del av følgende grupper",
"integration": "Integrering",
"integration": "Integrasjoner",
"no_related_found": "Finner ingen relaterte elementer.",
"scene": "Del av følgende scener",
"script": "Del av følgende skript"
@ -685,7 +688,7 @@
},
"not_editable": "Kan ikke redigeres",
"not_editable_text": "Denne entiteten kan ikke endres fra brukergrensesnittet fordi den er definert i configuration.yaml.",
"platform_not_loaded": "{platform}-integreringen er ikke lastet inn. Legg til konfigurasjonen ved å legge til 'default_config:' eller '{platform}:'.",
"platform_not_loaded": "{platform}-integrasjonen er ikke lastet inn. Legg til konfigurasjonen 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 konfigurert fra brukergrensesnittet, kan konfigureres fra brukergrensesnittet."
},
@ -698,7 +701,7 @@
"restored": {
"confirm_remove_text": "Er du sikker på at du vil fjerne denne entiteten?",
"confirm_remove_title": "Vil du fjerne entiteten?",
"not_provided": "Denne entiteten er ikke tilgjengelig for øyeblikket og er foreldreløs til en fjernet, endret eller ikke-fungerende integrering eller enhet.",
"not_provided": "Denne entiteten er ikke tilgjengelig for øyeblikket og er foreldreløs til en fjernet, endret eller ikke-fungerende integrasjon eller enhet.",
"remove_action": "Fjern entitet",
"remove_intro": "Hvis entiteten ikke lenger er i bruk, kan du rense den opp ved å fjerne den."
},
@ -812,6 +815,10 @@
"areas": {
"caption": "Områder",
"create_area": "OPPRETT OMRÅDE",
"data_table": {
"area": "Område",
"devices": "Enheter"
},
"delete": {
"confirmation_text": "Alle enheter som tilhører dette området vil ikke bli tildelt.",
"confirmation_title": "Er du sikker på at du vil slette dette området?"
@ -943,14 +950,14 @@
},
"unsupported_condition": "Ikke-støttet tilstand: {condition}"
},
"default_name": "Ny automatisering",
"default_name": "Ny automasjon",
"description": {
"label": "Beskrivelse",
"placeholder": "Valgfri beskrivelse"
},
"edit_ui": "Rediger med UI",
"edit_yaml": "Rediger som YAML",
"enable_disable": "Aktivere/deaktivere automatisering",
"enable_disable": "Aktivere/deaktivere automasjon",
"introduction": "Bruk automasjon for å få liv i hjemmet ditt",
"load_error_not_editable": "Kun automasjoner i automations.yaml kan redigeres.",
"load_error_unknown": "Feil ved lasting av automasjon ({err_no}).",
@ -961,7 +968,7 @@
"delete_confirm": "Er du sikker på at du vil slette dette?",
"duplicate": "Dupliser",
"header": "Utløsere",
"introduction": "Utløsere er det som starter en automatiseringsregel. Det er mulig å spesifisere flere utløsere for samme regel. Når en utløser aktiveres, vil Home Assistant bekrefte vilkårene, hvis noen, og kjøre handlingen. \n\n[Lær mer om utløsere.](https://home-assistant.io/docs/automation/trigger/)",
"introduction": "Utløsere er det som starter en automasjonsregel. Det er mulig å spesifisere flere utløsere for samme regel. Når en utløser aktiveres, vil Home Assistant bekrefte vilkårene - hvis noen - og utføre handlingen. \n\n[Lær mer om utløsere.](https://home-assistant.io/docs/automation/trigger/)",
"learn_more": "Lær mer om utløsere",
"name": "Utløse",
"type_select": "Utløser",
@ -1049,17 +1056,20 @@
"unsaved_confirm": "Du har endringer som ikke er lagret. Er du sikker på at du vil forlate?"
},
"picker": {
"add_automation": "Legg til automatisering",
"add_automation": "Legg til automasjon",
"delete_automation": "Slett automasjon",
"delete_confirm": "Er du sikker på at du vil slette denne automatiseringen?",
"delete_confirm": "Er du sikker på at du vil slette denne automasjonen?",
"edit_automation": "Rediger automasjon",
"header": "Automasjonsredigering",
"headers": {
"name": "Navn"
},
"introduction": "Automasjonsredigeringen lar deg lage og redigere automasjoner. Følg lenken under for å forsikre deg om at du har konfigurert Home Assistant riktig.",
"learn_more": "Lær mer om automasjoner",
"no_automations": "Vi kunne ikke finne noen redigerbare automasjoner",
"only_editable": "Bare automasjoner definert i automations.yaml kan redigeres.",
"pick_automation": "Velg automasjon for å redigere",
"show_info_automation": "Vis informasjon om automatisering"
"show_info_automation": "Vis informasjon om automasjon"
}
},
"cloud": {
@ -1122,7 +1132,7 @@
"loading": "Laster inn ...",
"manage": "Administrer",
"no_hooks_yet": "Ser ut som du ikke har noen webhooks ennå. Kom i gang ved å konfigurere en ",
"no_hooks_yet_link_automation": "webhook automatisering",
"no_hooks_yet_link_automation": "webhook-automasjon",
"no_hooks_yet_link_integration": "webhook-basert integrasjon",
"no_hooks_yet2": " eller ved å opprette en ",
"title": "Webhooks"
@ -1302,9 +1312,9 @@
"conditions": {
"caption": "Bare gjør noe hvis..."
},
"create": "Lag automatisering med enheten",
"no_automations": "Ingen automatiseringer",
"no_device_automations": "Det er ingen automatiseringer tilgjengelig for denne enheten.",
"create": "Lag automasjon med enheten",
"no_automations": "Ingen automasjoner",
"no_device_automations": "Det er ingen automasjoner tilgjengelig for denne enheten.",
"triggers": {
"caption": "Gjør noe når ..."
}
@ -1318,7 +1328,7 @@
"area": "Område",
"battery": "Batteri",
"device": "Enhet",
"integration": "Integrering",
"integration": "Integrasjon",
"manufacturer": "Produsent",
"model": "Modell",
"no_devices": "Ingen enheter"
@ -1334,6 +1344,7 @@
},
"info": "Enhetsinformasjon",
"name": "Navn",
"no_devices": "Ingen enheter",
"scene": {
"create": "Lag scene med enheten",
"no_scenes": "Ingen scener",
@ -1388,7 +1399,7 @@
"headers": {
"enabled": "Aktivert",
"entity_id": "Entitets-ID",
"integration": "Integrering",
"integration": "Integrasjon",
"name": "Navn",
"status": ""
},
@ -1399,7 +1410,7 @@
"button": "Fjern valgte",
"confirm_partly_text": "Du kan bare fjerne {removable} av de valgte {selected} enhetene. Enheter kan bare fjernes når integrasjonen ikke lenger leverer enhetene. Noen ganger må du starte Home Assistant på nytt før du kan fjerne enhetene til en fjernet integrasjon. Er du sikker på at du vil fjerne de flyttbare enhetene?",
"confirm_partly_title": "Bare {number} valgte enheter kan fjernes.",
"confirm_text": "Du bør fjerne dem fra Lovelace config og automatiseringer hvis de inneholder disse enhetene.",
"confirm_text": "Du bør fjerne dem fra Lovelace-konfigurasjonen og automasjoner hvis de inneholder disse entitetene.",
"confirm_title": "Vil du fjerne {number} enheter?"
},
"selected": "{number} valgte",
@ -1417,7 +1428,7 @@
"header": "Konfigurer Home Assistant",
"helpers": {
"caption": "Hjelpere",
"description": "Elementer som kan bidra til å bygge automatiseringer.",
"description": "Elementer som kan bidra til å bygge automasjoner.",
"dialog": {
"add_helper": "Legg hjelper",
"add_platform": "Legg til {platform}",
@ -1473,7 +1484,7 @@
},
"failed_create_area": "Kunne ikke opprette område.",
"finish": "Fullfør",
"loading_first_time": "Vent mens integreringen installeres",
"loading_first_time": "Vent mens integrasjonen installeres",
"name_new_area": "Navn på det nye området?",
"not_all_required_fields": "Ikke alle obligatoriske felt er fylt ut.",
"submit": "Send inn"
@ -1487,7 +1498,7 @@
"ignore": {
"confirm_delete_ignore": "Dette vil få integrasjonen til å vises i de oppdagede integrasjonene dine igjen når den blir oppdaget. Dette kan kreve omstart eller ta litt tid.",
"confirm_delete_ignore_title": "Slutt å ignorere {name}?",
"confirm_ignore": "Er du sikker på at du ikke vil konfigurere denne integreringen? Du kan angre dette ved å klikke på «Vis ignorerte integreringer» i overflow-menyen øverst til høyre.",
"confirm_ignore": "Er du sikker på at du ikke vil konfigurere denne integrasjonen? Du kan angre ved å klikke på «Vis ignorerte integrasjoner» i overflow-menyen øverst til høyre.",
"confirm_ignore_title": "Ignorer oppdaging av {name}?",
"hide_ignored": "Skjul ignorerte integrasjoner",
"ignore": "Ignorer",
@ -1593,12 +1604,12 @@
"device_tracker_pick": "Velg en enhet å spore",
"device_tracker_picked": "Spor enhet",
"link_integrations_page": "Integrasjonsside",
"link_presence_detection_integrations": "Integrering av tilstedeværelsesdeteksjon",
"link_presence_detection_integrations": "Integrasjon for tilstedeværelsesdeteksjon",
"linked_user": "Koblet bruker",
"name": "Navn",
"name_error_msg": "Navn er påkrevd",
"new_person": "Ny person",
"no_device_tracker_available_intro": "Når du har enheter som indikerer en persons tilstedeværelse, vil du kunne tilordne dem til en person her. Du kan legge til den første enheten ved å legge til en integrering av tilstedeværelses gjenkjenning fra integrerings siden.",
"no_device_tracker_available_intro": "Når du har enheter som indikerer en persons tilstedeværelse, vil du kunne tilordne dem til en person herfra. Du kan legge til den første enheten ved å legge til en integrasjon for tilstedeværelsesgjenkjenning fra integrasjonssiden.",
"update": "Oppdater"
},
"introduction": "Her kan du definere hver person av interesse i Home Assistant.",
@ -1638,6 +1649,9 @@
"delete_scene": "Slett scene",
"edit_scene": "Rediger scene",
"header": "Sceneditor",
"headers": {
"name": "Navn"
},
"introduction": "Sceneditoren lar deg lage og redigere scener. Følg lenken nedenfor for å lese instruksjonene for å forsikre deg om at du har konfigurert Home Assistant riktig.",
"learn_more": "Lær mer om scener",
"no_scenes": "Vi fant ingen redigerbare scener",
@ -1665,9 +1679,13 @@
"add_script": "Legg til skript",
"edit_script": "Rediger skript",
"header": "Skriptredigering",
"headers": {
"name": "Navn"
},
"introduction": "Skripteditoren lar deg lage og redigere skript. Følg lenken nedenfor for å lese instruksjonene for å forsikre deg om at du har konfigurert Home Assistant riktig.",
"learn_more": "Lær mer om skript",
"no_scripts": "Vi kunne ikke finne noen redigerbare skript",
"show_info": "Vis informasjon om skript",
"trigger_script": "Utløse skript"
}
},
@ -1725,14 +1743,21 @@
"group": "Gruppe",
"group_update_failed": "Gruppeoppdatering mislyktes:",
"id": "Id",
"name": "Navn",
"owner": "Eier",
"rename_user": "Gi nytt navn til bruker",
"system_generated": "System generert",
"system_generated_users_not_removable": "Kan ikke fjerne systemgenererte brukere.",
"unnamed_user": "Bruker uten navn",
"update_user": "Oppdater",
"user_rename_failed": "Brukernavn mislyktes:"
},
"picker": {
"headers": {
"group": "Gruppe",
"name": "Navn",
"system": "System"
},
"system_generated": "System generert",
"title": "Brukere"
}
@ -1862,7 +1887,7 @@
"name": "Navn",
"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 det til automatiseringer.",
"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"
@ -1871,7 +1896,7 @@
"edit_home_zone_narrow": "Radiusen til hjemsonen kan ikke redigeres fra frontend ennå. Plasseringen kan endres fra den generelle konfigurasjonen.",
"go_to_core_config": "Gå til generell konfigurasjon?",
"home_zone_core_config": "Plasseringen av hjemmesonen kan redigeres fra den generelle konfigurasjonssiden. Radiusen til hjemsonen kan ikke redigeres fra frontend ennå. Vil du gå til den generelle konfigurasjonen?",
"introduction": "Med soner kan du angi bestemte områder på jorden. Når en person er innenfor en sone, vil staten ta navnet fra sonen. Soner kan også brukes som en utløser eller tilstand i automatiserings oppsett.",
"introduction": "Med soner kan du angi bestemte områder på jorden. Når en person er innenfor en sone, vil tilstanden til lokasjonen ta navnet fra sonen. Soner kan også brukes som en utløser eller tilstand i automasjoner.",
"no_zones_created_yet": "Det ser ut til at du ikke har opprettet noen soner enda."
},
"zwave": {
@ -2135,6 +2160,9 @@
"description": "Entity Filter-kortet lar deg definere en liste over entiteter du bare vil spore når de er i en viss tilstand.",
"name": "Entitetsfilter"
},
"entity": {
"name": "Entitet"
},
"gauge": {
"description": "Gauge-kortet er et grunnleggende kort som gjør det mulig å se sensordata visuelt.",
"name": "Måler",
@ -2147,8 +2175,10 @@
},
"generic": {
"aspect_ratio": "Størrelsesforholdet",
"attribute": "Attributtet",
"camera_image": "Kameraentitet",
"camera_view": "Kameravisning",
"double_tap_action": "Dobbelttrykk Handling",
"entities": "Entiteter",
"entity": "Entitet",
"hold_action": "Hold handling",
@ -2199,6 +2229,7 @@
"default_zoom": "Standard zoom",
"description": "Kartkortet som lar deg vise enheter på et kart.",
"geo_location_sources": "Kilder for geolokasjon",
"hours_to_show": "Timer som skal vises",
"name": "Kart",
"source": "Kilde"
},
@ -2216,7 +2247,7 @@
"name": "Bildeelementer"
},
"picture-entity": {
"description": "Picture Entity-kortet viser en entitet i form av et bilde. I stedet for bilder fra URL, kan det også vise bilde av kameraenheter.",
"description": "Picture Entity-kortet viser en entitet i form av et bilde. I stedet for bilder fra URL, kan det også vise bilde av kameraentiteter.",
"name": "Bildeoppføring"
},
"picture-glance": {
@ -2255,6 +2286,10 @@
"name": "Værmelding"
}
},
"cardpicker": {
"custom_card": "Tilpasset",
"no_description": "Ingen beskrivelse tilgjengelig."
},
"edit_card": {
"add": "Legg til kort",
"delete": "Slett kort",
@ -2319,6 +2354,7 @@
"save_config": {
"cancel": "Glem det",
"close": "Lukk",
"empty_config": "Start med et tomt instrumentbord",
"header": "Ta kontroll over Lovelace brukergrensesnittet ditt",
"para": "Som standard vil Home Assistant opprettholde brukergrensesnittet ditt, oppdatere det når nye enheter eller Lovelace UI-komponenter blir tilgjengelige. Hvis du tar kontroll, vil vi ikke lenger gjøre endringer automatisk for deg.",
"para_sure": "Er du sikker på at du vil ta kontroll over brukergrensesnittet ditt?",
@ -2568,6 +2604,11 @@
"submit": "Send inn"
},
"current_user": "Du er logget inn som {fullName}.",
"dashboard": {
"description": "Velg et standard dashbord for denne enheten.",
"dropdown_label": "Dashboard",
"header": "Dashboard"
},
"force_narrow": {
"description": "Dette vil skjule sidepanelet som standard, tilsvarende opplevelsen på en mobil.",
"header": "Skjul alltid sidepanelet"

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "Luchtdruk",
"humidity": "Vochtigheid",
"precipitation": "Neerslag",
"temperature": "Temperatuur",
"visibility": "Zicht",
"wind_speed": "Windsnelheid"
@ -531,7 +532,9 @@
"wnw": "WNW",
"wsw": "WZW"
},
"forecast": "Verwachting"
"forecast": "Verwachting",
"high": "hoog",
"low": "Laag"
}
},
"common": {
@ -2175,6 +2178,7 @@
"attribute": "Kenmerk",
"camera_image": "Camera-entiteit",
"camera_view": "Cameraweergave",
"double_tap_action": "Dubbele tik Actie",
"entities": "Entiteiten",
"entity": "Entiteit",
"hold_action": "Actie vasthouden",
@ -2225,6 +2229,7 @@
"default_zoom": "Standaard zoom",
"description": "Met de Map-kaart kun je entiteiten op een kaart kunt weergeven.",
"geo_location_sources": "Geolocatiebronnen",
"hours_to_show": "Uren om weer te geven",
"name": "Kaart",
"source": "Bron"
},
@ -2281,6 +2286,10 @@
"name": "Weersverwachting"
}
},
"cardpicker": {
"custom_card": "Aangepaste UI's:",
"no_description": "Er is geen beschrijving beschikbaar"
},
"edit_card": {
"add": "Kaart toevoegen",
"delete": "Verwijder kaart",

View File

@ -1731,6 +1731,7 @@
"editor": {
"activate_user": "Активировать",
"active": "Активен",
"admin": "Администратор",
"caption": "Просмотр пользователя",
"change_password": "Изменить пароль",
"confirm_user_deletion": "Вы уверены, что хотите удалить {name}?",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "气压",
"humidity": "湿度",
"precipitation": "降水量",
"temperature": "温度",
"visibility": "能见度",
"wind_speed": "风速"
@ -531,7 +532,9 @@
"wnw": "西北偏西",
"wsw": "西南偏西"
},
"forecast": "预报"
"forecast": "预报",
"high": "高",
"low": "低"
}
},
"common": {
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "激活用户",
"active": "激活",
"admin": "管理员",
"caption": "用户信息",
"change_password": "更改密码",
"confirm_user_deletion": "您确定要删除{name}吗?",
@ -1744,6 +1748,7 @@
"owner": "所有者",
"rename_user": "重命名用户",
"system_generated": "系统生成",
"system_generated_users_not_editable": "无法更新系统生成的用户。",
"system_generated_users_not_removable": "无法删除系统生成的用户。",
"unnamed_user": "未命名的用户",
"update_user": "更新",
@ -2175,6 +2180,7 @@
"attribute": "属性",
"camera_image": "相机实体",
"camera_view": "相机视图",
"double_tap_action": "双击动作",
"entities": "实体",
"entity": "实体",
"hold_action": "保持操作",
@ -2225,6 +2231,7 @@
"default_zoom": "默认缩放",
"description": "“地图”卡片用于在地图上显示实体。",
"geo_location_sources": "地理位置来源",
"hours_to_show": "显示时间",
"name": "地图",
"source": "位置来源"
},
@ -2281,6 +2288,10 @@
"name": "天气预报"
}
},
"cardpicker": {
"custom_card": "自定义",
"no_description": "没有描述。"
},
"edit_card": {
"add": "添加卡片",
"delete": "删除",

View File

@ -509,6 +509,7 @@
"attributes": {
"air_pressure": "大氣壓",
"humidity": "濕度",
"precipitation": "降雨量",
"temperature": "溫度",
"visibility": "顯示選項",
"wind_speed": "風速"
@ -531,7 +532,9 @@
"wnw": "西北西",
"wsw": "西南西"
},
"forecast": "預報"
"forecast": "預報",
"high": "高",
"low": "低"
}
},
"common": {
@ -1731,6 +1734,7 @@
"editor": {
"activate_user": "激活用戶",
"active": "啟用",
"admin": "管理員",
"caption": "檢視用戶",
"change_password": "更改密碼",
"confirm_user_deletion": "確定要刪除 {name}",
@ -1744,6 +1748,7 @@
"owner": "擁有者",
"rename_user": "重命名用戶",
"system_generated": "系統產生",
"system_generated_users_not_editable": "無法更新系統產生用戶",
"system_generated_users_not_removable": "無法移除系統產生用戶",
"unnamed_user": "未命名用戶",
"update_user": "更新",
@ -2175,6 +2180,7 @@
"attribute": "屬性",
"camera_image": "攝影機物件",
"camera_view": "攝影機視角",
"double_tap_action": "雙點擊觸發",
"entities": "物件",
"entity": "物件",
"hold_action": "保持觸發",
@ -2225,6 +2231,7 @@
"default_zoom": "預設大小",
"description": "地圖面板可供顯示地圖物件。",
"geo_location_sources": "地理位置來源",
"hours_to_show": "顯示小時",
"name": "地圖面板",
"source": "來源"
},
@ -2281,6 +2288,10 @@
"name": "天氣預報面板"
}
},
"cardpicker": {
"custom_card": "自訂面板",
"no_description": "無描述可使用。"
},
"edit_card": {
"add": "新增面板",
"delete": "刪除面板",