Merge pull request #3399 from home-assistant/dev

20190720.0
This commit is contained in:
Paulus Schoutsen 2019-07-20 17:55:41 -07:00 committed by GitHub
commit 8dd44bca32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 462 additions and 182 deletions

View File

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

View File

@ -39,7 +39,7 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
<div class="target"> <div class="target">
<template is="dom-if" if="[[_hasKnownState(stateObj.state)]]"> <template is="dom-if" if="[[_hasKnownState(stateObj.state)]]">
<span class="state-label"> <span class="state-label">
[[_localizeState(localize, stateObj.state)]] [[_localizeState(localize, stateObj)]]
<template is="dom-if" if="[[stateObj.attributes.preset_mode]]"> <template is="dom-if" if="[[stateObj.attributes.preset_mode]]">
- [[_localizePreset(localize, stateObj.attributes.preset_mode)]] - [[_localizePreset(localize, stateObj.attributes.preset_mode)]]
</template> </template>
@ -116,8 +116,15 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
return state !== "unknown"; return state !== "unknown";
} }
_localizeState(localize, state) { _localizeState(localize, stateObj) {
return localize(`state.climate.${state}`) || state; const stateString = localize(`state.climate.${stateObj.state}`);
return stateObj.attributes.hvac_action
? `${localize(
`state_attributes.climate.hvac_action.${
stateObj.attributes.hvac_action
}`
)} (${stateString})`
: stateString;
} }
_localizePreset(localize, preset) { _localizePreset(localize, preset) {

View File

@ -78,14 +78,25 @@ class HaMenuButton extends LitElement {
protected updated(changedProps) { protected updated(changedProps) {
super.updated(changedProps); super.updated(changedProps);
if (!changedProps.has("narrow")) { if (!changedProps.has("narrow") && !changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
const oldNarrow =
changedProps.get("narrow") ||
(oldHass && oldHass.dockedSidebar === "always_hidden");
const newNarrow =
this.narrow || this.hass.dockedSidebar === "always_hidden";
if (oldNarrow === newNarrow) {
return; return;
} }
this.style.visibility = this.style.visibility =
this.narrow || this._alwaysVisible ? "initial" : "hidden"; newNarrow || this._alwaysVisible ? "initial" : "hidden";
if (!this.narrow) { if (!newNarrow) {
this._hasNotifications = false; this._hasNotifications = false;
if (this._unsubNotifications) { if (this._unsubNotifications) {
this._unsubNotifications(); this._unsubNotifications();

View File

@ -5,6 +5,7 @@ import {
css, css,
PropertyValues, PropertyValues,
property, property,
eventOptions,
} from "lit-element"; } from "lit-element";
import "@polymer/app-layout/app-toolbar/app-toolbar"; import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-icon-button/paper-icon-button";
@ -28,11 +29,11 @@ import {
} from "../data/persistent_notification"; } from "../data/persistent_notification";
import computeDomain from "../common/entity/compute_domain"; import computeDomain from "../common/entity/compute_domain";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
// tslint:disable-next-line: no-duplicate-imports
import { PaperIconItemElement } from "@polymer/paper-item/paper-icon-item";
const SHOW_AFTER_SPACER = ["config", "developer-tools"]; const SHOW_AFTER_SPACER = ["config", "developer-tools"];
const computeUrl = (urlPath) => `/${urlPath}`;
const SUPPORT_SCROLL_IF_NEEDED = "scrollIntoViewIfNeeded" in document.body; const SUPPORT_SCROLL_IF_NEEDED = "scrollIntoViewIfNeeded" in document.body;
const SORT_VALUE = { const SORT_VALUE = {
@ -91,22 +92,6 @@ const computePanels = (hass: HomeAssistant): [PanelInfo[], PanelInfo[]] => {
return [beforeSpacer, afterSpacer]; return [beforeSpacer, afterSpacer];
}; };
const renderPanel = (hass, panel) => html`
<a
aria-role="option"
href="${computeUrl(panel.url_path)}"
data-panel="${panel.url_path}"
tabindex="-1"
>
<paper-icon-item>
<ha-icon slot="item-icon" .icon="${panel.icon}"></ha-icon>
<span class="item-text">
${hass.localize(`panel.${panel.title}`) || panel.title}
</span>
</paper-icon-item>
</a>
`;
/* /*
* @appliesMixin LocalizeMixin * @appliesMixin LocalizeMixin
*/ */
@ -116,13 +101,14 @@ class HaSidebar extends LitElement {
@property({ type: Boolean }) public alwaysExpand = false; @property({ type: Boolean }) public alwaysExpand = false;
@property({ type: Boolean, reflect: true }) public expanded = false; @property({ type: Boolean, reflect: true }) public expanded = false;
@property({ type: Boolean, reflect: true }) public expandedWidth = false;
@property() public _defaultPage?: string = @property() public _defaultPage?: string =
localStorage.defaultPage || DEFAULT_PANEL; localStorage.defaultPage || DEFAULT_PANEL;
@property() private _externalConfig?: ExternalConfig; @property() private _externalConfig?: ExternalConfig;
@property() private _notifications?: PersistentNotification[]; @property() private _notifications?: PersistentNotification[];
private _expandTimeout?: number;
private _contractTimeout?: number; private _mouseLeaveTimeout?: number;
private _tooltipHideTimeout?: number;
private _recentKeydownActiveUntil = 0;
protected render() { protected render() {
const hass = this.hass; const hass = this.hass;
@ -148,30 +134,44 @@ class HaSidebar extends LitElement {
? html` ? html`
<paper-icon-button <paper-icon-button
aria-label="Sidebar Toggle" aria-label="Sidebar Toggle"
.icon=${hass.dockedSidebar ? "hass:menu-open" : "hass:menu"} .icon=${hass.dockedSidebar === "docked"
? "hass:menu-open"
: "hass:menu"}
@click=${this._toggleSidebar} @click=${this._toggleSidebar}
></paper-icon-button> ></paper-icon-button>
` `
: ""} : ""}
<span class="title">Home Assistant</span> <span class="title">Home Assistant</span>
</div> </div>
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}> <paper-listbox
<a attr-for-selected="data-panel"
aria-role="option" .selected=${hass.panelUrl}
href="${computeUrl(this._defaultPage)}" @focusin=${this._listboxFocusIn}
data-panel=${this._defaultPage} @focusout=${this._listboxFocusOut}
tabindex="-1" @scroll=${this._listboxScroll}
> @keydown=${this._listboxKeydown}
<paper-icon-item> >
<ha-icon slot="item-icon" icon="hass:apps"></ha-icon> ${this._renderPanel(
<span class="item-text">${hass.localize("panel.states")}</span> this._defaultPage,
</paper-icon-item> "hass:apps",
</a> hass.localize("panel.states")
)}
${beforeSpacer.map((panel) => renderPanel(hass, panel))} ${beforeSpacer.map((panel) =>
this._renderPanel(
panel.url_path,
panel.icon,
hass.localize(`panel.${panel.title}`) || panel.title
)
)}
<div class="spacer" disabled></div> <div class="spacer" disabled></div>
${afterSpacer.map((panel) => renderPanel(hass, panel))} ${afterSpacer.map((panel) =>
this._renderPanel(
panel.url_path,
panel.icon,
hass.localize(`panel.${panel.title}`) || panel.title
)
)}
${this._externalConfig && this._externalConfig.hasSettingsScreen ${this._externalConfig && this._externalConfig.hasSettingsScreen
? html` ? html`
<a <a
@ -181,7 +181,10 @@ class HaSidebar extends LitElement {
tabindex="-1" tabindex="-1"
@click=${this._handleExternalAppConfiguration} @click=${this._handleExternalAppConfiguration}
> >
<paper-icon-item> <paper-icon-item
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
>
<ha-icon <ha-icon
slot="item-icon" slot="item-icon"
icon="hass:cellphone-settings-variant" icon="hass:cellphone-settings-variant"
@ -201,9 +204,11 @@ class HaSidebar extends LitElement {
class="notifications" class="notifications"
aria-role="option" aria-role="option"
@click=${this._handleShowNotificationDrawer} @click=${this._handleShowNotificationDrawer}
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
> >
<ha-icon slot="item-icon" icon="hass:bell"></ha-icon> <ha-icon slot="item-icon" icon="hass:bell"></ha-icon>
${notificationCount > 0 ${!this.expanded && notificationCount > 0
? html` ? html`
<span class="notification-badge" slot="item-icon"> <span class="notification-badge" slot="item-icon">
${notificationCount} ${notificationCount}
@ -213,6 +218,11 @@ class HaSidebar extends LitElement {
<span class="item-text"> <span class="item-text">
${hass.localize("ui.notification_drawer.title")} ${hass.localize("ui.notification_drawer.title")}
</span> </span>
${this.expanded && notificationCount > 0
? html`
<span class="notification-badge">${notificationCount}</span>
`
: ""}
</paper-icon-item> </paper-icon-item>
<a <a
@ -227,7 +237,10 @@ class HaSidebar extends LitElement {
aria-role="option" aria-role="option"
aria-label=${hass.localize("panel.profile")} aria-label=${hass.localize("panel.profile")}
> >
<paper-icon-item> <paper-icon-item
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
>
<ha-user-badge slot="item-icon" .user=${hass.user}></ha-user-badge> <ha-user-badge slot="item-icon" .user=${hass.user}></ha-user-badge>
<span class="item-text"> <span class="item-text">
@ -236,13 +249,13 @@ class HaSidebar extends LitElement {
</paper-icon-item> </paper-icon-item>
</a> </a>
<div disabled class="bottom-spacer"></div> <div disabled class="bottom-spacer"></div>
<div class="tooltip"></div>
`; `;
} }
protected shouldUpdate(changedProps: PropertyValues): boolean { protected shouldUpdate(changedProps: PropertyValues): boolean {
if ( if (
changedProps.has("expanded") || changedProps.has("expanded") ||
changedProps.has("expandedWidth") ||
changedProps.has("narrow") || changedProps.has("narrow") ||
changedProps.has("alwaysExpand") || changedProps.has("alwaysExpand") ||
changedProps.has("_externalConfig") || changedProps.has("_externalConfig") ||
@ -275,21 +288,6 @@ class HaSidebar extends LitElement {
this._externalConfig = conf; this._externalConfig = conf;
}); });
} }
// On tablets, there is no hover. So we receive click and mouseenter at the
// same time. In that case, we're going to cancel expanding, because it is
// going to require another tap outside the sidebar to trigger mouseleave
this.addEventListener("click", () => {
if (this._expandTimeout) {
clearTimeout(this._expandTimeout);
this._expandTimeout = undefined;
}
});
this.addEventListener("mouseenter", () => {
this._expand();
});
this.addEventListener("mouseleave", () => {
this._contract();
});
subscribeNotifications(this.hass.connection, (notifications) => { subscribeNotifications(this.hass.connection, (notifications) => {
this._notifications = notifications; this._notifications = notifications;
}); });
@ -297,9 +295,8 @@ class HaSidebar extends LitElement {
protected updated(changedProps) { protected updated(changedProps) {
super.updated(changedProps); super.updated(changedProps);
if (changedProps.has("alwaysExpand") && this.alwaysExpand) { if (changedProps.has("alwaysExpand")) {
this.expanded = true; this.expanded = this.alwaysExpand;
this.expandedWidth = true;
} }
if (!SUPPORT_SCROLL_IF_NEEDED || !changedProps.has("hass")) { if (!SUPPORT_SCROLL_IF_NEEDED || !changedProps.has("hass")) {
return; return;
@ -314,31 +311,89 @@ class HaSidebar extends LitElement {
} }
} }
private _expand() { private get _tooltip() {
// We debounce it one frame, because on tablets, the mouse-enter and return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement;
// click event fire at the same time.
this._expandTimeout = window.setTimeout(() => {
this.expanded = true;
this.expandedWidth = true;
}, 0);
if (this._contractTimeout) {
clearTimeout(this._contractTimeout);
this._contractTimeout = undefined;
}
} }
private _contract() { private _itemMouseEnter(ev: MouseEvent) {
if (this._expandTimeout) { // On keypresses on the listbox, we're going to ignore mouse enter events
clearTimeout(this._expandTimeout); // for 100ms so that we ignore it when pressing down arrow scrolls the
this._expandTimeout = undefined; // sidebar causing the mouse to hover a new icon
} if (
if (this.alwaysExpand) { this.expanded ||
new Date().getTime() < this._recentKeydownActiveUntil
) {
return; return;
} }
this.expandedWidth = false; if (this._mouseLeaveTimeout) {
this._contractTimeout = window.setTimeout(() => { clearTimeout(this._mouseLeaveTimeout);
this.expanded = this.alwaysExpand || false; this._mouseLeaveTimeout = undefined;
}, 400); }
this._showTooltip(ev.currentTarget as PaperIconItemElement);
}
private _itemMouseLeave() {
if (this._mouseLeaveTimeout) {
clearTimeout(this._mouseLeaveTimeout);
}
this._mouseLeaveTimeout = window.setTimeout(() => {
this._hideTooltip();
}, 500);
}
private _listboxFocusIn(ev) {
if (this.expanded || ev.target.nodeName !== "A") {
return;
}
this._showTooltip(ev.target.querySelector("paper-icon-item"));
}
private _listboxFocusOut() {
this._hideTooltip();
}
@eventOptions({
passive: true,
})
private _listboxScroll() {
// On keypresses on the listbox, we're going to ignore scroll events
// for 100ms so that if pressing down arrow scrolls the sidebar, the tooltip
// will not be hidden.
if (new Date().getTime() < this._recentKeydownActiveUntil) {
return;
}
this._hideTooltip();
}
private _listboxKeydown() {
this._recentKeydownActiveUntil = new Date().getTime() + 100;
}
private _showTooltip(item: PaperIconItemElement) {
if (this._tooltipHideTimeout) {
clearTimeout(this._tooltipHideTimeout);
this._tooltipHideTimeout = undefined;
}
const tooltip = this._tooltip;
const listbox = this.shadowRoot!.querySelector("paper-listbox")!;
let top = item.offsetTop + 7;
if (listbox.contains(item)) {
top -= listbox.scrollTop;
}
tooltip.innerHTML = item.querySelector(".item-text")!.innerHTML;
tooltip.style.display = "block";
tooltip.style.top = `${top}px`;
tooltip.style.left = `${item.offsetLeft + item.clientWidth + 12}px`;
}
private _hideTooltip() {
// Delay it a little in case other events are pending processing.
if (!this._tooltipHideTimeout) {
this._tooltipHideTimeout = window.setTimeout(() => {
this._tooltipHideTimeout = undefined;
this._tooltip.style.display = "none";
}, 10);
}
} }
private _handleShowNotificationDrawer() { private _handleShowNotificationDrawer() {
@ -356,6 +411,25 @@ class HaSidebar extends LitElement {
fireEvent(this, "hass-toggle-menu"); fireEvent(this, "hass-toggle-menu");
} }
private _renderPanel(urlPath, icon, title) {
return html`
<a
aria-role="option"
href="${`/${urlPath}`}"
data-panel="${urlPath}"
tabindex="-1"
>
<paper-icon-item
@mouseenter=${this._itemMouseEnter}
@mouseleave=${this._itemMouseLeave}
>
<ha-icon slot="item-icon" .icon="${icon}"></ha-icon>
<span class="item-text">${title}</span>
</paper-icon-item>
</a>
`;
}
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
:host { :host {
@ -366,17 +440,10 @@ class HaSidebar extends LitElement {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
border-right: 1px solid var(--divider-color); border-right: 1px solid var(--divider-color);
background-color: var( background-color: var(--sidebar-background-color);
--sidebar-background-color,
var(--primary-background-color)
);
width: 64px; width: 64px;
transition: width 0.2s ease-in;
will-change: width;
contain: strict;
transition-delay: 0.2s;
} }
:host([expandedwidth]) { :host([expanded]) {
width: 256px; width: 256px;
} }
@ -512,6 +579,9 @@ class HaSidebar extends LitElement {
.notifications { .notifications {
cursor: pointer; cursor: pointer;
} }
.notifications .item-text {
flex: 1;
}
.profile { .profile {
} }
.profile paper-icon-item { .profile paper-icon-item {
@ -522,19 +592,22 @@ class HaSidebar extends LitElement {
} }
.notification-badge { .notification-badge {
position: absolute; min-width: 20px;
font-weight: 400; box-sizing: border-box;
bottom: 14px;
left: 26px;
border-radius: 50%; border-radius: 50%;
font-weight: 400;
background-color: var(--primary-color); background-color: var(--primary-color);
height: 20px;
line-height: 20px; line-height: 20px;
text-align: center; text-align: center;
padding: 0px 6px; padding: 0px 6px;
font-size: 0.65em;
color: var(--text-primary-color); color: var(--text-primary-color);
} }
ha-icon + .notification-badge {
position: absolute;
bottom: 14px;
left: 26px;
font-size: 0.65em;
}
.spacer { .spacer {
flex: 1; flex: 1;
@ -561,6 +634,18 @@ class HaSidebar extends LitElement {
.dev-tools a { .dev-tools a {
color: var(--sidebar-icon-color); color: var(--sidebar-icon-color);
} }
.tooltip {
display: none;
position: absolute;
opacity: 0.9;
border-radius: 2px;
white-space: nowrap;
color: var(--sidebar-background-color);
background-color: var(--sidebar-text-color);
padding: 4px;
font-weight: 500;
}
`; `;
} }
} }

View File

@ -49,3 +49,16 @@ export const CLIMATE_SUPPORT_FAN_MODE = 8;
export const CLIMATE_SUPPORT_PRESET_MODE = 16; export const CLIMATE_SUPPORT_PRESET_MODE = 16;
export const CLIMATE_SUPPORT_SWING_MODE = 32; export const CLIMATE_SUPPORT_SWING_MODE = 32;
export const CLIMATE_SUPPORT_AUX_HEAT = 64; export const CLIMATE_SUPPORT_AUX_HEAT = 64;
const hvacModeOrdering: { [key in HvacMode]: number } = {
auto: 1,
heat_cool: 2,
heat: 3,
cool: 4,
dry: 5,
fan_only: 6,
off: 7,
};
export const compareClimateHvacModes = (mode1: HvacMode, mode2: HvacMode) =>
hvacModeOrdering[mode1] - hvacModeOrdering[mode2];

View File

@ -29,6 +29,7 @@ import {
CLIMATE_SUPPORT_SWING_MODE, CLIMATE_SUPPORT_SWING_MODE,
CLIMATE_SUPPORT_AUX_HEAT, CLIMATE_SUPPORT_AUX_HEAT,
CLIMATE_SUPPORT_PRESET_MODE, CLIMATE_SUPPORT_PRESET_MODE,
compareClimateHvacModes,
} from "../../../data/climate"; } from "../../../data/climate";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
@ -176,13 +177,16 @@ class MoreInfoClimate extends LitElement {
.selected=${stateObj.state} .selected=${stateObj.state}
@selected-changed=${this._handleOperationmodeChanged} @selected-changed=${this._handleOperationmodeChanged}
> >
${stateObj.attributes.hvac_modes.map( ${stateObj.attributes.hvac_modes
(mode) => html` .concat()
<paper-item item-name=${mode}> .sort(compareClimateHvacModes)
${hass.localize(`state.climate.${mode}`)} .map(
</paper-item> (mode) => html`
` <paper-item item-name=${mode}>
)} ${hass.localize(`state.climate.${mode}`)}
</paper-item>
`
)}
</paper-listbox> </paper-listbox>
</ha-paper-dropdown-menu> </ha-paper-dropdown-menu>
</div> </div>
@ -202,11 +206,6 @@ class MoreInfoClimate extends LitElement {
.selected=${stateObj.attributes.preset_mode} .selected=${stateObj.attributes.preset_mode}
@selected-changed=${this._handlePresetmodeChanged} @selected-changed=${this._handlePresetmodeChanged}
> >
<paper-item item-name="">
${hass.localize(
`state_attributes.climate.preset_mode.none`
)}
</paper-item>
${stateObj.attributes.preset_modes!.map( ${stateObj.attributes.preset_modes!.map(
(mode) => html` (mode) => html`
<paper-item item-name=${mode}> <paper-item item-name=${mode}>

View File

@ -159,7 +159,7 @@ export const provideHass = (
localize: () => "", localize: () => "",
translationMetadata: translationMetadata as any, translationMetadata: translationMetadata as any,
dockedSidebar: false, dockedSidebar: "auto",
moreInfoEntityId: null as any, moreInfoEntityId: null as any,
async callService(domain, service, data) { async callService(domain, service, data) {
if (data && "entity_id" in data) { if (data && "entity_id" in data) {

View File

@ -44,8 +44,11 @@ class HomeAssistantMain extends LitElement {
return; return;
} }
const sidebarNarrow =
this.narrow || this.hass.dockedSidebar === "always_hidden";
const disableSwipe = const disableSwipe =
!this.narrow || NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1; !sidebarNarrow || NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
return html` return html`
<iron-media-query <iron-media-query
@ -55,7 +58,7 @@ class HomeAssistantMain extends LitElement {
<app-drawer-layout <app-drawer-layout
fullbleed fullbleed
.forceNarrow=${this.narrow} .forceNarrow=${sidebarNarrow}
responsive-width="0" responsive-width="0"
> >
<app-drawer <app-drawer
@ -64,12 +67,14 @@ class HomeAssistantMain extends LitElement {
slot="drawer" slot="drawer"
.disableSwipe=${disableSwipe} .disableSwipe=${disableSwipe}
.swipeOpen=${!disableSwipe} .swipeOpen=${!disableSwipe}
.persistent=${!this.narrow} .persistent=${!this.narrow &&
this.hass.dockedSidebar !== "always_hidden"}
> >
<ha-sidebar <ha-sidebar
.hass=${hass} .hass=${hass}
.narrow=${this.narrow} .narrow=${sidebarNarrow}
.alwaysExpand=${this.narrow || hass.dockedSidebar} .alwaysExpand=${sidebarNarrow ||
this.hass.dockedSidebar === "docked"}
></ha-sidebar> ></ha-sidebar>
</app-drawer> </app-drawer>
@ -86,7 +91,7 @@ class HomeAssistantMain extends LitElement {
import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar"); import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar");
this.addEventListener("hass-toggle-menu", () => { this.addEventListener("hass-toggle-menu", () => {
if (this.narrow) { if (this.narrow || this.hass.dockedSidebar === "always_hidden") {
if (this.drawer.opened) { if (this.drawer.opened) {
this.drawer.close(); this.drawer.close();
} else { } else {
@ -94,7 +99,7 @@ class HomeAssistantMain extends LitElement {
} }
} else { } else {
fireEvent(this, "hass-dock-sidebar", { fireEvent(this, "hass-dock-sidebar", {
dock: !this.hass.dockedSidebar, dock: this.hass.dockedSidebar === "auto" ? "docked" : "auto",
}); });
setTimeout(() => this.appLayout.resetLayout()); setTimeout(() => this.appLayout.resetLayout());
} }
@ -110,7 +115,10 @@ class HomeAssistantMain extends LitElement {
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
super.updated(changedProps); super.updated(changedProps);
this.toggleAttribute("expanded", this.narrow || this.hass.dockedSidebar); this.toggleAttribute(
"expanded",
this.narrow || this.hass.dockedSidebar !== "auto"
);
if (changedProps.has("route") && this.narrow) { if (changedProps.has("route") && this.narrow) {
this.drawer.close(); this.drawer.close();

View File

@ -146,7 +146,8 @@ class HaPanelConfig extends HassRouterPage {
const showAdvanced = !!( const showAdvanced = !!(
this._coreUserData && this._coreUserData.showAdvanced this._coreUserData && this._coreUserData.showAdvanced
); );
const isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide; const isWide =
this.hass.dockedSidebar === "docked" ? this._wideSidebar : this._wide;
if ("setProperties" in el) { if ("setProperties" in el) {
// As long as we have Polymer panels // As long as we have Polymer panels

View File

@ -2,7 +2,6 @@ import "../../../layouts/hass-loading-screen";
import { customElement, property } from "lit-element"; import { customElement, property } from "lit-element";
import { listenMediaQuery } from "../../../common/dom/media_query";
import { import {
HassRouterPage, HassRouterPage,
RouterOptions, RouterOptions,
@ -12,8 +11,7 @@ import { HomeAssistant } from "../../../types";
@customElement("zha-config-panel") @customElement("zha-config-panel")
class ZHAConfigPanel extends HassRouterPage { class ZHAConfigPanel extends HassRouterPage {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public _wideSidebar: boolean = false; @property() public isWide!: boolean;
@property() public _wide: boolean = false;
protected routerOptions: RouterOptions = { protected routerOptions: RouterOptions = {
defaultPage: "configuration", defaultPage: "configuration",
@ -33,33 +31,10 @@ class ZHAConfigPanel extends HassRouterPage {
}, },
}; };
private _listeners: Array<() => void> = [];
public connectedCallback(): void {
super.connectedCallback();
this._listeners.push(
listenMediaQuery("(min-width: 1040px)", (matches) => {
this._wide = matches;
})
);
this._listeners.push(
listenMediaQuery("(min-width: 1296px)", (matches) => {
this._wideSidebar = matches;
})
);
}
public disconnectedCallback(): void {
super.disconnectedCallback();
while (this._listeners.length) {
this._listeners.pop()!();
}
}
protected updatePageEl(el): void { protected updatePageEl(el): void {
el.route = this.routeTail; el.route = this.routeTail;
el.hass = this.hass; el.hass = this.hass;
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide; el.isWide = this.isWide;
} }
} }

View File

@ -23,7 +23,11 @@ import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand"
import { UNIT_F } from "../../../common/const"; import { UNIT_F } from "../../../common/const";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { ThermostatCardConfig } from "./types"; import { ThermostatCardConfig } from "./types";
import { ClimateEntity, HvacMode } from "../../../data/climate"; import {
ClimateEntity,
HvacMode,
compareClimateHvacModes,
} from "../../../data/climate";
const thermostatConfig = { const thermostatConfig = {
radius: 150, radius: 150,
@ -144,7 +148,13 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
<div class="climate-info"> <div class="climate-info">
<div id="set-temperature"></div> <div id="set-temperature"></div>
<div class="current-mode"> <div class="current-mode">
${this.hass!.localize(`state.climate.${stateObj.state}`)} ${stateObj.attributes.hvac_action
? this.hass!.localize(
`state_attributes.climate.hvac_action.${
stateObj.attributes.hvac_action
}`
)
: this.hass!.localize(`state.climate.${stateObj.state}`)}
${stateObj.attributes.preset_mode ${stateObj.attributes.preset_mode
? html` ? html`
- -
@ -157,9 +167,10 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
: ""} : ""}
</div> </div>
<div class="modes"> <div class="modes">
${stateObj.attributes.hvac_modes.map((modeItem) => ${stateObj.attributes.hvac_modes
this._renderIcon(modeItem, mode) .concat()
)} .sort(compareClimateHvacModes)
.map((modeItem) => this._renderIcon(modeItem, mode))}
</div> </div>
</div> </div>
</div> </div>

View File

@ -178,7 +178,8 @@ class LovelacePanel extends LitElement {
// Do -1 column if the menu is docked and open // Do -1 column if the menu is docked and open
this._columns = Math.max( this._columns = Math.max(
1, 1,
matchColumns - Number(!this.narrow && this.hass!.dockedSidebar) matchColumns -
Number(!this.narrow && this.hass!.dockedSidebar === "docked")
); );
} }

View File

@ -0,0 +1,52 @@
import {
LitElement,
TemplateResult,
html,
property,
customElement,
} from "lit-element";
import "@polymer/paper-toggle-button/paper-toggle-button";
import "./ha-settings-row";
import { HomeAssistant } from "../../types";
import { fireEvent } from "../../common/dom/fire_event";
import { PolymerChangedEvent } from "../../polymer-types";
@customElement("ha-force-narrow-row")
class HaPushNotificationsRow extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow!: boolean;
protected render(): TemplateResult | void {
return html`
<ha-settings-row .narrow=${this.narrow}>
<span slot="heading">
${this.hass.localize("ui.panel.profile.force_narrow.header")}
</span>
<span slot="description">
${this.hass.localize("ui.panel.profile.force_narrow.description")}
</span>
<paper-toggle-button
.checked=${this.hass.dockedSidebar === "always_hidden"}
@checked-changed=${this._checkedChanged}
></paper-toggle-button>
</ha-settings-row>
`;
}
private async _checkedChanged(ev: PolymerChangedEvent<boolean>) {
const newValue = ev.detail.value;
if (newValue === (this.hass.dockedSidebar === "always_hidden")) {
return;
}
fireEvent(this, "hass-dock-sidebar", {
dock: newValue ? "always_hidden" : "auto",
});
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-force-narrow-row": HaPushNotificationsRow;
}
}

View File

@ -25,6 +25,7 @@ import "./ha-long-lived-access-tokens-card";
import "./ha-pick-language-row"; import "./ha-pick-language-row";
import "./ha-pick-theme-row"; import "./ha-pick-theme-row";
import "./ha-push-notifications-row"; import "./ha-push-notifications-row";
import "./ha-force-narrow-row";
/* /*
* @appliesMixin EventsMixin * @appliesMixin EventsMixin
@ -83,6 +84,15 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
narrow="[[narrow]]" narrow="[[narrow]]"
hass="[[hass]]" hass="[[hass]]"
></ha-pick-theme-row> ></ha-pick-theme-row>
<template
is="dom-if"
if="[[_showNarrowRow(hass.dockedSidebar, narrow)]]"
>
<ha-force-narrow-row
narrow="[[narrow]]"
hass="[[hass]]"
></ha-force-narrow-row>
</template>
<ha-push-notifications-row <ha-push-notifications-row
narrow="[[narrow]]" narrow="[[narrow]]"
hass="[[hass]]" hass="[[hass]]"
@ -174,6 +184,10 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
_isAdmin(user) { _isAdmin(user) {
return user.is_admin; return user.is_admin;
} }
_showNarrowRow(dockedSidebar, narrow) {
return dockedSidebar === "auto" ? !narrow : true;
}
} }
customElements.define("ha-panel-profile", HaPanelProfile); customElements.define("ha-panel-profile", HaPanelProfile);

View File

@ -237,7 +237,7 @@ class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
// Do -1 column if the menu is docked and open // Do -1 column if the menu is docked and open
this._columns = Math.max( this._columns = Math.max(
1, 1,
matchColumns - (!this.narrow && this.hass.dockedSidebar) matchColumns - (!this.narrow && this.hass.dockedSidebar === "docked")
); );
} }

View File

@ -44,7 +44,7 @@ export const connectionMixin = (
localize: () => "", localize: () => "",
translationMetadata, translationMetadata,
dockedSidebar: true, dockedSidebar: "docked",
moreInfoEntityId: null, moreInfoEntityId: null,
callService: async (domain, service, serviceData = {}) => { callService: async (domain, service, serviceData = {}) => {
if (__DEV__) { if (__DEV__) {

View File

@ -2,9 +2,10 @@ import { storeState } from "../util/ha-pref-storage";
import { Constructor, LitElement } from "lit-element"; import { Constructor, LitElement } from "lit-element";
import { HassBaseEl } from "./hass-base-mixin"; import { HassBaseEl } from "./hass-base-mixin";
import { HASSDomEvent } from "../common/dom/fire_event"; import { HASSDomEvent } from "../common/dom/fire_event";
import { HomeAssistant } from "../types";
interface DockSidebarParams { interface DockSidebarParams {
dock: boolean; dock: HomeAssistant["dockedSidebar"];
} }
declare global { declare global {

View File

@ -323,6 +323,14 @@
"home": "Home", "home": "Home",
"sleep": "Sleep", "sleep": "Sleep",
"activity": "Activity" "activity": "Activity"
},
"hvac_action": {
"off": "Off",
"heating": "Heating",
"cooling": "Cooling",
"drying": "Drying",
"idle": "Idle",
"fan": "Fan"
} }
} }
}, },
@ -1044,6 +1052,10 @@
"current_user": "You are currently logged in as {fullName}.", "current_user": "You are currently logged in as {fullName}.",
"is_owner": "You are an owner.", "is_owner": "You are an owner.",
"logout": "Log out", "logout": "Log out",
"force_narrow": {
"header": "Always hide the sidebar",
"description": "This will hide the sidebar by default, similar to the mobile experience."
},
"push_notifications": { "push_notifications": {
"header": "Push Notifications", "header": "Push Notifications",
"description": "Send notifications to this device.", "description": "Send notifications to this device.",

View File

@ -139,7 +139,7 @@ export interface HomeAssistant {
localize: LocalizeFunc; localize: LocalizeFunc;
translationMetadata: TranslationMetadata; translationMetadata: TranslationMetadata;
dockedSidebar: boolean; dockedSidebar: "docked" | "always_hidden" | "auto";
moreInfoEntityId: string | null; moreInfoEntityId: string | null;
user?: CurrentUser; user?: CurrentUser;
callService: ( callService: (

View File

@ -19,7 +19,12 @@ export function getState() {
for (const key of STORED_STATE) { for (const key of STORED_STATE) {
if (key in STORAGE) { if (key in STORAGE) {
state[key] = JSON.parse(STORAGE[key]); let value = JSON.parse(STORAGE[key]);
// dockedSidebar went from boolean to enum on 20190720
if (key === "dockedSidebar" && typeof value === "boolean") {
value = value ? "docked" : "auto";
}
state[key] = value;
} }
} }

View File

@ -141,7 +141,8 @@
"high_demand": "Høj efterspørgsel", "high_demand": "Høj efterspørgsel",
"heat_pump": "Varmepumpe", "heat_pump": "Varmepumpe",
"gas": "Gas", "gas": "Gas",
"manual": "Manual" "manual": "Manual",
"heat_cool": "Varme\/køl"
}, },
"configurator": { "configurator": {
"configure": "Konfigurer", "configure": "Konfigurer",
@ -613,6 +614,14 @@
"add_node": "Tilføj node", "add_node": "Tilføj node",
"remove_node": "Fjern node", "remove_node": "Fjern node",
"cancel_command": "Annuller kommando" "cancel_command": "Annuller kommando"
},
"common": {
"value": "Værdi",
"instance": "Instans",
"index": "Indeks"
},
"values": {
"header": "Node værdier"
} }
}, },
"users": { "users": {

View File

@ -816,7 +816,11 @@
"close": "Close", "close": "Close",
"submit": "Submit" "submit": "Submit"
}, },
"logout": "Log out" "logout": "Log out",
"force_narrow": {
"header": "Always hide the sidebar",
"description": "This will hide the sidebar by default, similar to the mobile experience."
}
}, },
"page-authorize": { "page-authorize": {
"initializing": "Initializing", "initializing": "Initializing",
@ -1328,6 +1332,14 @@
"home": "Home", "home": "Home",
"sleep": "Sleep", "sleep": "Sleep",
"activity": "Activity" "activity": "Activity"
},
"hvac_action": {
"off": "Off",
"heating": "Heating",
"cooling": "Cooling",
"drying": "Drying",
"idle": "Idle",
"fan": "Fan"
} }
} }
}, },

View File

@ -20,7 +20,7 @@
"unavailable": "Indisponible" "unavailable": "Indisponible"
}, },
"alarm_control_panel": { "alarm_control_panel": {
"armed": "Enclenchée", "armed": "Activé",
"disarmed": "Désactivée", "disarmed": "Désactivée",
"armed_home": "Enclenchée (présent)", "armed_home": "Enclenchée (présent)",
"armed_away": "Enclenchée (absent)", "armed_away": "Enclenchée (absent)",
@ -591,8 +591,20 @@
"zwave": { "zwave": {
"caption": "Z-Wave", "caption": "Z-Wave",
"description": "Gérez votre réseau Z-Wave", "description": "Gérez votre réseau Z-Wave",
"network_status": {
"network_stopped": "Réseau Z-Wave arrêté",
"network_starting": "Démarrage du réseau Z-Wave...",
"network_starting_note": "Ceci peut prendre un certain temps en fonction du débit de votre réseau.",
"network_started": "Le réseau Z-Wave a été démarré"
},
"services": { "services": {
"save_config": "Enregistrer la configuration" "start_network": "Démarrer le réseau",
"stop_network": "Arrêter le réseau",
"test_network": "Tester le réseau",
"save_config": "Enregistrer la configuration",
"add_node_secure": "Ajouter un nœud sécurisé",
"add_node": "Ajouter un nœud",
"remove_node": "Supprimer un nœud"
} }
}, },
"users": { "users": {

View File

@ -8,7 +8,7 @@
"mailbox": "메일함", "mailbox": "메일함",
"shopping_list": "장보기목록", "shopping_list": "장보기목록",
"dev-info": "정보", "dev-info": "정보",
"developer_tools": "개발자도구", "developer_tools": "개발자 도구",
"calendar": "캘린더", "calendar": "캘린더",
"profile": "프로필" "profile": "프로필"
}, },
@ -614,6 +614,14 @@
"add_node": "노드 추가", "add_node": "노드 추가",
"remove_node": "노드 제거", "remove_node": "노드 제거",
"cancel_command": "명령 취소" "cancel_command": "명령 취소"
},
"common": {
"value": "값",
"instance": "인스턴스",
"index": "색인"
},
"values": {
"header": "노드 값"
} }
}, },
"users": { "users": {
@ -642,7 +650,7 @@
"caption": "Home Assistant Cloud", "caption": "Home Assistant Cloud",
"description_login": "{email} 로 로그인 되어있습니다", "description_login": "{email} 로 로그인 되어있습니다",
"description_not_login": "로그인이 되어있지 않습니다", "description_not_login": "로그인이 되어있지 않습니다",
"description_features": "Alexa 및 Google Assistant 를 통해 집 밖에서도 집을 관리합니다." "description_features": "Alexa 및 Google Assistant 를 통해 집 밖에서도 집을 관리하세요."
}, },
"integrations": { "integrations": {
"caption": "통합 구성요소", "caption": "통합 구성요소",
@ -1316,7 +1324,7 @@
"eco": "절전", "eco": "절전",
"away": "외출", "away": "외출",
"boost": "부스트", "boost": "부스트",
"comfort": "안락", "comfort": "쾌적",
"home": "재실", "home": "재실",
"sleep": "수면", "sleep": "수면",
"activity": "활동" "activity": "활동"

View File

@ -354,6 +354,19 @@
"introduction": "Pārvaldiet Home Assistant serveri no Home Assistant saskarnes.", "introduction": "Pārvaldiet Home Assistant serveri no Home Assistant saskarnes.",
"restart": "Restartēt", "restart": "Restartēt",
"stop": "Apturēt" "stop": "Apturēt"
},
"core_config": {
"edit_requires_storage": "Redaktors ir atspējots, jo konfigurācija ir definēta configuration.yaml.",
"location_name": "Jūsu Home Assistant instalācijas nosaukums",
"elevation": "Augstums",
"elevation_meters": "metri",
"time_zone": "Laika josla",
"unit_system": "Mērvienību sistēma",
"unit_system_imperial": "Imperiālā",
"unit_system_metric": "Metriskā",
"imperial_example": "Fārenheita, mārciņas",
"metric_example": "Celsija, kilogrami",
"save_button": "Saglabāt"
} }
} }
} }
@ -367,20 +380,21 @@
"description": "Veidojiet un rediģējiet automatizācijas", "description": "Veidojiet un rediģējiet automatizācijas",
"picker": { "picker": {
"header": "Automatizāciju redaktors", "header": "Automatizāciju redaktors",
"introduction": "Automatizācijas redaktors ļauj jums izveidot un rediģēt automatizācijas. Lūdzu, sekojiet [norādījumiem] (https:\/\/home-assistant.io\/docs\/automation\/editor\/), lai pārliecinātos, ka esat pareizi konfigurējis Home Assistant.", "introduction": "Automatizācijas redaktors ļauj jums izveidot un rediģēt automatizācijas. Lūdzu, sekojiet saitei zemāk, lai pārliecinātos, ka esat pareizi konfigurējis Home Assistant.",
"pick_automation": "Izvēlieties automatizāciju rediģēšanai", "pick_automation": "Izvēlieties automatizāciju rediģēšanai",
"no_automations": "Mēs nevarējām atrast rediģējamas automatizācijas", "no_automations": "Mēs nevarējām atrast rediģējamas automatizācijas",
"add_automation": "Pievienot automatizāciju" "add_automation": "Pievienot automatizāciju",
"learn_more": "Uzziniet vairāk par automatizācijām"
}, },
"editor": { "editor": {
"introduction": "Lietojiet automatizācijas, lai ienestu dzīvību Jūsu mājās", "introduction": "Lietojiet automatizācijas, lai iedzīvinātu Jūsu mājās",
"default_name": "Jauna Automatizācija", "default_name": "Jauna automatizācija",
"save": "Saglabāt", "save": "Saglabāt",
"unsaved_confirm": "Jums ir nesaglabātas izmaiņas. Vai tiešām vēlaties pamest?", "unsaved_confirm": "Jums ir nesaglabātas izmaiņas. Vai tiešām vēlaties pamest?",
"alias": "Nosaukums", "alias": "Nosaukums",
"triggers": { "triggers": {
"header": "Trigeri", "header": "Trigeri",
"introduction": "Trigeri ir tas, kas sāk automatizācijas noteikuma apstrādi. Vienam noteikumam ir iespējams norādīt vairākus trigerus. Kad trigeris sāk, Home Assistant apstiprina nosacījumus, ja tādi ir, un izsauc darbību. \n\n [Uzziniet vairāk par trigeriem.] (https:\/\/home-assistant.io\/docs\/automation\/trigger\/)", "introduction": "Trigeri ir tas, kas sāk automatizācijas noteikuma apstrādi. Vienam noteikumam ir iespējams norādīt vairākus trigerus. Kad trigeris sāk, Home Assistant apstiprina nosacījumus, ja tādi ir, un izsauc darbību.",
"add": "Pievienot trigeri", "add": "Pievienot trigeri",
"duplicate": "Dublicēt", "duplicate": "Dublicēt",
"delete": "Dzēst", "delete": "Dzēst",
@ -452,11 +466,12 @@
"geo_location": { "geo_location": {
"label": "Ģeogrāfiskā atrašanās vieta" "label": "Ģeogrāfiskā atrašanās vieta"
} }
} },
"learn_more": "Uzziniet vairāk par trigeriem"
}, },
"conditions": { "conditions": {
"header": "Nosacījumi", "header": "Nosacījumi",
"introduction": "Nosacījumi ir automatizācijas noteikuma izvēles daļa, un to var izmantot, lai novērstu darbību rašanos. Nosacījumi izskatās ļoti līdzīgi trigeriem, taču tie ir ļoti atšķirīgi. Trigeri izskatīs notikumus, kas notiek sistēmā, bet nosacījums tikai aplūko, kā sistēma šobrīd izskatās. Trigeri var novērot, ka slēdzis tiek ieslēgts. Nosacījums var redzēt tikai to, vai slēdzis pašlaik ir ieslēgts vai izslēgts. \n\n [Uzziniet vairāk par nosacījumiem.] (https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)", "introduction": "Nosacījumi ir automatizācijas noteikuma izvēles daļa, un to var izmantot, lai novērstu darbību rašanos. Nosacījumi izskatās ļoti līdzīgi trigeriem, taču tie ir ļoti atšķirīgi. Trigeri izskatīs notikumus, kas notiek sistēmā, bet nosacījums tikai aplūko, kā sistēma šobrīd izskatās. Trigeri var novērot, ka slēdzis tiek ieslēgts. Nosacījums var redzēt tikai to, vai slēdzis pašlaik ir ieslēgts vai izslēgts.",
"add": "Pievienot nosacījumu", "add": "Pievienot nosacījumu",
"duplicate": "Duplicēt", "duplicate": "Duplicēt",
"delete": "Dzēst", "delete": "Dzēst",
@ -497,11 +512,12 @@
"entity": "Vienība ar atrašanos vietu", "entity": "Vienība ar atrašanos vietu",
"zone": "Zona" "zone": "Zona"
} }
} },
"learn_more": "Uzziniet vairāk par nosacījumiem"
}, },
"actions": { "actions": {
"header": "Darbības", "header": "Darbības",
"introduction": "Darbības ir tādas, kādas Home Assistant veiks, kad tiks aktivizēta automatizācija. \n\n [Uzziniet vairāk par darbībām.] (https:\/\/home-assistant.io\/docs\/automation\/action\/)", "introduction": "Darbības nosaka, ko Home Assistant darīs, kad tiks aktivizēta automatizācija.",
"add": "Pievienot darbību", "add": "Pievienot darbību",
"duplicate": "Dublicēt", "duplicate": "Dublicēt",
"delete": "Dzēst", "delete": "Dzēst",
@ -530,7 +546,8 @@
"event": "Notikums:", "event": "Notikums:",
"service_data": "Pakalpojuma dati" "service_data": "Pakalpojuma dati"
} }
} },
"learn_more": "Uzziniet vairāk par darbībām"
} }
} }
}, },
@ -540,7 +557,10 @@
}, },
"zwave": { "zwave": {
"caption": "Z-Wave", "caption": "Z-Wave",
"description": "Pārvaldiet Z-Wave tīklu" "description": "Pārvaldiet Z-Wave tīklu",
"services": {
"save_config": "Saglabāt konfigurāciju"
}
}, },
"users": { "users": {
"caption": "Lietotāji", "caption": "Lietotāji",
@ -630,7 +650,7 @@
}, },
"long_lived_access_tokens": { "long_lived_access_tokens": {
"header": "Ilgtermiņas piekļuves pilnvaras", "header": "Ilgtermiņas piekļuves pilnvaras",
"description": "Izveidojiet ilgtermiņa piekļuves pilnvaras, lai ļautu jūsu skriptiem mijiedarboties ar jūsu Home Assistant instanci. Katra pilnvara būs derīga 10 gadus pēc tās radīšanas. Pašlaik ir aktīvi šādas ilgstošas piekļuves pilvaras.", "description": "Izveidojiet ilgtermiņa piekļuves pilnvaras, lai ļautu jūsu skriptiem mijiedarboties ar jūsu Home Assistant instanci. Katra pilnvara būs derīga 10 gadus pēc tās radīšanas. Pašlaik ir aktīvas sekojošas ilgstošas piekļuves pilvaras.",
"learn_auth_requests": "Uzziniet, kā veikt autentificētus pieprasījumus.", "learn_auth_requests": "Uzziniet, kā veikt autentificētus pieprasījumus.",
"created_at": "Izveidots {date}", "created_at": "Izveidots {date}",
"confirm_delete": "Vai tiešām vēlaties dzēst piekļuves pilnvaru {name}?", "confirm_delete": "Vai tiešām vēlaties dzēst piekļuves pilnvaru {name}?",
@ -665,7 +685,8 @@
"step_done": "Iestatīšana ir pabeigta {step}", "step_done": "Iestatīšana ir pabeigta {step}",
"close": "Aizvērt", "close": "Aizvērt",
"submit": "Iesniegt" "submit": "Iesniegt"
} },
"logout": "Iziet"
}, },
"page-authorize": { "page-authorize": {
"initializing": "Inicializēšana", "initializing": "Inicializēšana",
@ -836,6 +857,13 @@
"help": "Palīdzība", "help": "Palīdzība",
"refresh": "Atsvaidzināt" "refresh": "Atsvaidzināt"
} }
},
"page-demo": {
"cards": {
"demo": {
"learn_more": "Uzziniet vairāk par Home Assistant"
}
}
} }
}, },
"sidebar": { "sidebar": {

View File

@ -614,6 +614,14 @@
"add_node": "Dodaj węzeł", "add_node": "Dodaj węzeł",
"remove_node": "Usuń węzeł", "remove_node": "Usuń węzeł",
"cancel_command": "Anuluj polecenie" "cancel_command": "Anuluj polecenie"
},
"common": {
"value": "Wartość",
"instance": "Instancja",
"index": "Indeks"
},
"values": {
"header": "Wartości węzła"
} }
}, },
"users": { "users": {

View File

@ -614,6 +614,14 @@
"add_node": "Добавить узел", "add_node": "Добавить узел",
"remove_node": "Удалить узел", "remove_node": "Удалить узел",
"cancel_command": "Отменить команду" "cancel_command": "Отменить команду"
},
"common": {
"value": "Значение",
"instance": "Экземпляр",
"index": "Индекс"
},
"values": {
"header": "Значения узлов"
} }
}, },
"users": { "users": {