mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
commit
18df636573
@ -15,6 +15,7 @@ import { mockTemplate } from "./stubs/template";
|
|||||||
import { mockEvents } from "./stubs/events";
|
import { mockEvents } from "./stubs/events";
|
||||||
import { mockMediaPlayer } from "./stubs/media_player";
|
import { mockMediaPlayer } from "./stubs/media_player";
|
||||||
import { HomeAssistant } from "../../src/types";
|
import { HomeAssistant } from "../../src/types";
|
||||||
|
import { mockFrontend } from "./stubs/frontend";
|
||||||
|
|
||||||
class HaDemo extends HomeAssistantAppEl {
|
class HaDemo extends HomeAssistantAppEl {
|
||||||
protected async _handleConnProm() {
|
protected async _handleConnProm() {
|
||||||
@ -35,6 +36,7 @@ class HaDemo extends HomeAssistantAppEl {
|
|||||||
mockTemplate(hass);
|
mockTemplate(hass);
|
||||||
mockEvents(hass);
|
mockEvents(hass);
|
||||||
mockMediaPlayer(hass);
|
mockMediaPlayer(hass);
|
||||||
|
mockFrontend(hass);
|
||||||
selectedDemoConfig.then((conf) => {
|
selectedDemoConfig.then((conf) => {
|
||||||
hass.addEntities(conf.entities());
|
hass.addEntities(conf.entities());
|
||||||
if (conf.theme) {
|
if (conf.theme) {
|
||||||
|
7
demo/src/stubs/frontend.ts
Normal file
7
demo/src/stubs/frontend.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
export const mockFrontend = (hass: MockHomeAssistant) => {
|
||||||
|
hass.mockWS("frontend/get_user_data", () => ({
|
||||||
|
value: null,
|
||||||
|
}));
|
||||||
|
};
|
@ -60,11 +60,7 @@ class HassioAddonView extends PolymerElement {
|
|||||||
<app-header-layout has-scrolling-region="">
|
<app-header-layout has-scrolling-region="">
|
||||||
<app-header fixed="" slot="header">
|
<app-header fixed="" slot="header">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button hassio></ha-menu-button>
|
||||||
hassio
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon="hassio:arrow-left"
|
icon="hassio:arrow-left"
|
||||||
on-click="backTapped"
|
on-click="backTapped"
|
||||||
@ -119,8 +115,6 @@ class HassioAddonView extends PolymerElement {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
showMenu: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
route: Object,
|
route: Object,
|
||||||
routeData: {
|
routeData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -8,12 +8,7 @@ class HassioApp extends PolymerElement {
|
|||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<template is="dom-if" if="[[hass]]">
|
<template is="dom-if" if="[[hass]]">
|
||||||
<hassio-main
|
<hassio-main hass="[[hass]]" route="[[route]]"></hassio-main>
|
||||||
hass="[[hass]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
route="[[route]]"
|
|
||||||
></hassio-main>
|
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -21,8 +16,6 @@ class HassioApp extends PolymerElement {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
route: Object,
|
route: Object,
|
||||||
hassioPanel: {
|
hassioPanel: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -35,12 +28,9 @@ class HassioApp extends PolymerElement {
|
|||||||
super.ready();
|
super.ready();
|
||||||
window.setProperties = this.setProperties.bind(this);
|
window.setProperties = this.setProperties.bind(this);
|
||||||
this.addEventListener("location-changed", () => this._locationChanged());
|
this.addEventListener("location-changed", () => this._locationChanged());
|
||||||
this.addEventListener("hass-open-menu", () => this._menuEvent(true));
|
this.addEventListener("hass-toggle-menu", (ev) =>
|
||||||
this.addEventListener("hass-close-menu", () => this._menuEvent(false));
|
this.hassioPanel.fire("hass-toggle-menu", ev.detail)
|
||||||
}
|
);
|
||||||
|
|
||||||
_menuEvent(shouldOpen) {
|
|
||||||
this.hassioPanel.fire(shouldOpen ? "hass-open-menu" : "hass-close-menu");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_locationChanged() {
|
_locationChanged() {
|
||||||
|
@ -28,18 +28,13 @@ class HassioMain extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
></hassio-data>
|
></hassio-data>
|
||||||
|
|
||||||
<template is="dom-if" if="[[!loaded]]">
|
<template is="dom-if" if="[[!loaded]]">
|
||||||
<hass-loading-screen
|
<hass-loading-screen></hass-loading-screen>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></hass-loading-screen>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template is="dom-if" if="[[loaded]]">
|
<template is="dom-if" if="[[loaded]]">
|
||||||
<template is="dom-if" if="[[!equalsAddon(routeData.page)]]">
|
<template is="dom-if" if="[[!equalsAddon(routeData.page)]]">
|
||||||
<hassio-pages-with-tabs
|
<hassio-pages-with-tabs
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
page="[[routeData.page]]"
|
page="[[routeData.page]]"
|
||||||
supervisor-info="[[supervisorInfo]]"
|
supervisor-info="[[supervisorInfo]]"
|
||||||
hass-info="[[hassInfo]]"
|
hass-info="[[hassInfo]]"
|
||||||
@ -49,8 +44,6 @@ class HassioMain extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
<template is="dom-if" if="[[equalsAddon(routeData.page)]]">
|
<template is="dom-if" if="[[equalsAddon(routeData.page)]]">
|
||||||
<hassio-addon-view
|
<hassio-addon-view
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
route="[[route]]"
|
route="[[route]]"
|
||||||
></hassio-addon-view>
|
></hassio-addon-view>
|
||||||
</template>
|
</template>
|
||||||
@ -61,8 +54,6 @@ class HassioMain extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
route: {
|
route: {
|
||||||
type: Object,
|
type: Object,
|
||||||
// Fake route object
|
// Fake route object
|
||||||
|
@ -37,11 +37,7 @@ class HassioPagesWithTabs extends NavigateMixin(PolymerElement) {
|
|||||||
<app-header-layout id="layout" has-scrolling-region>
|
<app-header-layout id="layout" has-scrolling-region>
|
||||||
<app-header fixed slot="header">
|
<app-header fixed slot="header">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button hassio></ha-menu-button>
|
||||||
hassio
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>Hass.io</div>
|
<div main-title>Hass.io</div>
|
||||||
<template is="dom-if" if="[[showRefreshButton(page)]]">
|
<template is="dom-if" if="[[showRefreshButton(page)]]">
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
@ -107,8 +103,6 @@ class HassioPagesWithTabs extends NavigateMixin(PolymerElement) {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
showMenu: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
page: String,
|
page: String,
|
||||||
supervisorInfo: Object,
|
supervisorInfo: Object,
|
||||||
hostInfo: Object,
|
hostInfo: Object,
|
||||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20190313.0",
|
version="20190315.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",
|
||||||
|
@ -53,7 +53,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
protected render() {
|
protected render() {
|
||||||
if (!this._authProviders) {
|
if (!this._authProviders) {
|
||||||
return html`
|
return html`
|
||||||
<p>[[localize('ui.panel.page-authorize.initializing')]]</p>
|
<p>${this.localize("ui.panel.page-authorize.initializing")}</p>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
src/common/dom/media_query.ts
Normal file
16
src/common/dom/media_query.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Attach a media query. Listener is called right away and when it matches.
|
||||||
|
* @param mediaQuery media query to match.
|
||||||
|
* @param listener listener to call when media query changes between match/unmatch
|
||||||
|
* @returns function to remove the listener.
|
||||||
|
*/
|
||||||
|
export const listenMediaQuery = (
|
||||||
|
mediaQuery: string,
|
||||||
|
matchesChanged: (matches: boolean) => void
|
||||||
|
) => {
|
||||||
|
const mql = matchMedia(mediaQuery);
|
||||||
|
const listener = (e) => matchesChanged(e.matches);
|
||||||
|
mql.addListener(listener);
|
||||||
|
matchesChanged(mql.matches);
|
||||||
|
return () => mql.removeListener(listener);
|
||||||
|
};
|
@ -79,3 +79,25 @@ export const computeLocalize = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Silly helper function that converts an object of placeholders to array so we
|
||||||
|
* can convert it back to an object again inside the localize func.
|
||||||
|
* @param localize
|
||||||
|
* @param key
|
||||||
|
* @param placeholders
|
||||||
|
*/
|
||||||
|
export const localizeKey = (
|
||||||
|
localize: LocalizeFunc,
|
||||||
|
key: string,
|
||||||
|
placeholders?: { [key: string]: string }
|
||||||
|
) => {
|
||||||
|
const args: [string, ...string[]] = [key];
|
||||||
|
if (placeholders) {
|
||||||
|
Object.keys(placeholders).forEach((placeholderKey) => {
|
||||||
|
args.push(placeholderKey);
|
||||||
|
args.push(placeholders[placeholderKey]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return localize(...args);
|
||||||
|
};
|
||||||
|
@ -39,7 +39,7 @@ class StateBadge extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
if (!changedProps.has("stateObj")) {
|
if (!changedProps.has("stateObj") || !this.stateObj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const stateObj = this.stateObj;
|
const stateObj = this.stateObj;
|
||||||
|
@ -11,9 +11,6 @@ import { fireEvent } from "../common/dom/fire_event";
|
|||||||
|
|
||||||
@customElement("ha-menu-button")
|
@customElement("ha-menu-button")
|
||||||
class HaMenuButton extends LitElement {
|
class HaMenuButton extends LitElement {
|
||||||
@property({ type: Boolean })
|
|
||||||
public showMenu = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
public hassio = false;
|
public hassio = false;
|
||||||
|
|
||||||
@ -33,7 +30,7 @@ class HaMenuButton extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _toggleMenu(): void {
|
private _toggleMenu(): void {
|
||||||
fireEvent(this, this.showMenu ? "hass-close-menu" : "hass-open-menu");
|
fireEvent(this, "hass-toggle-menu");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,9 @@ export interface CloudWebhook {
|
|||||||
managed?: boolean;
|
managed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fetchCloudStatus = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<CloudStatus>({ type: "cloud/status" });
|
||||||
|
|
||||||
export const createCloudhook = (hass: HomeAssistant, webhookId: string) =>
|
export const createCloudhook = (hass: HomeAssistant, webhookId: string) =>
|
||||||
hass.callWS<CloudWebhook>({
|
hass.callWS<CloudWebhook>({
|
||||||
type: "cloud/cloudhook/create",
|
type: "cloud/cloudhook/create",
|
||||||
|
@ -22,7 +22,8 @@ export interface ConfigFlowStepCreateEntry {
|
|||||||
flow_id: string;
|
flow_id: string;
|
||||||
handler: string;
|
handler: string;
|
||||||
title: string;
|
title: string;
|
||||||
data: any;
|
// Config entry ID
|
||||||
|
result: string;
|
||||||
description: string;
|
description: string;
|
||||||
description_placeholders: { [key: string]: string };
|
description_placeholders: { [key: string]: string };
|
||||||
}
|
}
|
||||||
|
@ -25,16 +25,32 @@ import {
|
|||||||
fetchConfigFlow,
|
fetchConfigFlow,
|
||||||
createConfigFlow,
|
createConfigFlow,
|
||||||
ConfigFlowStep,
|
ConfigFlowStep,
|
||||||
handleConfigFlowStep,
|
|
||||||
deleteConfigFlow,
|
deleteConfigFlow,
|
||||||
FieldSchema,
|
|
||||||
ConfigFlowStepForm,
|
|
||||||
} from "../../data/config_entries";
|
} from "../../data/config_entries";
|
||||||
import { PolymerChangedEvent, applyPolymerEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
import { HaConfigFlowParams } from "./show-dialog-config-flow";
|
import { HaConfigFlowParams } from "./show-dialog-config-flow";
|
||||||
|
|
||||||
|
import "./step-flow-loading";
|
||||||
|
import "./step-flow-form";
|
||||||
|
import "./step-flow-abort";
|
||||||
|
import "./step-flow-create-entry";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
fetchDeviceRegistry,
|
||||||
|
} from "../../data/device_registry";
|
||||||
|
import { AreaRegistryEntry, fetchAreaRegistry } from "../../data/area_registry";
|
||||||
|
|
||||||
let instance = 0;
|
let instance = 0;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// for fire event
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"flow-update": {
|
||||||
|
step?: ConfigFlowStep;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("dialog-config-flow")
|
@customElement("dialog-config-flow")
|
||||||
class ConfigFlowDialog extends LitElement {
|
class ConfigFlowDialog extends LitElement {
|
||||||
@property()
|
@property()
|
||||||
@ -49,18 +65,15 @@ class ConfigFlowDialog extends LitElement {
|
|||||||
private _step?: ConfigFlowStep;
|
private _step?: ConfigFlowStep;
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
private _stepData?: { [key: string]: any };
|
private _devices?: DeviceRegistryEntry[];
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
private _errorMsg?: string;
|
private _areas?: AreaRegistryEntry[];
|
||||||
|
|
||||||
public async showDialog(params: HaConfigFlowParams): Promise<void> {
|
public async showDialog(params: HaConfigFlowParams): Promise<void> {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
this._instance = instance++;
|
this._instance = instance++;
|
||||||
this._step = undefined;
|
|
||||||
this._stepData = {};
|
|
||||||
this._errorMsg = undefined;
|
|
||||||
|
|
||||||
const fetchStep = params.continueFlowId
|
const fetchStep = params.continueFlowId
|
||||||
? fetchConfigFlow(params.hass, params.continueFlowId)
|
? fetchConfigFlow(params.hass, params.continueFlowId)
|
||||||
@ -93,201 +106,91 @@ class ConfigFlowDialog extends LitElement {
|
|||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
const localize = this._params.hass.localize;
|
|
||||||
|
|
||||||
const step = this._step;
|
|
||||||
let headerContent: string | undefined;
|
|
||||||
let bodyContent: TemplateResult | undefined;
|
|
||||||
let buttonContent: TemplateResult | undefined;
|
|
||||||
let descriptionKey: string | undefined;
|
|
||||||
|
|
||||||
if (!step) {
|
|
||||||
bodyContent = html`
|
|
||||||
<div class="init-spinner">
|
|
||||||
<paper-spinner active></paper-spinner>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} else if (step.type === "abort") {
|
|
||||||
descriptionKey = `component.${step.handler}.config.abort.${step.reason}`;
|
|
||||||
headerContent = "Aborted";
|
|
||||||
bodyContent = html``;
|
|
||||||
buttonContent = html`
|
|
||||||
<mwc-button @click="${this._flowDone}">Close</mwc-button>
|
|
||||||
`;
|
|
||||||
} else if (step.type === "create_entry") {
|
|
||||||
descriptionKey = `component.${
|
|
||||||
step.handler
|
|
||||||
}.config.create_entry.${step.description || "default"}`;
|
|
||||||
headerContent = "Success!";
|
|
||||||
bodyContent = html`
|
|
||||||
<p>Created config for ${step.title}</p>
|
|
||||||
`;
|
|
||||||
buttonContent = html`
|
|
||||||
<mwc-button @click="${this._flowDone}">Close</mwc-button>
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
// form
|
|
||||||
descriptionKey = `component.${step.handler}.config.step.${
|
|
||||||
step.step_id
|
|
||||||
}.description`;
|
|
||||||
headerContent = localize(
|
|
||||||
`component.${step.handler}.config.step.${step.step_id}.title`
|
|
||||||
);
|
|
||||||
bodyContent = html`
|
|
||||||
<ha-form
|
|
||||||
.data=${this._stepData}
|
|
||||||
@data-changed=${this._stepDataChanged}
|
|
||||||
.schema=${step.data_schema}
|
|
||||||
.error=${step.errors}
|
|
||||||
.computeLabel=${this._labelCallback}
|
|
||||||
.computeError=${this._errorCallback}
|
|
||||||
></ha-form>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const allRequiredInfoFilledIn =
|
|
||||||
this._stepData &&
|
|
||||||
step.data_schema.every(
|
|
||||||
(field) =>
|
|
||||||
field.optional ||
|
|
||||||
!["", undefined].includes(this._stepData![field.name])
|
|
||||||
);
|
|
||||||
|
|
||||||
buttonContent = this._loading
|
|
||||||
? html`
|
|
||||||
<div class="submit-spinner">
|
|
||||||
<paper-spinner active></paper-spinner>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<div>
|
|
||||||
<mwc-button
|
|
||||||
@click=${this._submitStep}
|
|
||||||
.disabled=${!allRequiredInfoFilledIn}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</mwc-button>
|
|
||||||
|
|
||||||
${!allRequiredInfoFilledIn
|
|
||||||
? html`
|
|
||||||
<paper-tooltip position="left">
|
|
||||||
Not all required fields are filled in.
|
|
||||||
</paper-tooltip>
|
|
||||||
`
|
|
||||||
: html``}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let description: string | undefined;
|
|
||||||
|
|
||||||
if (step && descriptionKey) {
|
|
||||||
const args: [string, ...string[]] = [descriptionKey];
|
|
||||||
const placeholders = step.description_placeholders || {};
|
|
||||||
Object.keys(placeholders).forEach((key) => {
|
|
||||||
args.push(key);
|
|
||||||
args.push(placeholders[key]);
|
|
||||||
});
|
|
||||||
description = localize(...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<paper-dialog
|
<paper-dialog with-backdrop opened @opened-changed=${this._openedChanged}>
|
||||||
with-backdrop
|
${this._loading
|
||||||
.opened=${true}
|
|
||||||
@opened-changed=${this._openedChanged}
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
${headerContent}
|
|
||||||
</h2>
|
|
||||||
<paper-dialog-scrollable>
|
|
||||||
${this._errorMsg
|
|
||||||
? html`
|
? html`
|
||||||
<div class="error">${this._errorMsg}</div>
|
<step-flow-loading></step-flow-loading>
|
||||||
`
|
`
|
||||||
: ""}
|
: this._step === undefined
|
||||||
${description
|
? // When we are going to next step, we render 1 round of empty
|
||||||
|
// to reset the element.
|
||||||
|
""
|
||||||
|
: this._step.type === "form"
|
||||||
? html`
|
? html`
|
||||||
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
<step-flow-form
|
||||||
|
.step=${this._step}
|
||||||
|
.hass=${this._params.hass}
|
||||||
|
></step-flow-form>
|
||||||
`
|
`
|
||||||
: ""}
|
: this._step.type === "abort"
|
||||||
${bodyContent}
|
? html`
|
||||||
</paper-dialog-scrollable>
|
<step-flow-abort
|
||||||
<div class="buttons">
|
.step=${this._step}
|
||||||
${buttonContent}
|
.hass=${this._params.hass}
|
||||||
</div>
|
></step-flow-abort>
|
||||||
|
`
|
||||||
|
: this._devices === undefined || this._areas === undefined
|
||||||
|
? // When it's a create entry result, we will fetch device & area registry
|
||||||
|
html`
|
||||||
|
<step-flow-loading></step-flow-loading>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<step-flow-create-entry
|
||||||
|
.step=${this._step}
|
||||||
|
.hass=${this._params.hass}
|
||||||
|
.devices=${this._devices}
|
||||||
|
.areas=${this._areas}
|
||||||
|
></step-flow-create-entry>
|
||||||
|
`}
|
||||||
</paper-dialog>
|
</paper-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this.addEventListener("keypress", (ev) => {
|
this.addEventListener("flow-update", (ev) => {
|
||||||
if (ev.keyCode === 13) {
|
this._processStep((ev as any).detail.step);
|
||||||
this._submitStep();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
if (
|
||||||
|
changedProps.has("_step") &&
|
||||||
|
this._step &&
|
||||||
|
this._step.type === "create_entry"
|
||||||
|
) {
|
||||||
|
this._fetchDevices(this._step.result);
|
||||||
|
this._fetchAreas();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private get _dialog(): PaperDialogElement {
|
private get _dialog(): PaperDialogElement {
|
||||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
return this.shadowRoot!.querySelector("paper-dialog")!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _submitStep(): Promise<void> {
|
private async _fetchDevices(configEntryId) {
|
||||||
this._loading = true;
|
// Wait 5 seconds to give integrations time to find devices
|
||||||
this._errorMsg = undefined;
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
const devices = await fetchDeviceRegistry(this._params!.hass);
|
||||||
const curInstance = this._instance;
|
this._devices = devices.filter((device) =>
|
||||||
const stepData = this._stepData || {};
|
device.config_entries.includes(configEntryId)
|
||||||
|
|
||||||
const toSendData = {};
|
|
||||||
Object.keys(stepData).forEach((key) => {
|
|
||||||
const value = stepData[key];
|
|
||||||
const isEmpty = [undefined, ""].includes(value);
|
|
||||||
|
|
||||||
if (!isEmpty) {
|
|
||||||
toSendData[key] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const step = await handleConfigFlowStep(
|
|
||||||
this._params!.hass,
|
|
||||||
this._step!.flow_id,
|
|
||||||
toSendData
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (curInstance !== this._instance) {
|
private async _fetchAreas() {
|
||||||
|
this._areas = await fetchAreaRegistry(this._params!.hass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _processStep(step: ConfigFlowStep): Promise<void> {
|
||||||
|
if (step === undefined) {
|
||||||
|
this._flowDone();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this._step = undefined;
|
||||||
this._processStep(step);
|
await this.updateComplete;
|
||||||
} catch (err) {
|
|
||||||
this._errorMsg =
|
|
||||||
(err && err.body && err.body.message) || "Unknown error occurred";
|
|
||||||
} finally {
|
|
||||||
this._loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _processStep(step: ConfigFlowStep): void {
|
|
||||||
this._step = step;
|
this._step = step;
|
||||||
|
|
||||||
// We got a new form if there are no errors.
|
|
||||||
if (step.type === "form") {
|
|
||||||
if (!step.errors) {
|
|
||||||
step.errors = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(step.errors).length === 0) {
|
|
||||||
const data = {};
|
|
||||||
step.data_schema.forEach((field) => {
|
|
||||||
if ("default" in field) {
|
|
||||||
data[field.name] = field.default;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._stepData = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _flowDone(): void {
|
private _flowDone(): void {
|
||||||
@ -307,10 +210,9 @@ class ConfigFlowDialog extends LitElement {
|
|||||||
flowFinished,
|
flowFinished,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._errorMsg = undefined;
|
|
||||||
this._step = undefined;
|
this._step = undefined;
|
||||||
this._stepData = {};
|
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
|
this._devices = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||||
@ -320,51 +222,17 @@ class ConfigFlowDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _stepDataChanged(ev: PolymerChangedEvent<any>): void {
|
|
||||||
this._stepData = applyPolymerEvent(ev, this._stepData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _labelCallback = (schema: FieldSchema): string => {
|
|
||||||
const step = this._step as ConfigFlowStepForm;
|
|
||||||
|
|
||||||
return this._params!.hass.localize(
|
|
||||||
`component.${step.handler}.config.step.${step.step_id}.data.${
|
|
||||||
schema.name
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _errorCallback = (error: string) =>
|
|
||||||
this._params!.hass.localize(
|
|
||||||
`component.${this._step!.handler}.config.error.${error}`
|
|
||||||
);
|
|
||||||
|
|
||||||
static get styles(): CSSResultArray {
|
static get styles(): CSSResultArray {
|
||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
paper-dialog {
|
paper-dialog {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
ha-markdown {
|
paper-dialog > * {
|
||||||
word-break: break-word;
|
margin: 0;
|
||||||
}
|
|
||||||
ha-markdown a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
ha-markdown img:first-child:last-child {
|
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
padding: 0;
|
||||||
}
|
|
||||||
.init-spinner {
|
|
||||||
padding: 10px 100px 34px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.submit-spinner {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
66
src/dialogs/config-flow/step-flow-abort.ts
Normal file
66
src/dialogs/config-flow/step-flow-abort.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
CSSResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
|
||||||
|
import { ConfigFlowStepAbort } from "../../data/config_entries";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { localizeKey } from "../../common/translations/localize";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { configFlowContentStyles } from "./styles";
|
||||||
|
|
||||||
|
@customElement("step-flow-abort")
|
||||||
|
class StepFlowAbort extends LitElement {
|
||||||
|
@property()
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
private step!: ConfigFlowStepAbort;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
const localize = this.hass.localize;
|
||||||
|
const step = this.step;
|
||||||
|
|
||||||
|
const description = localizeKey(
|
||||||
|
localize,
|
||||||
|
`component.${step.handler}.config.abort.${step.reason}`,
|
||||||
|
step.description_placeholders
|
||||||
|
);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<h2>Aborted</h2>
|
||||||
|
<div class="content">
|
||||||
|
${
|
||||||
|
description
|
||||||
|
? html`
|
||||||
|
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<mwc-button @click="${this._flowDone}">Close</mwc-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _flowDone(): void {
|
||||||
|
fireEvent(this, "flow-update", { step: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return configFlowContentStyles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"step-flow-abort": StepFlowAbort;
|
||||||
|
}
|
||||||
|
}
|
191
src/dialogs/config-flow/step-flow-create-entry.ts
Normal file
191
src/dialogs/config-flow/step-flow-create-entry.ts
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
CSSResultArray,
|
||||||
|
css,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
|
||||||
|
import { ConfigFlowStepCreateEntry } from "../../data/config_entries";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { localizeKey } from "../../common/translations/localize";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { configFlowContentStyles } from "./styles";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
updateDeviceRegistryEntry,
|
||||||
|
} from "../../data/device_registry";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
createAreaRegistryEntry,
|
||||||
|
} from "../../data/area_registry";
|
||||||
|
|
||||||
|
@customElement("step-flow-create-entry")
|
||||||
|
class StepFlowCreateEntry extends LitElement {
|
||||||
|
@property()
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
public step!: ConfigFlowStepCreateEntry;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
public devices!: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@property()
|
||||||
|
public areas!: AreaRegistryEntry[];
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
const localize = this.hass.localize;
|
||||||
|
const step = this.step;
|
||||||
|
|
||||||
|
const description = localizeKey(
|
||||||
|
localize,
|
||||||
|
`component.${step.handler}.config.create_entry.${step.description ||
|
||||||
|
"default"}`,
|
||||||
|
step.description_placeholders
|
||||||
|
);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<h2>Success!</h2>
|
||||||
|
<div class="content">
|
||||||
|
${
|
||||||
|
description
|
||||||
|
? html`
|
||||||
|
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
<p>Created config for ${step.title}.</p>
|
||||||
|
${
|
||||||
|
this.devices.length === 0
|
||||||
|
? ""
|
||||||
|
: html`
|
||||||
|
<p>We found the following devices:</p>
|
||||||
|
<div class="devices">
|
||||||
|
${this.devices.map(
|
||||||
|
(device) =>
|
||||||
|
html`
|
||||||
|
<div class="device">
|
||||||
|
<b>${device.name}</b><br />
|
||||||
|
${device.model} (${device.manufacturer})
|
||||||
|
|
||||||
|
<paper-dropdown-menu-light
|
||||||
|
label="Area"
|
||||||
|
.device=${device.id}
|
||||||
|
@selected-item-changed=${this._handleAreaChanged}
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
selected="0"
|
||||||
|
>
|
||||||
|
<paper-item>
|
||||||
|
${localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.no_area"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
${this.areas.map(
|
||||||
|
(area) => html`
|
||||||
|
<paper-item .area=${area.area_id}>
|
||||||
|
${area.name}
|
||||||
|
</paper-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu-light>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
${
|
||||||
|
this.devices.length > 0
|
||||||
|
? html`
|
||||||
|
<mwc-button @click="${this._addArea}">Add Area</mwc-button>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
<mwc-button @click="${this._flowDone}">Finish</mwc-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _flowDone(): void {
|
||||||
|
fireEvent(this, "flow-update", { step: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _addArea() {
|
||||||
|
const name = prompt("Name of the new area?");
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const area = await createAreaRegistryEntry(this.hass, {
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
this.areas = [...this.areas, area];
|
||||||
|
} catch (err) {
|
||||||
|
alert("Failed to create area.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleAreaChanged(ev: Event) {
|
||||||
|
const dropdown = ev.currentTarget as any;
|
||||||
|
const device = dropdown.device;
|
||||||
|
|
||||||
|
// Item first becomes null, then new item.
|
||||||
|
if (!dropdown.selectedItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const area = dropdown.selectedItem.area;
|
||||||
|
try {
|
||||||
|
await updateDeviceRegistryEntry(this.hass, device, {
|
||||||
|
area_id: area,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Error saving area: ${err.message}`);
|
||||||
|
dropdown.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultArray {
|
||||||
|
return [
|
||||||
|
configFlowContentStyles,
|
||||||
|
css`
|
||||||
|
.devices {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: -4px;
|
||||||
|
}
|
||||||
|
.device {
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.buttons > *:last-child {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"step-flow-create-entry": StepFlowCreateEntry;
|
||||||
|
}
|
||||||
|
}
|
222
src/dialogs/config-flow/step-flow-form.ts
Normal file
222
src/dialogs/config-flow/step-flow-form.ts
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
CSSResultArray,
|
||||||
|
css,
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
|
|
||||||
|
import "../../components/ha-form";
|
||||||
|
import "../../components/ha-markdown";
|
||||||
|
import "../../resources/ha-style";
|
||||||
|
import {
|
||||||
|
handleConfigFlowStep,
|
||||||
|
FieldSchema,
|
||||||
|
ConfigFlowStepForm,
|
||||||
|
} from "../../data/config_entries";
|
||||||
|
import { PolymerChangedEvent, applyPolymerEvent } from "../../polymer-types";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { localizeKey } from "../../common/translations/localize";
|
||||||
|
import { configFlowContentStyles } from "./styles";
|
||||||
|
|
||||||
|
@customElement("step-flow-form")
|
||||||
|
class StepFlowForm extends LitElement {
|
||||||
|
@property()
|
||||||
|
public step!: ConfigFlowStepForm;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
private _loading = false;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
private _stepData?: { [key: string]: any };
|
||||||
|
|
||||||
|
@property()
|
||||||
|
private _errorMsg?: string;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
const localize = this.hass.localize;
|
||||||
|
const step = this.step;
|
||||||
|
|
||||||
|
const allRequiredInfoFilledIn =
|
||||||
|
this._stepData === undefined
|
||||||
|
? // If no data filled in, just check that any field is required
|
||||||
|
step.data_schema.find((field) => !field.optional) === undefined
|
||||||
|
: // If data is filled in, make sure all required fields are
|
||||||
|
this._stepData &&
|
||||||
|
step.data_schema.every(
|
||||||
|
(field) =>
|
||||||
|
field.optional ||
|
||||||
|
!["", undefined].includes(this._stepData![field.name])
|
||||||
|
);
|
||||||
|
|
||||||
|
const description = localizeKey(
|
||||||
|
localize,
|
||||||
|
`component.${step.handler}.config.step.${step.step_id}.description`,
|
||||||
|
step.description_placeholders
|
||||||
|
);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<h2>
|
||||||
|
${localize(
|
||||||
|
`component.${step.handler}.config.step.${step.step_id}.title`
|
||||||
|
)}
|
||||||
|
</h2>
|
||||||
|
<div class="content">
|
||||||
|
${this._errorMsg
|
||||||
|
? html`
|
||||||
|
<div class="error">${this._errorMsg}</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${description
|
||||||
|
? html`
|
||||||
|
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<ha-form
|
||||||
|
.data=${this._stepDataProcessed}
|
||||||
|
@data-changed=${this._stepDataChanged}
|
||||||
|
.schema=${step.data_schema}
|
||||||
|
.error=${step.errors}
|
||||||
|
.computeLabel=${this._labelCallback}
|
||||||
|
.computeError=${this._errorCallback}
|
||||||
|
></ha-form>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
${this._loading
|
||||||
|
? html`
|
||||||
|
<div class="submit-spinner">
|
||||||
|
<paper-spinner active></paper-spinner>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div>
|
||||||
|
<mwc-button
|
||||||
|
@click=${this._submitStep}
|
||||||
|
.disabled=${!allRequiredInfoFilledIn}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</mwc-button>
|
||||||
|
|
||||||
|
${!allRequiredInfoFilledIn
|
||||||
|
? html`
|
||||||
|
<paper-tooltip position="left">
|
||||||
|
Not all required fields are filled in.
|
||||||
|
</paper-tooltip>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this.addEventListener("keypress", (ev) => {
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
this._submitStep();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _stepDataProcessed() {
|
||||||
|
if (this._stepData !== undefined) {
|
||||||
|
return this._stepData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
this.step.data_schema.forEach((field) => {
|
||||||
|
if ("default" in field) {
|
||||||
|
data[field.name] = field.default;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _submitStep(): Promise<void> {
|
||||||
|
this._loading = true;
|
||||||
|
this._errorMsg = undefined;
|
||||||
|
|
||||||
|
const flowId = this.step.flow_id;
|
||||||
|
const stepData = this._stepData || {};
|
||||||
|
|
||||||
|
const toSendData = {};
|
||||||
|
Object.keys(stepData).forEach((key) => {
|
||||||
|
const value = stepData[key];
|
||||||
|
const isEmpty = [undefined, ""].includes(value);
|
||||||
|
|
||||||
|
if (!isEmpty) {
|
||||||
|
toSendData[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const step = await handleConfigFlowStep(
|
||||||
|
this.hass,
|
||||||
|
this.step.flow_id,
|
||||||
|
toSendData
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.step || flowId !== this.step.flow_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent(this, "flow-update", {
|
||||||
|
step,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
this._errorMsg =
|
||||||
|
(err && err.body && err.body.message) || "Unknown error occurred";
|
||||||
|
} finally {
|
||||||
|
this._loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _stepDataChanged(ev: PolymerChangedEvent<any>): void {
|
||||||
|
this._stepData = applyPolymerEvent(ev, this._stepData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _labelCallback = (schema: FieldSchema): string => {
|
||||||
|
const step = this.step as ConfigFlowStepForm;
|
||||||
|
|
||||||
|
return this.hass.localize(
|
||||||
|
`component.${step.handler}.config.step.${step.step_id}.data.${
|
||||||
|
schema.name
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _errorCallback = (error: string) =>
|
||||||
|
this.hass.localize(`component.${this.step.handler}.config.error.${error}`);
|
||||||
|
|
||||||
|
static get styles(): CSSResultArray {
|
||||||
|
return [
|
||||||
|
configFlowContentStyles,
|
||||||
|
css`
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-spinner {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"step-flow-form": StepFlowForm;
|
||||||
|
}
|
||||||
|
}
|
35
src/dialogs/config-flow/step-flow-loading.ts
Normal file
35
src/dialogs/config-flow/step-flow-loading.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
customElement,
|
||||||
|
CSSResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/paper-spinner/paper-spinner-lite";
|
||||||
|
|
||||||
|
@customElement("step-flow-loading")
|
||||||
|
class StepFlowLoading extends LitElement {
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
return html`
|
||||||
|
<div class="init-spinner">
|
||||||
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.init-spinner {
|
||||||
|
padding: 50px 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"step-flow-loading": StepFlowLoading;
|
||||||
|
}
|
||||||
|
}
|
33
src/dialogs/config-flow/styles.ts
Normal file
33
src/dialogs/config-flow/styles.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { css } from "lit-element";
|
||||||
|
|
||||||
|
export const configFlowContentStyles = css`
|
||||||
|
h2 {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
position: relative;
|
||||||
|
padding: 8px 8px 8px 24px;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--primary-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-markdown {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
ha-markdown a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
ha-markdown img:first-child:last-child {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
`;
|
@ -46,6 +46,11 @@ class MoreInfoCamera extends UpdatingElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.hass!.config.components.includes("stream")) {
|
||||||
|
this._renderMJPEG();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const videoEl = document.createElement("video");
|
const videoEl = document.createElement("video");
|
||||||
videoEl.style.width = "100%";
|
videoEl.style.width = "100%";
|
||||||
videoEl.autoplay = true;
|
videoEl.autoplay = true;
|
||||||
|
@ -55,8 +55,7 @@ function initialize(panel, properties) {
|
|||||||
|
|
||||||
const forwardEvent = (ev) =>
|
const forwardEvent = (ev) =>
|
||||||
window.parent.customPanel.fire(ev.type, ev.detail);
|
window.parent.customPanel.fire(ev.type, ev.detail);
|
||||||
root.addEventListener("hass-open-menu", forwardEvent);
|
root.addEventListener("hass-toggle-menu", forwardEvent);
|
||||||
root.addEventListener("hass-close-menu", forwardEvent);
|
|
||||||
root.addEventListener("location-changed", () =>
|
root.addEventListener("location-changed", () =>
|
||||||
window.parent.customPanel.navigate(window.location.pathname)
|
window.parent.customPanel.navigate(window.location.pathname)
|
||||||
);
|
);
|
||||||
|
@ -4,29 +4,31 @@ import {
|
|||||||
LitElement,
|
LitElement,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
html,
|
html,
|
||||||
property,
|
|
||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
css,
|
css,
|
||||||
customElement,
|
customElement,
|
||||||
|
property,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../components/ha-menu-button";
|
import "../components/ha-menu-button";
|
||||||
|
import "../components/ha-paper-icon-button-arrow-prev";
|
||||||
import { haStyle } from "../resources/styles";
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
@customElement("hass-loading-screen")
|
@customElement("hass-loading-screen")
|
||||||
class HassLoadingScreen extends LitElement {
|
class HassLoadingScreen extends LitElement {
|
||||||
@property({ type: Boolean })
|
@property() public isRoot? = false;
|
||||||
public narrow?: boolean;
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
|
||||||
public showMenu?: boolean;
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
${this.isRoot
|
||||||
.narrow=${this.narrow}
|
? html`
|
||||||
.showMenu=${this.showMenu}
|
<ha-menu-button></ha-menu-button>
|
||||||
></ha-menu-button>
|
`
|
||||||
|
: html`
|
||||||
|
<ha-paper-icon-button-arrow-prev
|
||||||
|
@click=${this._handleBack}
|
||||||
|
></ha-paper-icon-button-arrow-prev>
|
||||||
|
`}
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
@ -34,6 +36,10 @@ class HassLoadingScreen extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleBack() {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultArray {
|
static get styles(): CSSResultArray {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
192
src/layouts/hass-router-page.ts
Normal file
192
src/layouts/hass-router-page.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { UpdatingElement, property, PropertyValues } from "lit-element";
|
||||||
|
import "./hass-error-screen";
|
||||||
|
import { Route } from "../types";
|
||||||
|
import { navigate } from "../common/navigate";
|
||||||
|
|
||||||
|
const extractPage = (path: string, defaultPage: string) => {
|
||||||
|
if (path === "") {
|
||||||
|
return defaultPage;
|
||||||
|
}
|
||||||
|
const subpathStart = path.indexOf("/", 1);
|
||||||
|
return subpathStart === -1
|
||||||
|
? path.substr(1)
|
||||||
|
: path.substr(1, subpathStart - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RouteOptions {
|
||||||
|
tag: string;
|
||||||
|
load: () => Promise<unknown>;
|
||||||
|
cache?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RouterOptions {
|
||||||
|
isRoot?: boolean;
|
||||||
|
defaultPage?: string;
|
||||||
|
preloadAll?: boolean;
|
||||||
|
cacheAll?: boolean;
|
||||||
|
showLoading?: boolean;
|
||||||
|
routes: {
|
||||||
|
[route: string]: RouteOptions;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time to wait for code to load before we show loading screen.
|
||||||
|
const LOADING_SCREEN_THRESHOLD = 400; // ms
|
||||||
|
|
||||||
|
export class HassRouterPage extends UpdatingElement {
|
||||||
|
protected static routerOptions: RouterOptions = { routes: {} };
|
||||||
|
|
||||||
|
protected static finalize() {
|
||||||
|
super.finalize();
|
||||||
|
this._routerOptions = this.routerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _routerOptions: RouterOptions;
|
||||||
|
|
||||||
|
@property() public route!: Route;
|
||||||
|
private _currentPage = "";
|
||||||
|
private _cache = {};
|
||||||
|
|
||||||
|
protected update(changedProps: PropertyValues) {
|
||||||
|
super.update(changedProps);
|
||||||
|
|
||||||
|
if (!changedProps.has("route")) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this._updatePageEl(this.lastChild, changedProps);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = this.route;
|
||||||
|
|
||||||
|
const routerOptions = (this.constructor as typeof HassRouterPage)
|
||||||
|
._routerOptions;
|
||||||
|
const defaultPage = routerOptions.defaultPage || "";
|
||||||
|
|
||||||
|
if (route && route.path === "") {
|
||||||
|
navigate(this, `${route.prefix}/${defaultPage}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPage = route ? extractPage(route.path, defaultPage) : "not_found";
|
||||||
|
|
||||||
|
if (this._currentPage === newPage) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this._updatePageEl(this.lastChild, changedProps);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currentPage = newPage;
|
||||||
|
|
||||||
|
const routeOptions = routerOptions.routes[newPage];
|
||||||
|
|
||||||
|
if (!routeOptions) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this._updatePageEl(this.lastChild, changedProps);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadProm = routeOptions.load();
|
||||||
|
|
||||||
|
// Check when loading the page source failed.
|
||||||
|
loadProm.catch(() => {
|
||||||
|
// Verify that we're still trying to show the same page.
|
||||||
|
if (this._currentPage !== newPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes either loading screen or the panel
|
||||||
|
this.removeChild(this.lastChild!);
|
||||||
|
|
||||||
|
// Show error screen
|
||||||
|
const errorEl = document.createElement("hass-error-screen");
|
||||||
|
errorEl.error = `Error while loading page ${newPage}.`;
|
||||||
|
this.appendChild(errorEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we don't show loading screen, just show the panel.
|
||||||
|
// It will be automatically upgraded when loading done.
|
||||||
|
if (!routerOptions.showLoading) {
|
||||||
|
this._createPanel(routerOptions, newPage, routeOptions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are only going to show the loading screen after some time.
|
||||||
|
// That way we won't have a double fast flash on fast connections.
|
||||||
|
let created = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (created || this._currentPage !== newPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show a loading screen.
|
||||||
|
if (this.lastChild) {
|
||||||
|
this.removeChild(this.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingEl = document.createElement("hass-loading-screen");
|
||||||
|
loadingEl.isRoot = routerOptions.isRoot;
|
||||||
|
this.appendChild(loadingEl);
|
||||||
|
}, LOADING_SCREEN_THRESHOLD);
|
||||||
|
|
||||||
|
loadProm.then(() => {
|
||||||
|
// Check if we're still trying to show the same page.
|
||||||
|
if (this._currentPage !== newPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
created = true;
|
||||||
|
this._createPanel(routerOptions, newPage, routeOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
|
const options = (this.constructor as typeof HassRouterPage)._routerOptions;
|
||||||
|
|
||||||
|
if (options.preloadAll) {
|
||||||
|
Object.values(options.routes).forEach((route) => route.load());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _updatePageEl(_pageEl, _changedProps?: PropertyValues) {
|
||||||
|
// default we do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createPanel(
|
||||||
|
routerOptions: RouterOptions,
|
||||||
|
page: string,
|
||||||
|
routeOptions: RouteOptions
|
||||||
|
) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this.removeChild(this.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const panelEl =
|
||||||
|
this._cache[page] || document.createElement(routeOptions.tag);
|
||||||
|
this._updatePageEl(panelEl);
|
||||||
|
this.appendChild(panelEl);
|
||||||
|
|
||||||
|
if (routerOptions.cacheAll || routeOptions.cache) {
|
||||||
|
this._cache[page] = panelEl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get routeTail(): Route {
|
||||||
|
const route = this.route!;
|
||||||
|
const dividerPos = route.path.indexOf("/", 1);
|
||||||
|
return dividerPos === -1
|
||||||
|
? {
|
||||||
|
prefix: route.path,
|
||||||
|
path: "",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
prefix: route.path.substr(0, dividerPos),
|
||||||
|
path: route.path.substr(dividerPos),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -25,8 +25,7 @@ const NON_SWIPABLE_PANELS = ["kiosk", "map"];
|
|||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"hass-open-menu": undefined;
|
"hass-toggle-menu": undefined;
|
||||||
"hass-close-menu": undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +69,6 @@ class HomeAssistantMain extends LitElement {
|
|||||||
.narrow=${this._narrow}
|
.narrow=${this._narrow}
|
||||||
.hass=${hass}
|
.hass=${hass}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.showMenu=${hass.dockedSidebar}
|
|
||||||
></partial-panel-resolver>
|
></partial-panel-resolver>
|
||||||
</app-drawer-layout>
|
</app-drawer-layout>
|
||||||
`;
|
`;
|
||||||
@ -79,18 +77,21 @@ class HomeAssistantMain extends LitElement {
|
|||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar");
|
import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar");
|
||||||
|
|
||||||
this.addEventListener("hass-open-menu", () => {
|
this.addEventListener("hass-toggle-menu", () => {
|
||||||
|
const shouldOpen = !this.drawer.opened;
|
||||||
|
|
||||||
|
if (shouldOpen) {
|
||||||
if (this._narrow) {
|
if (this._narrow) {
|
||||||
this.drawer.open();
|
this.drawer.open();
|
||||||
} else {
|
} else {
|
||||||
fireEvent(this, "hass-dock-sidebar", { dock: true });
|
fireEvent(this, "hass-dock-sidebar", { dock: true });
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
this.addEventListener("hass-close-menu", () => {
|
|
||||||
this.drawer.close();
|
this.drawer.close();
|
||||||
if (this.hass!.dockedSidebar) {
|
if (this.hass!.dockedSidebar) {
|
||||||
fireEvent(this, "hass-dock-sidebar", { dock: false });
|
fireEvent(this, "hass-dock-sidebar", { dock: false });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,252 +1,139 @@
|
|||||||
import { LitElement, html, PropertyValues, property } from "lit-element";
|
import { property, customElement } from "lit-element";
|
||||||
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
|
|
||||||
import "./hass-loading-screen";
|
import { HomeAssistant } from "../types";
|
||||||
import "./hass-error-screen";
|
import { HassRouterPage, RouterOptions } from "./hass-router-page";
|
||||||
import { HomeAssistant, Panel, PanelElement, Route } from "../types";
|
|
||||||
|
|
||||||
// Cache of panel loading promises.
|
@customElement("partial-panel-resolver")
|
||||||
const LOADED: { [panel: string]: Promise<void> } = {};
|
class PartialPanelResolver extends HassRouterPage {
|
||||||
|
protected static routerOptions: RouterOptions = {
|
||||||
// Which panel elements we will cache.
|
isRoot: true,
|
||||||
// Maybe we can cache them all eventually, but not sure yet about
|
showLoading: true,
|
||||||
// unknown side effects (like history taking a lot of memory, reset needed)
|
routes: {
|
||||||
const CACHED_EL = ["lovelace", "states"];
|
calendar: {
|
||||||
|
tag: "ha-panel-calendar",
|
||||||
function ensureLoaded(panel): Promise<void> | null {
|
load: () =>
|
||||||
if (panel in LOADED) {
|
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
|
||||||
return LOADED[panel];
|
},
|
||||||
}
|
config: {
|
||||||
|
tag: "ha-panel-config",
|
||||||
let imported;
|
load: () =>
|
||||||
// Name each panel we support here, that way Webpack knows about it.
|
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
|
||||||
switch (panel) {
|
},
|
||||||
case "config":
|
custom: {
|
||||||
imported = import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config");
|
tag: "ha-panel-custom",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
|
||||||
case "custom":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom");
|
"dev-event": {
|
||||||
break;
|
tag: "ha-panel-dev-event",
|
||||||
|
load: () =>
|
||||||
case "dev-event":
|
import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
|
||||||
imported = import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event");
|
},
|
||||||
break;
|
"dev-info": {
|
||||||
|
tag: "ha-panel-dev-info",
|
||||||
case "dev-info":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info");
|
import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
|
||||||
break;
|
},
|
||||||
|
"dev-mqtt": {
|
||||||
case "dev-mqtt":
|
tag: "ha-panel-dev-mqtt",
|
||||||
imported = import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt");
|
load: () =>
|
||||||
break;
|
import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
|
||||||
|
},
|
||||||
case "dev-service":
|
"dev-service": {
|
||||||
imported = import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service");
|
tag: "ha-panel-dev-service",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
|
||||||
case "dev-state":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state");
|
"dev-state": {
|
||||||
break;
|
tag: "ha-panel-dev-state",
|
||||||
|
load: () =>
|
||||||
case "dev-template":
|
import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
|
||||||
imported = import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template");
|
},
|
||||||
break;
|
"dev-template": {
|
||||||
|
tag: "ha-panel-dev-template",
|
||||||
case "lovelace":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace");
|
import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
|
||||||
break;
|
},
|
||||||
|
lovelace: {
|
||||||
case "states":
|
cache: true,
|
||||||
imported = import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states");
|
tag: "ha-panel-lovelace",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
|
||||||
case "history":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history");
|
states: {
|
||||||
break;
|
cache: true,
|
||||||
|
tag: "ha-panel-states",
|
||||||
case "iframe":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe");
|
import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
|
||||||
break;
|
},
|
||||||
|
history: {
|
||||||
case "kiosk":
|
tag: "ha-panel-history",
|
||||||
imported = import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk");
|
load: () =>
|
||||||
break;
|
import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
|
||||||
|
},
|
||||||
case "logbook":
|
iframe: {
|
||||||
imported = import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook");
|
tag: "ha-panel-iframe",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
|
||||||
case "mailbox":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox");
|
kiosk: {
|
||||||
break;
|
tag: "ha-panel-kiosk",
|
||||||
|
load: () =>
|
||||||
case "map":
|
import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
|
||||||
imported = import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map");
|
},
|
||||||
break;
|
logbook: {
|
||||||
|
tag: "ha-panel-logbook",
|
||||||
case "profile":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile");
|
import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
|
||||||
break;
|
},
|
||||||
|
mailbox: {
|
||||||
case "shopping-list":
|
tag: "ha-panel-mailbox",
|
||||||
imported = import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list");
|
load: () =>
|
||||||
break;
|
import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
|
||||||
|
},
|
||||||
case "calendar":
|
map: {
|
||||||
imported = import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar");
|
tag: "ha-panel-map",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
|
||||||
default:
|
},
|
||||||
imported = null;
|
profile: {
|
||||||
}
|
tag: "ha-panel-profile",
|
||||||
|
load: () =>
|
||||||
if (imported != null) {
|
import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
|
||||||
LOADED[panel] = imported;
|
},
|
||||||
}
|
"shopping-list": {
|
||||||
|
tag: "ha-panel-shopping-list",
|
||||||
return imported;
|
load: () =>
|
||||||
}
|
import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
|
||||||
|
},
|
||||||
class PartialPanelResolver extends LitElement {
|
},
|
||||||
|
};
|
||||||
@property() public hass?: HomeAssistant;
|
@property() public hass?: HomeAssistant;
|
||||||
@property() public narrow?: boolean;
|
@property() public narrow?: boolean;
|
||||||
@property() public showMenu?: boolean;
|
|
||||||
@property() public route?: Route | null;
|
|
||||||
|
|
||||||
@property() private _routeTail?: Route | null;
|
protected _updatePageEl(el) {
|
||||||
@property() private _panelEl?: PanelElement;
|
const hass = this.hass!;
|
||||||
@property() private _error?: boolean;
|
|
||||||
private _panel?: Panel;
|
|
||||||
private _cache: { [name: string]: PanelElement } = {};
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (this._error) {
|
|
||||||
return html`
|
|
||||||
<hass-error-screen
|
|
||||||
error="Error while loading this panel."
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.showMenu=${this.showMenu}
|
|
||||||
></hass-error-screen>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._panelEl) {
|
|
||||||
return html`
|
|
||||||
<hass-loading-screen
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.showMenu=${this.showMenu}
|
|
||||||
></hass-loading-screen>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
${this._panelEl}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
if (!this.hass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedProps.has("route")) {
|
|
||||||
// Manual splitting
|
|
||||||
const route = this.route!;
|
|
||||||
const dividerPos = route.path.indexOf("/", 1);
|
|
||||||
this._routeTail =
|
|
||||||
dividerPos === -1
|
|
||||||
? {
|
|
||||||
prefix: route.path,
|
|
||||||
path: "",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
prefix: route.path.substr(0, dividerPos),
|
|
||||||
path: route.path.substr(dividerPos),
|
|
||||||
};
|
|
||||||
|
|
||||||
// If just route changed, no need to process further.
|
|
||||||
if (changedProps.size === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedProps.has("hass")) {
|
|
||||||
const panel = this.hass.panels[this.hass.panelUrl];
|
|
||||||
|
|
||||||
if (panel !== this._panel) {
|
|
||||||
this._panel = panel;
|
|
||||||
this._panelEl = undefined;
|
|
||||||
|
|
||||||
// Found cached one, use that
|
|
||||||
if (panel.component_name in this._cache) {
|
|
||||||
this._panelEl = this._cache[panel.component_name];
|
|
||||||
this._updatePanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadingProm = ensureLoaded(panel.component_name);
|
|
||||||
|
|
||||||
if (loadingProm === null) {
|
|
||||||
this._error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingProm.then(
|
|
||||||
() => {
|
|
||||||
// If panel changed while loading.
|
|
||||||
if (this._panel !== panel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._panelEl = (this._panelEl = document.createElement(
|
|
||||||
`ha-panel-${panel.component_name}`
|
|
||||||
)) as PanelElement;
|
|
||||||
|
|
||||||
if (CACHED_EL.includes(panel.component_name)) {
|
|
||||||
this._cache[panel.component_name] = this._panelEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._error = false;
|
|
||||||
this._updatePanel();
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
console.error("Error loading panel", err);
|
|
||||||
this._error = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updatePanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updatePanel() {
|
|
||||||
const el = this._panelEl;
|
|
||||||
|
|
||||||
if (!el) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("setProperties" in el) {
|
if ("setProperties" in el) {
|
||||||
// As long as we have Polymer panels
|
// As long as we have Polymer panels
|
||||||
(el as any).setProperties({
|
(el as PolymerElement).setProperties({
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
showMenu: this.showMenu,
|
route: this.routeTail,
|
||||||
route: this._routeTail,
|
panel: hass.panels[hass.panelUrl],
|
||||||
panel: this._panel,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
el.hass = this.hass;
|
el.hass = hass;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
el.showMenu = this.showMenu;
|
el.route = this.routeTail;
|
||||||
el.route = this._routeTail;
|
el.panel = hass.panels[hass.panelUrl];
|
||||||
el.panel = this._panel;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("partial-panel-resolver", PartialPanelResolver);
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"partial-panel-resolver": PartialPanelResolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -67,10 +67,7 @@ class HaPanelCalendar extends LocalizeMixin(PolymerElement) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.calendar')]]</div>
|
<div main-title>[[localize('panel.calendar')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -145,11 +142,6 @@ class HaPanelCalendar extends LocalizeMixin(PolymerElement) {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,15 +128,6 @@ class HaAutomationPicker extends LocalizeMixin(NavigateMixin(PolymerElement)) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
automations: {
|
automations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
|
@ -31,8 +31,6 @@ class HaConfigAutomation extends PolymerElement {
|
|||||||
<template is="dom-if" if="[[!showEditor]]">
|
<template is="dom-if" if="[[!showEditor]]">
|
||||||
<ha-automation-picker
|
<ha-automation-picker
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
automations="[[automations]]"
|
automations="[[automations]]"
|
||||||
is-wide="[[isWide]]"
|
is-wide="[[isWide]]"
|
||||||
></ha-automation-picker>
|
></ha-automation-picker>
|
||||||
@ -52,8 +50,6 @@ class HaConfigAutomation extends PolymerElement {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
route: Object,
|
route: Object,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
_routeData: Object,
|
_routeData: Object,
|
||||||
|
@ -40,7 +40,7 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
<app-header-layout has-scrolling-region="">
|
<app-header-layout has-scrolling-region="">
|
||||||
<app-header slot="header" fixed="">
|
<app-header slot="header" fixed="">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button narrow="[[narrow]]" show-menu="[[showMenu]]"></ha-menu-button>
|
<ha-menu-button></ha-menu-button>
|
||||||
<div main-title="">[[localize('panel.config')]]</div>
|
<div main-title="">[[localize('panel.config')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -111,8 +111,6 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
hass: Object,
|
hass: Object,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
cloudStatus: Object,
|
cloudStatus: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +75,6 @@ class HaConfigSection extends PolymerElement {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
isWide: {
|
isWide: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
import "@polymer/app-route/app-route";
|
|
||||||
import "@polymer/iron-media-query/iron-media-query";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
import isComponentLoaded from "../../common/config/is_component_loaded";
|
|
||||||
import EventsMixin from "../../mixins/events-mixin";
|
|
||||||
import NavigateMixin from "../../mixins/navigate-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
* @appliesMixin NavigateMixin
|
|
||||||
*/
|
|
||||||
class HaPanelConfig extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<app-route
|
|
||||||
route="[[route]]"
|
|
||||||
pattern="/:page"
|
|
||||||
data="{{_routeData}}"
|
|
||||||
></app-route>
|
|
||||||
|
|
||||||
<iron-media-query query="(min-width: 1040px)" query-matches="{{wide}}">
|
|
||||||
</iron-media-query>
|
|
||||||
<iron-media-query
|
|
||||||
query="(min-width: 1296px)"
|
|
||||||
query-matches="{{wideSidebar}}"
|
|
||||||
>
|
|
||||||
</iron-media-query>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "area_registry")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-area-registry
|
|
||||||
page-name="area_registry"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-area-registry>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "core")]]' restamp>
|
|
||||||
<ha-config-core
|
|
||||||
page-name="core"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-core>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "cloud")]]' restamp>
|
|
||||||
<ha-config-cloud
|
|
||||||
page-name="cloud"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
cloud-status="[[_cloudStatus]]"
|
|
||||||
></ha-config-cloud>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "dashboard")]]'>
|
|
||||||
<ha-config-dashboard
|
|
||||||
page-name="dashboard"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
cloud-status="[[_cloudStatus]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-config-dashboard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "automation")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-automation
|
|
||||||
page-name="automation"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-automation>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "script")]]' restamp>
|
|
||||||
<ha-config-script
|
|
||||||
page-name="script"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-script>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "entity_registry")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-entity-registry
|
|
||||||
page-name="entity_registry"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-entity-registry>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "zha")]]' restamp>
|
|
||||||
<ha-config-zha
|
|
||||||
page-name="zha"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-zha>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "zwave")]]' restamp>
|
|
||||||
<ha-config-zwave
|
|
||||||
page-name="zwave"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-zwave>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "person")]]' restamp>
|
|
||||||
<ha-config-person
|
|
||||||
page-name="person"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-person>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "customize")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-customize
|
|
||||||
page-name="customize"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-customize>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "integrations")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-entries
|
|
||||||
route="[[route]]"
|
|
||||||
page-name="integrations"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
></ha-config-entries>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "users")]]' restamp>
|
|
||||||
<ha-config-users
|
|
||||||
page-name="users"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
></ha-config-users>
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
_cloudStatus: {
|
|
||||||
type: Object,
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
route: {
|
|
||||||
type: Object,
|
|
||||||
observer: "_routeChanged",
|
|
||||||
},
|
|
||||||
|
|
||||||
_routeData: Object,
|
|
||||||
|
|
||||||
wide: Boolean,
|
|
||||||
wideSidebar: Boolean,
|
|
||||||
|
|
||||||
isWide: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: "computeIsWide(showMenu, wideSidebar, wide)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
if (isComponentLoaded(this.hass, "cloud")) {
|
|
||||||
this._updateCloudStatus();
|
|
||||||
}
|
|
||||||
this.addEventListener("ha-refresh-cloud-status", () =>
|
|
||||||
this._updateCloudStatus()
|
|
||||||
);
|
|
||||||
import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry");
|
|
||||||
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation");
|
|
||||||
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud");
|
|
||||||
import(/* webpackChunkName: "panel-config-config" */ "./config-entries/ha-config-entries");
|
|
||||||
import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core");
|
|
||||||
import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize");
|
|
||||||
import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard");
|
|
||||||
import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script");
|
|
||||||
import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry");
|
|
||||||
import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users");
|
|
||||||
import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha");
|
|
||||||
import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave");
|
|
||||||
import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person");
|
|
||||||
}
|
|
||||||
|
|
||||||
async _updateCloudStatus() {
|
|
||||||
this._cloudStatus = await this.hass.callWS({ type: "cloud/status" });
|
|
||||||
|
|
||||||
if (this._cloudStatus.cloud === "connecting") {
|
|
||||||
setTimeout(() => this._updateCloudStatus(), 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
computeIsWide(showMenu, wideSidebar, wide) {
|
|
||||||
return showMenu ? wideSidebar : wide;
|
|
||||||
}
|
|
||||||
|
|
||||||
_routeChanged(route) {
|
|
||||||
if (route.path === "" && route.prefix === "/config") {
|
|
||||||
this.navigate("/config/dashboard", true);
|
|
||||||
}
|
|
||||||
this.fire("iron-resize");
|
|
||||||
}
|
|
||||||
|
|
||||||
_equals(a, b) {
|
|
||||||
return a === b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-panel-config", HaPanelConfig);
|
|
142
src/panels/config/ha-panel-config.ts
Normal file
142
src/panels/config/ha-panel-config.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { property, PropertyValues, customElement } from "lit-element";
|
||||||
|
import "../../layouts/hass-loading-screen";
|
||||||
|
import isComponentLoaded from "../../common/config/is_component_loaded";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||||
|
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||||
|
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||||
|
|
||||||
|
@customElement("ha-panel-config")
|
||||||
|
class HaPanelConfig extends HassRouterPage {
|
||||||
|
protected static routerOptions: RouterOptions = {
|
||||||
|
defaultPage: "dashboard",
|
||||||
|
cacheAll: true,
|
||||||
|
preloadAll: true,
|
||||||
|
routes: {
|
||||||
|
area_registry: {
|
||||||
|
tag: "ha-config-area-registry",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"),
|
||||||
|
},
|
||||||
|
automation: {
|
||||||
|
tag: "ha-config-automation",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"),
|
||||||
|
},
|
||||||
|
cloud: {
|
||||||
|
tag: "ha-config-cloud",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"),
|
||||||
|
},
|
||||||
|
core: {
|
||||||
|
tag: "ha-config-core",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"),
|
||||||
|
},
|
||||||
|
customize: {
|
||||||
|
tag: "ha-config-customize",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"),
|
||||||
|
},
|
||||||
|
dashboard: {
|
||||||
|
tag: "ha-config-dashboard",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"),
|
||||||
|
},
|
||||||
|
entity_registry: {
|
||||||
|
tag: "ha-config-entity-registry",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"),
|
||||||
|
},
|
||||||
|
integrations: {
|
||||||
|
tag: "ha-config-integrations",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-integrations" */ "./integrations/ha-config-integrations"),
|
||||||
|
},
|
||||||
|
person: {
|
||||||
|
tag: "ha-config-person",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person"),
|
||||||
|
},
|
||||||
|
script: {
|
||||||
|
tag: "ha-config-script",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"),
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
tag: "ha-config-users",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"),
|
||||||
|
},
|
||||||
|
zha: {
|
||||||
|
tag: "ha-config-zha",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha"),
|
||||||
|
},
|
||||||
|
zwave: {
|
||||||
|
tag: "ha-config-zwave",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public _wideSidebar: boolean = false;
|
||||||
|
@property() public _wide: boolean = false;
|
||||||
|
@property() private _cloudStatus?: CloudStatus;
|
||||||
|
|
||||||
|
private _listeners: Array<() => void> = [];
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
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() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
while (this._listeners.length) {
|
||||||
|
this._listeners.pop()!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
if (isComponentLoaded(this.hass, "cloud")) {
|
||||||
|
this._updateCloudStatus();
|
||||||
|
}
|
||||||
|
this.addEventListener("ha-refresh-cloud-status", () =>
|
||||||
|
this._updateCloudStatus()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _updatePageEl(el) {
|
||||||
|
el.route = this.route;
|
||||||
|
el.hass = this.hass;
|
||||||
|
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
|
||||||
|
el.cloudStatus = this._cloudStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateCloudStatus() {
|
||||||
|
this._cloudStatus = await fetchCloudStatus(this.hass);
|
||||||
|
|
||||||
|
if (this._cloudStatus.cloud === "connecting") {
|
||||||
|
setTimeout(() => this._updateCloudStatus(), 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-panel-config": HaPanelConfig;
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin";
|
|||||||
import compare from "../../../common/string/compare";
|
import compare from "../../../common/string/compare";
|
||||||
import { fetchAreaRegistry } from "../../../data/area_registry";
|
import { fetchAreaRegistry } from "../../../data/area_registry";
|
||||||
|
|
||||||
class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
class HaConfigIntegrations extends NavigateMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<app-route
|
<app-route
|
||||||
@ -158,4 +158,4 @@ class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-config-entries", HaConfigEntries);
|
customElements.define("ha-config-integrations", HaConfigIntegrations);
|
@ -32,8 +32,6 @@ class HaConfigScript extends PolymerElement {
|
|||||||
<template is="dom-if" if="[[!showEditor]]">
|
<template is="dom-if" if="[[!showEditor]]">
|
||||||
<ha-script-picker
|
<ha-script-picker
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
scripts="[[scripts]]"
|
scripts="[[scripts]]"
|
||||||
is-wide="[[isWide]]"
|
is-wide="[[isWide]]"
|
||||||
></ha-script-picker>
|
></ha-script-picker>
|
||||||
@ -53,8 +51,6 @@ class HaConfigScript extends PolymerElement {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
route: Object,
|
route: Object,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
_routeData: Object,
|
_routeData: Object,
|
||||||
|
@ -130,15 +130,6 @@ class HaScriptEditor extends LocalizeMixin(NavigateMixin(PolymerElement)) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
type: Object,
|
type: Object,
|
||||||
value: null,
|
value: null,
|
||||||
|
@ -124,15 +124,6 @@ class HaScriptPicker extends LocalizeMixin(NavigateMixin(PolymerElement)) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
scripts: {
|
scripts: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
|
@ -16,7 +16,6 @@ class HaPanelCustom extends NavigateMixin(EventsMixin(PolymerElement)) {
|
|||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
showMenu: Boolean,
|
|
||||||
route: Object,
|
route: Object,
|
||||||
panel: {
|
panel: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -26,7 +25,7 @@ class HaPanelCustom extends NavigateMixin(EventsMixin(PolymerElement)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get observers() {
|
static get observers() {
|
||||||
return ["_dataChanged(hass, narrow, showMenu, route)"];
|
return ["_dataChanged(hass, narrow, route)"];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -74,7 +73,6 @@ It will have access to all data in Home Assistant.
|
|||||||
panel,
|
panel,
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
showMenu: this.showMenu,
|
|
||||||
route: this.route,
|
route: this.route,
|
||||||
});
|
});
|
||||||
this.appendChild(element);
|
this.appendChild(element);
|
||||||
@ -109,16 +107,15 @@ It will have access to all data in Home Assistant.
|
|||||||
delete window.customPanel;
|
delete window.customPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dataChanged(hass, narrow, showMenu, route) {
|
_dataChanged(hass, narrow, route) {
|
||||||
if (!this._setProperties) return;
|
if (!this._setProperties) return;
|
||||||
this._setProperties({ hass, narrow, showMenu, route });
|
this._setProperties({ hass, narrow, route });
|
||||||
}
|
}
|
||||||
|
|
||||||
registerIframe(initialize, setProperties) {
|
registerIframe(initialize, setProperties) {
|
||||||
initialize(this.panel, {
|
initialize(this.panel, {
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
showMenu: this.showMenu,
|
|
||||||
route: this.route,
|
route: this.route,
|
||||||
});
|
});
|
||||||
this._setProperties = setProperties;
|
this._setProperties = setProperties;
|
||||||
|
@ -52,10 +52,7 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>Events</div>
|
<div main-title>Events</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -100,16 +97,6 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
eventType: {
|
eventType: {
|
||||||
type: String,
|
type: String,
|
||||||
value: "",
|
value: "",
|
||||||
|
@ -23,14 +23,10 @@ const OPT_IN_PANEL = "states";
|
|||||||
|
|
||||||
class HaPanelDevInfo extends LitElement {
|
class HaPanelDevInfo extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
public narrow?: boolean;
|
|
||||||
public showMenu?: boolean;
|
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
narrow: {},
|
|
||||||
showMenu: {},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,10 +56,7 @@ class HaPanelDevInfo extends LitElement {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
.narrow="${this.narrow}"
|
|
||||||
.showMenu="${this.showMenu}"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>About</div>
|
<div main-title>About</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
@ -41,10 +41,7 @@ class HaPanelDevMqtt extends PolymerElement {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>MQTT</div>
|
<div main-title>MQTT</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -80,8 +77,6 @@ class HaPanelDevMqtt extends PolymerElement {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
topic: String,
|
topic: String,
|
||||||
payload: String,
|
payload: String,
|
||||||
};
|
};
|
||||||
|
@ -84,10 +84,7 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>Services</div>
|
<div main-title>Services</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -187,16 +184,6 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
domainService: {
|
domainService: {
|
||||||
type: String,
|
type: String,
|
||||||
observer: "_domainServiceChanged",
|
observer: "_domainServiceChanged",
|
||||||
|
@ -73,10 +73,7 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>States</div>
|
<div main-title>States</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -183,16 +180,6 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
_entityId: {
|
_entityId: {
|
||||||
type: String,
|
type: String,
|
||||||
value: "",
|
value: "",
|
||||||
|
@ -70,10 +70,7 @@ class HaPanelDevTemplate extends PolymerElement {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>Templates</div>
|
<div main-title>Templates</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -125,16 +122,6 @@ class HaPanelDevTemplate extends PolymerElement {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
error: {
|
error: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
|
@ -65,10 +65,7 @@ class HaPanelHistory extends LocalizeMixin(PolymerElement) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.history')]]</div>
|
<div main-title>[[localize('panel.history')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -123,15 +120,6 @@ class HaPanelHistory extends LocalizeMixin(PolymerElement) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
stateHistory: {
|
stateHistory: {
|
||||||
type: Object,
|
type: Object,
|
||||||
value: null,
|
value: null,
|
||||||
|
@ -16,10 +16,7 @@ class HaPanelIframe extends PolymerElement {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[panel.title]]</div>
|
<div main-title>[[panel.title]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
@ -38,14 +35,6 @@ class HaPanelIframe extends PolymerElement {
|
|||||||
panel: {
|
panel: {
|
||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,10 +89,7 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.logbook')]]</div>
|
<div main-title>[[localize('panel.logbook')]]</div>
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon="hass:refresh"
|
icon="hass:refresh"
|
||||||
@ -164,16 +161,6 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ISO8601 formatted date string
|
// ISO8601 formatted date string
|
||||||
_currentDate: {
|
_currentDate: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -20,11 +20,13 @@ import stateIcon from "../../../common/entity/state_icon";
|
|||||||
import computeStateDomain from "../../../common/entity/compute_state_domain";
|
import computeStateDomain from "../../../common/entity/compute_state_domain";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||||
|
import computeDomain from "../../../common/entity/compute_domain";
|
||||||
import { HomeAssistant, LightEntity } from "../../../types";
|
import { HomeAssistant, LightEntity } from "../../../types";
|
||||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
|
||||||
import { longPress } from "../common/directives/long-press-directive";
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
import { handleClick } from "../common/handle-click";
|
import { handleClick } from "../common/handle-click";
|
||||||
|
import { DOMAINS_TOGGLE } from "../../../common/const";
|
||||||
|
|
||||||
export interface Config extends LovelaceCardConfig {
|
export interface Config extends LovelaceCardConfig {
|
||||||
entity: string;
|
entity: string;
|
||||||
@ -44,8 +46,8 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
public static getStubConfig(): object {
|
public static getStubConfig(): object {
|
||||||
return {
|
return {
|
||||||
tap_action: { action: "more-info" },
|
tap_action: { action: "toggle" },
|
||||||
hold_action: { action: "none" },
|
hold_action: { action: "more-info" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +64,27 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard {
|
|||||||
throw new Error("Invalid Entity");
|
throw new Error("Invalid Entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._config = { theme: "default", ...config };
|
this._config = {
|
||||||
|
theme: "default",
|
||||||
|
hold_action: { action: "more-info" },
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DOMAINS_TOGGLE.has(computeDomain(config.entity))) {
|
||||||
|
this._config = {
|
||||||
|
tap_action: {
|
||||||
|
action: "toggle",
|
||||||
|
},
|
||||||
|
...this._config,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._config = {
|
||||||
|
tap_action: {
|
||||||
|
action: "more-info",
|
||||||
|
},
|
||||||
|
...this._config,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
@ -26,7 +26,6 @@ class LovelacePanel extends LitElement {
|
|||||||
public panel?: PanelInfo<LovelacePanelConfig>;
|
public panel?: PanelInfo<LovelacePanelConfig>;
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
public narrow?: boolean;
|
public narrow?: boolean;
|
||||||
public showMenu?: boolean;
|
|
||||||
public route?: Route;
|
public route?: Route;
|
||||||
private _columns?: number;
|
private _columns?: number;
|
||||||
private _state?: "loading" | "loaded" | "error" | "yaml-editor";
|
private _state?: "loading" | "loaded" | "error" | "yaml-editor";
|
||||||
@ -38,8 +37,6 @@ class LovelacePanel extends LitElement {
|
|||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
lovelace: {},
|
lovelace: {},
|
||||||
narrow: {},
|
|
||||||
showMenu: {},
|
|
||||||
route: {},
|
route: {},
|
||||||
_columns: {},
|
_columns: {},
|
||||||
_state: {},
|
_state: {},
|
||||||
@ -60,8 +57,6 @@ class LovelacePanel extends LitElement {
|
|||||||
if (state === "loaded") {
|
if (state === "loaded") {
|
||||||
return html`
|
return html`
|
||||||
<hui-root
|
<hui-root
|
||||||
.narrow="${this.narrow}"
|
|
||||||
.showMenu="${this.showMenu}"
|
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.lovelace="${this.lovelace}"
|
.lovelace="${this.lovelace}"
|
||||||
.route="${this.route}"
|
.route="${this.route}"
|
||||||
@ -73,12 +68,7 @@ class LovelacePanel extends LitElement {
|
|||||||
|
|
||||||
if (state === "error") {
|
if (state === "error") {
|
||||||
return html`
|
return html`
|
||||||
<hass-error-screen
|
<hass-error-screen title="Lovelace" .error="${this._errorMsg}">
|
||||||
title="Lovelace"
|
|
||||||
.error="${this._errorMsg}"
|
|
||||||
.narrow="${this.narrow}"
|
|
||||||
.showMenu="${this.showMenu}"
|
|
||||||
>
|
|
||||||
<mwc-button on-click="_forceFetchConfig">Reload Lovelace</mwc-button>
|
<mwc-button on-click="_forceFetchConfig">Reload Lovelace</mwc-button>
|
||||||
</hass-error-screen>
|
</hass-error-screen>
|
||||||
`;
|
`;
|
||||||
@ -95,16 +85,25 @@ class LovelacePanel extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-loading-screen
|
<hass-loading-screen></hass-loading-screen>
|
||||||
.narrow="${this.narrow}"
|
|
||||||
.showMenu="${this.showMenu}"
|
|
||||||
></hass-loading-screen>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updated(changedProps: PropertyValues): void {
|
public updated(changedProps: PropertyValues): void {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
if (changedProps.has("narrow") || changedProps.has("showMenu")) {
|
|
||||||
|
if (changedProps.has("narrow")) {
|
||||||
|
this._updateColumns();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changedProps.has("hass")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldHass = changedProps.get("hass") as this["hass"];
|
||||||
|
|
||||||
|
if (oldHass && this.hass!.dockedSidebar !== oldHass.dockedSidebar) {
|
||||||
this._updateColumns();
|
this._updateColumns();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +143,7 @@ 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.showMenu)
|
matchColumns - Number(!this.narrow && this.hass!.dockedSidebar)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyDeclarations,
|
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
css,
|
css,
|
||||||
|
property,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||||
@ -60,39 +60,22 @@ const JS_CACHE = {};
|
|||||||
let loadedUnusedEntities = false;
|
let loadedUnusedEntities = false;
|
||||||
|
|
||||||
class HUIRoot extends LitElement {
|
class HUIRoot extends LitElement {
|
||||||
public narrow?: boolean;
|
@property() public hass?: HomeAssistant;
|
||||||
public showMenu?: boolean;
|
@property() public lovelace?: Lovelace;
|
||||||
public hass?: HomeAssistant;
|
@property() public columns?: number;
|
||||||
public lovelace?: Lovelace;
|
@property() public narrow?: boolean;
|
||||||
public columns?: number;
|
@property() public route?: { path: string; prefix: string };
|
||||||
public route?: { path: string; prefix: string };
|
@property() private _routeData?: { view: string };
|
||||||
private _routeData?: { view: string };
|
@property() private _curView?: number | "hass-unused-entities";
|
||||||
private _curView?: number | "hass-unused-entities";
|
@property() private _notificationsOpen = false;
|
||||||
private _notificationsOpen: boolean;
|
@property() private _persistentNotifications?: Notification[];
|
||||||
private _persistentNotifications?: Notification[];
|
|
||||||
private _viewCache?: { [viewId: string]: HUIView };
|
private _viewCache?: { [viewId: string]: HUIView };
|
||||||
|
|
||||||
private _debouncedConfigChanged: () => void;
|
private _debouncedConfigChanged: () => void;
|
||||||
private _unsubNotifications?: () => void;
|
private _unsubNotifications?: () => void;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
|
||||||
return {
|
|
||||||
narrow: {},
|
|
||||||
showMenu: {},
|
|
||||||
hass: {},
|
|
||||||
lovelace: {},
|
|
||||||
columns: {},
|
|
||||||
route: {},
|
|
||||||
_routeData: {},
|
|
||||||
_curView: {},
|
|
||||||
_notificationsOpen: {},
|
|
||||||
_persistentNotifications: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._notificationsOpen = false;
|
|
||||||
// The view can trigger a re-render when it knows that certain
|
// The view can trigger a re-render when it knows that certain
|
||||||
// web components have been loaded.
|
// web components have been loaded.
|
||||||
this._debouncedConfigChanged = debounce(
|
this._debouncedConfigChanged = debounce(
|
||||||
@ -181,10 +164,7 @@ class HUIRoot extends LitElement {
|
|||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
.narrow="${this.narrow}"
|
|
||||||
.showMenu="${this.showMenu}"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>${this.config.title || "Home Assistant"}</div>
|
<div main-title>${this.config.title || "Home Assistant"}</div>
|
||||||
<hui-notifications-button
|
<hui-notifications-button
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
|
@ -80,10 +80,7 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.mailbox')]]</div>
|
<div main-title>[[localize('panel.mailbox')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div sticky hidden$="[[areTabsHidden(platforms)]]">
|
<div sticky hidden$="[[areTabsHidden(platforms)]]">
|
||||||
@ -135,16 +132,6 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
platforms: {
|
platforms: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
|
@ -27,10 +27,7 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.map')]]</div>
|
<div main-title>[[localize('panel.map')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
@ -44,15 +41,6 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
observer: "drawEntities",
|
observer: "drawEntities",
|
||||||
},
|
},
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,10 +52,7 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.profile')]]</div>
|
<div main-title>[[localize('panel.profile')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
@ -121,7 +118,6 @@ class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
showMenu: Boolean,
|
|
||||||
_refreshTokens: Array,
|
_refreshTokens: Array,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -70,10 +70,7 @@ class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
|
|||||||
<app-header-layout has-scrolling-region>
|
<app-header-layout has-scrolling-region>
|
||||||
<app-header slot="header" fixed>
|
<app-header slot="header" fixed>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title>[[localize('panel.shopping_list')]]</div>
|
<div main-title>[[localize('panel.shopping_list')]]</div>
|
||||||
<ha-start-voice-button
|
<ha-start-voice-button
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
@ -145,8 +142,6 @@ class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
canListen: Boolean,
|
canListen: Boolean,
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -65,10 +65,7 @@ class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
<ha-app-layout id="layout">
|
<ha-app-layout id="layout">
|
||||||
<app-header effects="waterfall" condenses="" fixed="" slot="header">
|
<app-header effects="waterfall" condenses="" fixed="" slot="header">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button
|
<ha-menu-button></ha-menu-button>
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-menu-button>
|
|
||||||
<div main-title="">
|
<div main-title="">
|
||||||
[[computeTitle(views, defaultView, locationName)]]
|
[[computeTitle(views, defaultView, locationName)]]
|
||||||
</div>
|
</div>
|
||||||
@ -160,10 +157,6 @@ class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
panelVisible: {
|
panelVisible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
@ -215,7 +208,7 @@ class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get observers() {
|
static get observers() {
|
||||||
return ["_updateColumns(narrow, showMenu)"];
|
return ["_updateColumns(narrow, hass.dockedSidebar)"];
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
@ -239,7 +232,10 @@ class PartialCards extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
_updateColumns() {
|
_updateColumns() {
|
||||||
const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
|
const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
|
||||||
// Do -1 column if the menu is docked and open
|
// Do -1 column if the menu is docked and open
|
||||||
this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
|
this._columns = Math.max(
|
||||||
|
1,
|
||||||
|
matchColumns - (!this.narrow && this.hass.dockedSidebar)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
areTabsHidden(views, showTabs) {
|
areTabsHidden(views, showTabs) {
|
||||||
|
@ -18,6 +18,7 @@ export const haStyle = css`
|
|||||||
}
|
}
|
||||||
|
|
||||||
app-toolbar ha-menu-button + [main-title],
|
app-toolbar ha-menu-button + [main-title],
|
||||||
|
app-toolbar ha-paper-icon-button-arrow-prev + [main-title],
|
||||||
app-toolbar paper-icon-button + [main-title] {
|
app-toolbar paper-icon-button + [main-title] {
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,6 @@ export interface Route {
|
|||||||
export interface PanelElement extends HTMLElement {
|
export interface PanelElement extends HTMLElement {
|
||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
narrow?: boolean;
|
narrow?: boolean;
|
||||||
showMenu?: boolean;
|
|
||||||
route?: Route | null;
|
route?: Route | null;
|
||||||
panel?: Panel;
|
panel?: Panel;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +250,15 @@
|
|||||||
"on": "مشغل",
|
"on": "مشغل",
|
||||||
"paused": "موقّف مؤقتا",
|
"paused": "موقّف مؤقتا",
|
||||||
"returning": "العودة"
|
"returning": "العودة"
|
||||||
|
},
|
||||||
|
"timer": {
|
||||||
|
"active": "مفعل",
|
||||||
|
"idle": "خامل",
|
||||||
|
"paused": "موقّف مؤقتا"
|
||||||
|
},
|
||||||
|
"person": {
|
||||||
|
"home": "في المنزل",
|
||||||
|
"not_home": "خارج المنزل"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"state_badge": {
|
"state_badge": {
|
||||||
@ -272,6 +281,10 @@
|
|||||||
"device_tracker": {
|
"device_tracker": {
|
||||||
"home": "في المنزل",
|
"home": "في المنزل",
|
||||||
"not_home": "خارج المنزل"
|
"not_home": "خارج المنزل"
|
||||||
|
},
|
||||||
|
"person": {
|
||||||
|
"home": "في المنزل",
|
||||||
|
"not_home": "خارج المنزل"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ui": {
|
"ui": {
|
||||||
@ -340,7 +353,8 @@
|
|||||||
"introduction": "محرر المتحكم الآلي يسمح لك بإنشاء وتحرير المتحكمات الآلية. يرجى قراءة [الإرشادات] (https:\/\/home-assistant.io\/docs\/automation\/editor\/) للتأكد من إعدادك Home Assistant بشكل صحيح.",
|
"introduction": "محرر المتحكم الآلي يسمح لك بإنشاء وتحرير المتحكمات الآلية. يرجى قراءة [الإرشادات] (https:\/\/home-assistant.io\/docs\/automation\/editor\/) للتأكد من إعدادك Home Assistant بشكل صحيح.",
|
||||||
"pick_automation": "أختر متحكم آلي للتعديل",
|
"pick_automation": "أختر متحكم آلي للتعديل",
|
||||||
"no_automations": "لم تعثر على أي متحكم آلي قابل للتعديل",
|
"no_automations": "لم تعثر على أي متحكم آلي قابل للتعديل",
|
||||||
"add_automation": "أضف متحكم آلي"
|
"add_automation": "أضف متحكم آلي",
|
||||||
|
"learn_more": "معرفة المزيد عن التحكم الآلي"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"introduction": "استخدم المتحكمات الآلية لتجعل منزلك ينبض بالحياة",
|
"introduction": "استخدم المتحكمات الآلية لتجعل منزلك ينبض بالحياة",
|
||||||
@ -414,7 +428,8 @@
|
|||||||
"minutes": "الدقائق",
|
"minutes": "الدقائق",
|
||||||
"seconds": "ثواني"
|
"seconds": "ثواني"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"learn_more": "معرفة المزيد عن المشغلات"
|
||||||
},
|
},
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"header": "الشروط",
|
"header": "الشروط",
|
||||||
@ -459,7 +474,8 @@
|
|||||||
"entity": "الجهاز في المنطقة",
|
"entity": "الجهاز في المنطقة",
|
||||||
"zone": "المنطقة"
|
"zone": "المنطقة"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"learn_more": "معرفة المزيد عن الشروط"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"header": "الإجراءات",
|
"header": "الإجراءات",
|
||||||
@ -492,7 +508,8 @@
|
|||||||
"event": "الحدث",
|
"event": "الحدث",
|
||||||
"service_data": "بيانات الخدمة"
|
"service_data": "بيانات الخدمة"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"learn_more": "معرفة المزيد عن الإجراءات"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -515,7 +532,15 @@
|
|||||||
"change_password": "تغيير كلمة السر",
|
"change_password": "تغيير كلمة السر",
|
||||||
"activate_user": "تفعيل المستخدم",
|
"activate_user": "تفعيل المستخدم",
|
||||||
"deactivate_user": "إلغاء تنشيط المستخدم",
|
"deactivate_user": "إلغاء تنشيط المستخدم",
|
||||||
"delete_user": "حذف المستخدم"
|
"delete_user": "حذف المستخدم",
|
||||||
|
"caption": "عرض المستخدم"
|
||||||
|
},
|
||||||
|
"add_user": {
|
||||||
|
"caption": "أضف مستخدم",
|
||||||
|
"name": "الاسم",
|
||||||
|
"username": "اسم المستخدم",
|
||||||
|
"password": "كلمه السر",
|
||||||
|
"create": "إنشاء"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
@ -540,7 +565,24 @@
|
|||||||
"hub": "متصل من خلال",
|
"hub": "متصل من خلال",
|
||||||
"firmware": "نظام التشغيل {version}",
|
"firmware": "نظام التشغيل {version}",
|
||||||
"device_unavailable": "الجهاز غير متوفر",
|
"device_unavailable": "الجهاز غير متوفر",
|
||||||
"entity_unavailable": "العنصر غير متوفر"
|
"entity_unavailable": "العنصر غير متوفر",
|
||||||
|
"no_area": "لا توجد منطقة"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"person": {
|
||||||
|
"detail": {
|
||||||
|
"device_tracker_pick": "اختر جهاز لتتبع"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"area_registry": {
|
||||||
|
"picker": {
|
||||||
|
"no_areas": "يبدو أنه لا توجد أي مناطق معرفة!",
|
||||||
|
"create_area": "إنشاء منطقة"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zha": {
|
||||||
|
"services": {
|
||||||
|
"updateDeviceName": "تعيين اسم مخصص لهذا الجهاز في سجل الأجهزة."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -599,7 +641,11 @@
|
|||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
"user": {
|
"user": {
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "املأ جميع الحقول المطلوبة"
|
"required_fields": "املأ جميع الحقول المطلوبة",
|
||||||
|
"password_not_match": "كلمة السر غير مطابقة"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"password_confirm": "تأكيد كلمة السر"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -607,10 +653,36 @@
|
|||||||
"editor": {
|
"editor": {
|
||||||
"edit_card": {
|
"edit_card": {
|
||||||
"edit": "تصحيح",
|
"edit": "تصحيح",
|
||||||
"delete": "حذف"
|
"delete": "حذف",
|
||||||
|
"move": "نقل"
|
||||||
},
|
},
|
||||||
"save_config": {
|
"save_config": {
|
||||||
"cancel": "لا يهم"
|
"cancel": "لا يهم"
|
||||||
|
},
|
||||||
|
"raw_editor": {
|
||||||
|
"header": "تعديل",
|
||||||
|
"save": "حفظ",
|
||||||
|
"unsaved_changes": "التغييرات غير محفوظة",
|
||||||
|
"saved": "تم الحفظ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"entity_not_found": "الجهاز غير متوفر: {entity}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page-authorize": {
|
||||||
|
"form": {
|
||||||
|
"providers": {
|
||||||
|
"command_line": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"username": "اسم المستخدم",
|
||||||
|
"password": "كلمة السر"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,6 +753,9 @@
|
|||||||
"target_temperature": "درجة الحرارة المستهدفة",
|
"target_temperature": "درجة الحرارة المستهدفة",
|
||||||
"operation": "تشغيل",
|
"operation": "تشغيل",
|
||||||
"away_mode": "حالة خارج المنزل"
|
"away_mode": "حالة خارج المنزل"
|
||||||
|
},
|
||||||
|
"alarm_control_panel": {
|
||||||
|
"arm_custom_bypass": "تجاوز مخصص"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
@ -750,7 +825,8 @@
|
|||||||
"updater": "تحديث",
|
"updater": "تحديث",
|
||||||
"weblink": "Weblink",
|
"weblink": "Weblink",
|
||||||
"zwave": "Z-Wave",
|
"zwave": "Z-Wave",
|
||||||
"vacuum": "مكنسة كهرباء"
|
"vacuum": "مكنسة كهرباء",
|
||||||
|
"person": "شخص"
|
||||||
},
|
},
|
||||||
"attribute": {
|
"attribute": {
|
||||||
"weather": {
|
"weather": {
|
||||||
@ -758,5 +834,19 @@
|
|||||||
"visibility": "الرؤية",
|
"visibility": "الرؤية",
|
||||||
"wind_speed": "سرعة الرياح"
|
"wind_speed": "سرعة الرياح"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"state_attributes": {
|
||||||
|
"climate": {
|
||||||
|
"fan_mode": {
|
||||||
|
"off": "مطفأ",
|
||||||
|
"on": "مفعل",
|
||||||
|
"auto": "آلي"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "مسؤولين",
|
||||||
|
"system-users": "مستخدمين",
|
||||||
|
"system-read-only": "مستخدمين للعرض فقط"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -270,6 +270,10 @@
|
|||||||
"active": "активен",
|
"active": "активен",
|
||||||
"idle": "неработещ",
|
"idle": "неработещ",
|
||||||
"paused": "в пауза"
|
"paused": "в пауза"
|
||||||
|
},
|
||||||
|
"person": {
|
||||||
|
"home": "Вкъщи",
|
||||||
|
"not_home": "Отсъства"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"state_badge": {
|
"state_badge": {
|
||||||
@ -292,6 +296,10 @@
|
|||||||
"device_tracker": {
|
"device_tracker": {
|
||||||
"home": "Вкъщи",
|
"home": "Вкъщи",
|
||||||
"not_home": "Отсъства"
|
"not_home": "Отсъства"
|
||||||
|
},
|
||||||
|
"person": {
|
||||||
|
"home": "Вкъщи",
|
||||||
|
"not_home": "Отсъства"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ui": {
|
"ui": {
|
||||||
@ -313,7 +321,7 @@
|
|||||||
"empty": "Нямате съобщения",
|
"empty": "Нямате съобщения",
|
||||||
"playback_title": "Възпроизвеждане на съобщение",
|
"playback_title": "Възпроизвеждане на съобщение",
|
||||||
"delete_prompt": "Изтриване на това съобщение?",
|
"delete_prompt": "Изтриване на това съобщение?",
|
||||||
"delete_button": "Изтрий"
|
"delete_button": "Изтриване"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"header": "Конфигуриране на Home Assistant",
|
"header": "Конфигуриране на Home Assistant",
|
||||||
@ -362,23 +370,24 @@
|
|||||||
"description": "Създаване и редактиране на автоматизации",
|
"description": "Създаване и редактиране на автоматизации",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Редактор на автоматизации",
|
"header": "Редактор на автоматизации",
|
||||||
"introduction": "Редакторът на автоматизации позволява да създавате и редактирате автоматизации. Моля, прочетете [указанията](https:\/\/home-assistant.io\/docs\/automation\/editor\/), за да се уверите, че Home Assistant е правилно конфигуриран.",
|
"introduction": "Редакторът на автоматизации позволява да създавате и редактирате автоматизации. Моля, последвайте препратката по-долу за да прочетете инструкциите за да се уверите, че Home Assistant е правилно конфигуриран.",
|
||||||
"pick_automation": "Изберете автоматизация за редактиране",
|
"pick_automation": "Изберете автоматизация за редактиране",
|
||||||
"no_automations": "Не можахме да намерим никакви автоматизации подлежащи на редакция",
|
"no_automations": "Не можахме да намерим никакви автоматизации подлежащи на редакция",
|
||||||
"add_automation": "Добавяне на автоматизация"
|
"add_automation": "Добавяне на автоматизация",
|
||||||
|
"learn_more": "Научете повече за автоматизациите"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"introduction": "Използвайте автоматизации, за да съживите дома си",
|
"introduction": "Използвайте автоматизации, за да съживите дома си",
|
||||||
"default_name": "Нова автоматизация",
|
"default_name": "Нова автоматизация",
|
||||||
"save": "Запази",
|
"save": "Запазване",
|
||||||
"unsaved_confirm": "Имате незапазени промени. Сигурни ли сте, че искате да напуснете?",
|
"unsaved_confirm": "Имате незапазени промени. Сигурни ли сте, че искате да напуснете?",
|
||||||
"alias": "Име",
|
"alias": "Име",
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"header": "Тригери",
|
"header": "Тригери",
|
||||||
"introduction": "Тригерите са това, което стартира обработката на правило за автоматизация. Възможно е да се зададат множество тригери за едно и също правило. След като тригера стартира, Home Assistant ще провери условията, ако има такива, и ще стартира действието. \n\n[Научете повече за тригерите.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
"introduction": "Тригерите са това, което стартира обработката на правило за автоматизация. Възможно е да се зададат множество тригери за едно и също правило. След като тригера стартира, Home Assistant ще провери условията, ако има такива, и ще стартира действието.",
|
||||||
"add": "Добавяне на тригер",
|
"add": "Добавяне на тригер",
|
||||||
"duplicate": "Копирай",
|
"duplicate": "Копиране",
|
||||||
"delete": "Изтрий",
|
"delete": "Изтриване",
|
||||||
"delete_confirm": "Сигурни ли сте, че искате да изтриете?",
|
"delete_confirm": "Сигурни ли сте, че искате да изтриете?",
|
||||||
"unsupported_platform": "Неподдържана платформа: {platform}",
|
"unsupported_platform": "Неподдържана платформа: {platform}",
|
||||||
"type_select": "Тип на тригера",
|
"type_select": "Тип на тригера",
|
||||||
@ -452,14 +461,15 @@
|
|||||||
"enter": "Влизане",
|
"enter": "Влизане",
|
||||||
"leave": "Излизане"
|
"leave": "Излизане"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"learn_more": "Научете повече за тригерите"
|
||||||
},
|
},
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"header": "Условия",
|
"header": "Условия",
|
||||||
"introduction": "Условията са незадължителна част от правилото за автоматизация и могат да се използват, за да се предотврати настъпването на действие, когато се задейства тригера. Условията изглеждат много близки до тригерите, но са много различни. Тригерът ще разглежда събитията, случващи се в системата, докато условието разглежда само как системата изглежда в момента. Тригера може да наблюдава включването на ключ. Условието може само да види дали ключ е включен или изключен в момента. \n\n[Научете повече за условията.](https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
"introduction": "Условията са незадължителна част от правилото за автоматизация и могат да се използват, за да се предотврати настъпването на действие, когато се задейства тригера. Условията изглеждат много близки до тригерите, но са много различни. Тригерът ще разглежда събитията, случващи се в системата, докато условието разглежда само как системата изглежда в момента. Тригера може да наблюдава включването на ключ. Условието може само да види дали ключ е включен или изключен в момента.",
|
||||||
"add": "Добавяне на условие",
|
"add": "Добавяне на условие",
|
||||||
"duplicate": "Копирай",
|
"duplicate": "Копиране",
|
||||||
"delete": "Изтрий",
|
"delete": "Изтриване",
|
||||||
"delete_confirm": "Сигурни ли сте, че искате да изтриете?",
|
"delete_confirm": "Сигурни ли сте, че искате да изтриете?",
|
||||||
"unsupported_condition": "Неподдържано условие: {condition}",
|
"unsupported_condition": "Неподдържано условие: {condition}",
|
||||||
"type_select": "Тип на условие",
|
"type_select": "Тип на условие",
|
||||||
@ -497,11 +507,12 @@
|
|||||||
"entity": "Обект с местоположение",
|
"entity": "Обект с местоположение",
|
||||||
"zone": "Зона"
|
"zone": "Зона"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"learn_more": "Научете повече за условията"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"header": "Действия",
|
"header": "Действия",
|
||||||
"introduction": "Действията са това, което Home Assistant ще направи, когато се активира автоматизацията. \n\n[Научете повече за действията.](https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
"introduction": "Действията са това, което Home Assistant ще направи, когато се активира автоматизацията.",
|
||||||
"add": "Добавяне на действие",
|
"add": "Добавяне на действие",
|
||||||
"duplicate": "Копиране",
|
"duplicate": "Копиране",
|
||||||
"delete": "Изтриване",
|
"delete": "Изтриване",
|
||||||
@ -530,7 +541,8 @@
|
|||||||
"event": "Събитие",
|
"event": "Събитие",
|
||||||
"service_data": "Сервизна информация"
|
"service_data": "Сервизна информация"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"learn_more": "Научете повече за действията"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -561,18 +573,18 @@
|
|||||||
"name": "Име",
|
"name": "Име",
|
||||||
"username": "Потребителско име",
|
"username": "Потребителско име",
|
||||||
"password": "Парола",
|
"password": "Парола",
|
||||||
"create": "Създай"
|
"create": "Създаване"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
"caption": "HomeHome Assistant Cloud",
|
"caption": "Home Assistant Cloud",
|
||||||
"description_login": "Влезли сте като {email}",
|
"description_login": "Влезли сте като {email}",
|
||||||
"description_not_login": "Не сте влезли"
|
"description_not_login": "Не сте влезли"
|
||||||
},
|
},
|
||||||
"integrations": {
|
"integrations": {
|
||||||
"caption": "Интеграции",
|
"caption": "Интеграции",
|
||||||
"description": "Управление на свързани устройства и услуги",
|
"description": "Управление на свързани устройства и услуги",
|
||||||
"discovered": "Открит",
|
"discovered": "Открити",
|
||||||
"configured": "Конфигуриран",
|
"configured": "Конфигуриран",
|
||||||
"new": "Настройте нова интеграция",
|
"new": "Настройте нова интеграция",
|
||||||
"configure": "Конфигуриране",
|
"configure": "Конфигуриране",
|
||||||
@ -585,8 +597,8 @@
|
|||||||
"manuf": "от {manufacturer}",
|
"manuf": "от {manufacturer}",
|
||||||
"hub": "Свързан чрез",
|
"hub": "Свързан чрез",
|
||||||
"firmware": "Фърмуер: {version}",
|
"firmware": "Фърмуер: {version}",
|
||||||
"device_unavailable": "Устройството е недостъпно",
|
"device_unavailable": "недостъпно устройство",
|
||||||
"entity_unavailable": "Недостъпен",
|
"entity_unavailable": "недостъпен",
|
||||||
"no_area": "Без област"
|
"no_area": "Без област"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -594,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Управление на Zigbee мрежата за домашна автоматизация",
|
"description": "Управление на Zigbee мрежата за домашна автоматизация",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "Преконфигурирайте ZHA устройство (оздравяване на устройство). Използвайте това, ако имате проблеми с устройството. Ако въпросното устройство е захранвано с батерии, моля, уверете се, че е будно и приема команди, когато използвате тази услуга."
|
"reconfigure": "Преконфигурирайте ZHA устройство (оздравяване на устройство). Използвайте това, ако имате проблеми с устройството. Ако въпросното устройство е захранвано с батерии, моля, уверете се, че е будно и приема команди, когато използвате тази услуга.",
|
||||||
|
"updateDeviceName": "Задайте персонализирано име за това устройство в регистъра на устройствата."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -603,7 +616,10 @@
|
|||||||
"picker": {
|
"picker": {
|
||||||
"header": "Регистър на областите",
|
"header": "Регистър на областите",
|
||||||
"introduction": "Областите се използват за организиране на местоположението на устройствата. Тази информация ще се използва в Home Assistant, за да ви помогне при организирането на Вашия интерфейс, права за достъп и интеграции с други системи.",
|
"introduction": "Областите се използват за организиране на местоположението на устройствата. Тази информация ще се използва в Home Assistant, за да ви помогне при организирането на Вашия интерфейс, права за достъп и интеграции с други системи.",
|
||||||
"introduction2": "За да поставите устройства в дадена област, използвайте връзката по-долу, за да отидете на страницата за интеграции, след което кликнете върху конфигурирана интеграция, за да видите картите на устройството."
|
"introduction2": "За да поставите устройства в дадена област, използвайте връзката по-долу, за да отидете на страницата за интеграции, след което кликнете върху конфигурирана интеграция, за да видите картите на устройството.",
|
||||||
|
"integrations_page": "Интеграции",
|
||||||
|
"no_areas": "Изглежда, че все още нямате области!",
|
||||||
|
"create_area": "СЪЗДАВАНЕ НА ОБЛАСТ"
|
||||||
},
|
},
|
||||||
"no_areas": "Изглежда, че все още нямате области!",
|
"no_areas": "Изглежда, че все още нямате области!",
|
||||||
"create_area": "СЪЗДАВАНЕ НА ОБЛАСТ",
|
"create_area": "СЪЗДАВАНЕ НА ОБЛАСТ",
|
||||||
@ -621,7 +637,8 @@
|
|||||||
"header": "Регистър на обектите",
|
"header": "Регистър на обектите",
|
||||||
"unavailable": "(недостъпен)",
|
"unavailable": "(недостъпен)",
|
||||||
"introduction": "Home Assistant поддържа регистър на всички обекти, които някога е виждал, които могат да бъдат идентифицирани уникално. Всеки от тези обекти ще има идентификатор на обект, който ще бъде резервиран само за този обект.",
|
"introduction": "Home Assistant поддържа регистър на всички обекти, които някога е виждал, които могат да бъдат идентифицирани уникално. Всеки от тези обекти ще има идентификатор на обект, който ще бъде резервиран само за този обект.",
|
||||||
"introduction2": "Използвайте регистъра на обектите, за да промените името, идентификатора на обекта или да премахнете записа от Home Assistant. Моля имайте на предвид, че премахването на записа от регистъра на обектите няма да премахне обекта. За да направите това, следвайте препратката по-долу и я премахнете от страницата за интеграции."
|
"introduction2": "Използвайте регистъра на обектите, за да промените името, идентификатора на обекта или да премахнете записа от Home Assistant. Моля имайте на предвид, че премахването на записа от регистъра на обектите няма да премахне обекта. За да направите това, следвайте препратката по-долу и я премахнете от страницата за интеграции.",
|
||||||
|
"integrations_page": "Интеграции"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"unavailable": "Този обект не е достъпeн към момента.",
|
"unavailable": "Този обект не е достъпeн към момента.",
|
||||||
@ -696,7 +713,7 @@
|
|||||||
"new_password": "Нова парола",
|
"new_password": "Нова парола",
|
||||||
"confirm_new_password": "Потвърждение на новата парола",
|
"confirm_new_password": "Потвърждение на новата парола",
|
||||||
"error_required": "Задължително",
|
"error_required": "Задължително",
|
||||||
"submit": "Изпращане"
|
"submit": "Промяна"
|
||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"header": "Модули за много-факторна аутентикация",
|
"header": "Модули за много-факторна аутентикация",
|
||||||
@ -816,11 +833,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Име",
|
"name": "Име",
|
||||||
"username": "Потребителско име",
|
"username": "Потребителско име",
|
||||||
"password": "Парола"
|
"password": "Парола",
|
||||||
|
"password_confirm": "Потвърждение на парола"
|
||||||
},
|
},
|
||||||
"create_account": "Създай акаунт",
|
"create_account": "Създай акаунт",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "Попълнете всички задължителни полета"
|
"required_fields": "Попълнете всички задължителни полета",
|
||||||
|
"password_not_match": "Паролите не съвпадат"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -828,7 +847,7 @@
|
|||||||
"cards": {
|
"cards": {
|
||||||
"shopping-list": {
|
"shopping-list": {
|
||||||
"checked_items": "Отметнати артикули",
|
"checked_items": "Отметнати артикули",
|
||||||
"clear_items": "Изтрий отметнатите артикули",
|
"clear_items": "Изчистване на отметнатите артикули",
|
||||||
"add_item": "Добави артикул"
|
"add_item": "Добави артикул"
|
||||||
},
|
},
|
||||||
"empty_state": {
|
"empty_state": {
|
||||||
@ -840,13 +859,13 @@
|
|||||||
"editor": {
|
"editor": {
|
||||||
"edit_card": {
|
"edit_card": {
|
||||||
"header": "Конфигуриране на Карта",
|
"header": "Конфигуриране на Карта",
|
||||||
"save": "Запази",
|
"save": "Запазване",
|
||||||
"toggle_editor": "Превключете редактора",
|
"toggle_editor": "Превключете редактора",
|
||||||
"pick_card": "Изберете картата, която искате да добавите.",
|
"pick_card": "Изберете картата, която искате да добавите.",
|
||||||
"add": "Добавяне на карта",
|
"add": "Добавяне на карта",
|
||||||
"edit": "Редактиране",
|
"edit": "Редактиране",
|
||||||
"delete": "Изтриване",
|
"delete": "Изтриване",
|
||||||
"move": "Премести"
|
"move": "Преместване"
|
||||||
},
|
},
|
||||||
"migrate": {
|
"migrate": {
|
||||||
"header": "Несъвместима конфигурация",
|
"header": "Несъвместима конфигурация",
|
||||||
@ -870,6 +889,12 @@
|
|||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"raw_editor": "Текстов редактор на конфурацията"
|
"raw_editor": "Текстов редактор на конфурацията"
|
||||||
|
},
|
||||||
|
"raw_editor": {
|
||||||
|
"header": "Редактиране на конфигурацията",
|
||||||
|
"save": "Запазване",
|
||||||
|
"unsaved_changes": "Незапазени промени",
|
||||||
|
"saved": "Запазено"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
@ -891,7 +916,7 @@
|
|||||||
"common": {
|
"common": {
|
||||||
"loading": "Зареждане",
|
"loading": "Зареждане",
|
||||||
"cancel": "Отмени",
|
"cancel": "Отмени",
|
||||||
"save": "Запази"
|
"save": "Запазване"
|
||||||
},
|
},
|
||||||
"duration": {
|
"duration": {
|
||||||
"day": "{count}{count, plural,\n one {ден}\n other {дни}\n}",
|
"day": "{count}{count, plural,\n one {ден}\n other {дни}\n}",
|
||||||
@ -1048,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
"more_info_settings": {
|
"more_info_settings": {
|
||||||
"save": "Запази",
|
"save": "Запазване",
|
||||||
"name": "Име",
|
"name": "Име",
|
||||||
"entity_id": "Идентификация на обект"
|
"entity_id": "Идентификация на обект"
|
||||||
},
|
},
|
||||||
@ -1118,7 +1143,8 @@
|
|||||||
"hassio": "Hass.io",
|
"hassio": "Hass.io",
|
||||||
"homeassistant": "Home Assistant",
|
"homeassistant": "Home Assistant",
|
||||||
"lovelace": "Lovelace",
|
"lovelace": "Lovelace",
|
||||||
"system_health": "Здраве на системата"
|
"system_health": "Здраве на системата",
|
||||||
|
"person": "Човек"
|
||||||
},
|
},
|
||||||
"attribute": {
|
"attribute": {
|
||||||
"weather": {
|
"weather": {
|
||||||
@ -1135,5 +1161,10 @@
|
|||||||
"auto": "Автоматичен"
|
"auto": "Автоматичен"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "Администратори",
|
||||||
|
"system-users": "Потребители",
|
||||||
|
"system-read-only": "Потребители с достъп само за четене"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -616,7 +616,8 @@
|
|||||||
"header": "Område registrering",
|
"header": "Område registrering",
|
||||||
"introduction": "Områder bruges til at organisere hvor enhederne er. Disse oplysninger vil blive brugt i Home Assistant til at hjælpe dig med at organisere din grænseflade, tilladelser og integrationer med andre systemer.",
|
"introduction": "Områder bruges til at organisere hvor enhederne er. Disse oplysninger vil blive brugt i Home Assistant til at hjælpe dig med at organisere din grænseflade, tilladelser og integrationer med andre systemer.",
|
||||||
"introduction2": "Hvis du vil placere enheder i et område, skal du bruge linket herunder til at navigere til integrationssiden og derefter klikke på en konfigureret integration for at komme til enhedskortene.",
|
"introduction2": "Hvis du vil placere enheder i et område, skal du bruge linket herunder til at navigere til integrationssiden og derefter klikke på en konfigureret integration for at komme til enhedskortene.",
|
||||||
"integrations_page": "Integrations side"
|
"integrations_page": "Integrations side",
|
||||||
|
"create_area": "OPRET OMRÅDE"
|
||||||
},
|
},
|
||||||
"no_areas": "Du ikke har ingen områder endnu!",
|
"no_areas": "Du ikke har ingen områder endnu!",
|
||||||
"create_area": "OPRET OMRÅDE",
|
"create_area": "OPRET OMRÅDE",
|
||||||
@ -1156,5 +1157,9 @@
|
|||||||
"auto": "Automatisk"
|
"auto": "Automatisk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "Administratorer",
|
||||||
|
"system-users": "Brugere"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,10 +29,10 @@
|
|||||||
"armed_home": "Σπίτι Οπλισμένο",
|
"armed_home": "Σπίτι Οπλισμένο",
|
||||||
"armed_away": "Οπλισμένος μακριά",
|
"armed_away": "Οπλισμένος μακριά",
|
||||||
"armed_night": "Οπλισμένο βράδυ",
|
"armed_night": "Οπλισμένο βράδυ",
|
||||||
"pending": "εκκρεμής",
|
"pending": "Εκκρεμής",
|
||||||
"arming": "Όπλιση",
|
"arming": "Όπλιση",
|
||||||
"disarming": "Αφόπλιση",
|
"disarming": "Αφόπλιση",
|
||||||
"triggered": "παραβίαση",
|
"triggered": "Παραβίαση",
|
||||||
"armed_custom_bypass": "Προσαρμοσμένη παράκαμψη ενεργή"
|
"armed_custom_bypass": "Προσαρμοσμένη παράκαμψη ενεργή"
|
||||||
},
|
},
|
||||||
"automation": {
|
"automation": {
|
||||||
@ -126,16 +126,16 @@
|
|||||||
"on": "Ενεργοποιημένο"
|
"on": "Ενεργοποιημένο"
|
||||||
},
|
},
|
||||||
"camera": {
|
"camera": {
|
||||||
"recording": "Εγγραφή",
|
"recording": "Καταγράφει",
|
||||||
"streaming": "Μετάδοση Ροής",
|
"streaming": "Μετάδοση Ροής",
|
||||||
"idle": "Σε αδράνεια"
|
"idle": "Αδρανές"
|
||||||
},
|
},
|
||||||
"climate": {
|
"climate": {
|
||||||
"off": "Ανενεργό",
|
"off": "Ανενεργό",
|
||||||
"on": "Ενεργό",
|
"on": "Ενεργό",
|
||||||
"heat": "Θερμό",
|
"heat": "Θερμό",
|
||||||
"cool": "Δροσερό",
|
"cool": "Δροσερό",
|
||||||
"idle": "Σε αδράνεια",
|
"idle": "Αδρανές",
|
||||||
"auto": "Αυτόματο",
|
"auto": "Αυτόματο",
|
||||||
"dry": "Ξηρό",
|
"dry": "Ξηρό",
|
||||||
"fan_only": "Ανεμιστήρας μόνο",
|
"fan_only": "Ανεμιστήρας μόνο",
|
||||||
@ -144,7 +144,7 @@
|
|||||||
"performance": "Απόδοση",
|
"performance": "Απόδοση",
|
||||||
"high_demand": "Υψηλή ζήτηση",
|
"high_demand": "Υψηλή ζήτηση",
|
||||||
"heat_pump": "Αντλία θερμότητας",
|
"heat_pump": "Αντλία θερμότητας",
|
||||||
"gas": "Φυσικού αερίου",
|
"gas": "Αέριο",
|
||||||
"manual": "Εγχειρίδιο"
|
"manual": "Εγχειρίδιο"
|
||||||
},
|
},
|
||||||
"configurator": {
|
"configurator": {
|
||||||
@ -287,7 +287,7 @@
|
|||||||
"armed_home": "Οπλισμένο",
|
"armed_home": "Οπλισμένο",
|
||||||
"armed_away": "Οπλισμένο",
|
"armed_away": "Οπλισμένο",
|
||||||
"armed_night": "Οπλισμένο",
|
"armed_night": "Οπλισμένο",
|
||||||
"pending": "Εκκρεμής",
|
"pending": "Εκκρ",
|
||||||
"arming": "Όπλιση",
|
"arming": "Όπλιση",
|
||||||
"disarming": "Αφόπλιση",
|
"disarming": "Αφόπλιση",
|
||||||
"triggered": "Ενεργ",
|
"triggered": "Ενεργ",
|
||||||
@ -606,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Διαχείριση του δικτύου ZigBee Home Automation",
|
"description": "Διαχείριση του δικτύου ZigBee Home Automation",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "Ρυθμίστε ξανά τη συσκευή ZHA (heal συσκευή). Χρησιμοποιήστε αυτήν την επιλογή εάν αντιμετωπίζετε ζητήματα με τη συσκευή. Εάν η συγκεκριμένη συσκευή τροφοδοτείται απο μπαταρία βεβαιωθείτε ότι είναι ενεργοποιημένη και δέχεται εντολές όταν χρησιμοποιείτε αυτή την υπηρεσία."
|
"reconfigure": "Ρυθμίστε ξανά τη συσκευή ZHA (heal συσκευή). Χρησιμοποιήστε αυτήν την επιλογή εάν αντιμετωπίζετε ζητήματα με τη συσκευή. Εάν η συγκεκριμένη συσκευή τροφοδοτείται απο μπαταρία βεβαιωθείτε ότι είναι ενεργοποιημένη και δέχεται εντολές όταν χρησιμοποιείτε αυτή την υπηρεσία.",
|
||||||
|
"updateDeviceName": "Ορίστε ένα προσαρμοσμένο όνομα γι αυτήν τη συσκευή στο μητρώο συσκευών."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -616,7 +617,9 @@
|
|||||||
"header": "Περιοχή Μητρώου",
|
"header": "Περιοχή Μητρώου",
|
||||||
"introduction": "Οι περιοχές χρησιμοποιούνται για την οργάνωση της τοποθεσίας των συσκευών. Αυτές οι πληροφορίες θα χρησιμοποιηθούν σε όλο το Home Assistant για να σας βοηθήσουν στην οργάνωση της διασύνδεσης, των αδειών και των ενσωματώσεών σας σε άλλα συστήματα.",
|
"introduction": "Οι περιοχές χρησιμοποιούνται για την οργάνωση της τοποθεσίας των συσκευών. Αυτές οι πληροφορίες θα χρησιμοποιηθούν σε όλο το Home Assistant για να σας βοηθήσουν στην οργάνωση της διασύνδεσης, των αδειών και των ενσωματώσεών σας σε άλλα συστήματα.",
|
||||||
"introduction2": "Για να τοποθετήσετε συσκευές σε μια περιοχή, χρησιμοποιήστε τον παρακάτω σύνδεσμο για να μεταβείτε στη σελίδα ενοποιήσεων και στη συνέχεια κάντε κλικ στην ρυθμισμένη ενωποίηση για να μεταβείτε στις κάρτες της συσκευής.",
|
"introduction2": "Για να τοποθετήσετε συσκευές σε μια περιοχή, χρησιμοποιήστε τον παρακάτω σύνδεσμο για να μεταβείτε στη σελίδα ενοποιήσεων και στη συνέχεια κάντε κλικ στην ρυθμισμένη ενωποίηση για να μεταβείτε στις κάρτες της συσκευής.",
|
||||||
"integrations_page": "Σελίδα ενσωματώσεων"
|
"integrations_page": "Σελίδα ενσωματώσεων",
|
||||||
|
"no_areas": "Φαίνεται ότι δεν έχετε ορίσει ακόμα κάποια περιοχή!",
|
||||||
|
"create_area": "ΔΗΜΙΟΥΡΓΙΑ ΠΕΡΙΟΧΗΣ"
|
||||||
},
|
},
|
||||||
"no_areas": "Φαίνεται ότι δεν έχετε ορίσει ακόμα κάποια περιοχή!",
|
"no_areas": "Φαίνεται ότι δεν έχετε ορίσει ακόμα κάποια περιοχή!",
|
||||||
"create_area": "ΔΗΜΙΟΥΡΓΙΑ ΠΕΡΙΟΧΗΣ",
|
"create_area": "ΔΗΜΙΟΥΡΓΙΑ ΠΕΡΙΟΧΗΣ",
|
||||||
@ -830,11 +833,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Όνομα",
|
"name": "Όνομα",
|
||||||
"username": "Όνομα χρήστη",
|
"username": "Όνομα χρήστη",
|
||||||
"password": "Κωδικός"
|
"password": "Κωδικός",
|
||||||
|
"password_confirm": "Επιβεβαίωση Κωδικού"
|
||||||
},
|
},
|
||||||
"create_account": "Δημιουργία Λογαριασμού",
|
"create_account": "Δημιουργία Λογαριασμού",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "Συμπληρώστε όλα τα υποχρεωτικά πεδία"
|
"required_fields": "Συμπληρώστε όλα τα υποχρεωτικά πεδία",
|
||||||
|
"password_not_match": "Οι κωδικοί πρόσβασης δεν ταιριάζουν"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1156,5 +1161,10 @@
|
|||||||
"auto": "Αυτόματο"
|
"auto": "Αυτόματο"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "Διαχειριστές",
|
||||||
|
"system-users": "Χρήστες",
|
||||||
|
"system-read-only": "Χρήστες μόνο για ανάγνωση"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -606,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Gestion de réseau domotique ZigBee",
|
"description": "Gestion de réseau domotique ZigBee",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "Reconfigurer le périphérique ZHA. Utilisez cette option si vous rencontrez des problèmes avec le périphérique. Si l'appareil en question est un appareil alimenté par batterie, assurez-vous qu'il soit allumé et qu'il accepte les commandes lorsque vous utilisez ce service."
|
"reconfigure": "Reconfigurer le périphérique ZHA. Utilisez cette option si vous rencontrez des problèmes avec le périphérique. Si l'appareil en question est un appareil alimenté par batterie, assurez-vous qu'il soit allumé et qu'il accepte les commandes lorsque vous utilisez ce service.",
|
||||||
|
"updateDeviceName": "Définissez un nom personnalisé pour ce périphérique dans le registre de périphériques."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -616,7 +617,9 @@
|
|||||||
"header": "Registre des pièces",
|
"header": "Registre des pièces",
|
||||||
"introduction": "Les zones sont utilisées pour organiser l'emplacement des périphériques. Ces informations seront utilisées partout dans Home Assistant pour vous aider à organiser votre interface, vos autorisations et vos intégrations avec d'autres systèmes.",
|
"introduction": "Les zones sont utilisées pour organiser l'emplacement des périphériques. Ces informations seront utilisées partout dans Home Assistant pour vous aider à organiser votre interface, vos autorisations et vos intégrations avec d'autres systèmes.",
|
||||||
"introduction2": "Pour placer des périphériques dans une zone, utilisez le lien ci-dessous pour accéder à la page des intégrations, puis cliquez sur une intégration configurée pour accéder aux cartes de périphérique.",
|
"introduction2": "Pour placer des périphériques dans une zone, utilisez le lien ci-dessous pour accéder à la page des intégrations, puis cliquez sur une intégration configurée pour accéder aux cartes de périphérique.",
|
||||||
"integrations_page": "Page des intégrations"
|
"integrations_page": "Page des intégrations",
|
||||||
|
"no_areas": "Vous n'avez pas encore configuré de pièce !",
|
||||||
|
"create_area": "CRÉER UNE PIÈCE"
|
||||||
},
|
},
|
||||||
"no_areas": "Vous n'avez pas encore configuré de pièce !",
|
"no_areas": "Vous n'avez pas encore configuré de pièce !",
|
||||||
"create_area": "CRÉER UNE PIÈCE",
|
"create_area": "CRÉER UNE PIÈCE",
|
||||||
@ -830,11 +833,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"password": "Mot de passe"
|
"password": "Mot de passe",
|
||||||
|
"password_confirm": "Confirmez le mot de passe"
|
||||||
},
|
},
|
||||||
"create_account": "Créer un compte",
|
"create_account": "Créer un compte",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "Remplissez tous les champs requis"
|
"required_fields": "Remplissez tous les champs requis",
|
||||||
|
"password_not_match": "Les mots de passe ne correspondent pas"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1156,5 +1161,10 @@
|
|||||||
"auto": "Auto"
|
"auto": "Auto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "Administrateurs",
|
||||||
|
"system-users": "Utilisateurs",
|
||||||
|
"system-read-only": "Utilisateurs en lecture seule"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -616,7 +616,8 @@
|
|||||||
"header": "Gebiedenregister",
|
"header": "Gebiedenregister",
|
||||||
"introduction": "Gebieden worden gebruikt om te bepalen waar apparaten zijn. Deze informatie wordt overal in de Home Assistant gebruikt om u te helpen bij het organiseren van uw interface, machtigingen en integraties met andere systemen.",
|
"introduction": "Gebieden worden gebruikt om te bepalen waar apparaten zijn. Deze informatie wordt overal in de Home Assistant gebruikt om u te helpen bij het organiseren van uw interface, machtigingen en integraties met andere systemen.",
|
||||||
"introduction2": "Als u apparaten in een gebied wilt plaatsen, gebruikt u de onderstaande koppeling om naar de integratiespagina te gaan en vervolgens op een geconfigureerde integratie te klikken om naar de apparaatkaarten te gaan.",
|
"introduction2": "Als u apparaten in een gebied wilt plaatsen, gebruikt u de onderstaande koppeling om naar de integratiespagina te gaan en vervolgens op een geconfigureerde integratie te klikken om naar de apparaatkaarten te gaan.",
|
||||||
"integrations_page": "Integratiespagina"
|
"integrations_page": "Integratiespagina",
|
||||||
|
"create_area": "MAAK RUIMTE"
|
||||||
},
|
},
|
||||||
"no_areas": "Het lijkt erop dat je nog geen gebieden hebt!",
|
"no_areas": "Het lijkt erop dat je nog geen gebieden hebt!",
|
||||||
"create_area": "MAAK GEBIED",
|
"create_area": "MAAK GEBIED",
|
||||||
@ -830,11 +831,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Naam",
|
"name": "Naam",
|
||||||
"username": "Gebruikersnaam",
|
"username": "Gebruikersnaam",
|
||||||
"password": "Wachtwoord"
|
"password": "Wachtwoord",
|
||||||
|
"password_confirm": "Bevestig wachtwoord"
|
||||||
},
|
},
|
||||||
"create_account": "Account aanmaken",
|
"create_account": "Account aanmaken",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "Vul alle verplichte velden in"
|
"required_fields": "Vul alle verplichte velden in",
|
||||||
|
"password_not_match": "Wachtwoorden komen niet overeen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1156,5 +1159,8 @@
|
|||||||
"auto": "Auto"
|
"auto": "Auto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-users": "Gebruikers"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -370,7 +370,7 @@
|
|||||||
"description": "Twórz i edytuj reguły automatyzacji",
|
"description": "Twórz i edytuj reguły automatyzacji",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Edytor automatyzacji",
|
"header": "Edytor automatyzacji",
|
||||||
"introduction": "Edytor automatyzacji pozwala tworzyć i edytować reguły automatyzacji. Przeczytaj [instrukcje](https:\/\/home-assistant.io\/docs\/automation\/editor\/), aby upewnić się, że poprawnie skonfigurowałeś Home Assistant'a.",
|
"introduction": "Edytor automatyzacji pozwala tworzyć i edytować reguły automatyzacji. Kliknij poniższy link, aby przeczytać instrukcję, jak poprawnie skonfigurować reguły automatyzacji w Home Assistant.",
|
||||||
"pick_automation": "Wybierz regułę automatyzacji do edycji",
|
"pick_automation": "Wybierz regułę automatyzacji do edycji",
|
||||||
"no_automations": "Nie znaleziono żadnych edytowalnych reguł automatyzacji",
|
"no_automations": "Nie znaleziono żadnych edytowalnych reguł automatyzacji",
|
||||||
"add_automation": "Dodaj regułę automatyzacji",
|
"add_automation": "Dodaj regułę automatyzacji",
|
||||||
|
@ -606,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Управляйте сетью Zigbee Home Automation",
|
"description": "Управляйте сетью Zigbee Home Automation",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "Перенастройка устройства ZHA. Используйте эту службу, если у Вас есть проблемы с устройством. Если рассматриваемое устройство работает от батареи, пожалуйста, убедитесь, что оно не находится в режиме сна и принимает команды, когда вы запускаете эту службу."
|
"reconfigure": "Перенастройка устройства ZHA. Используйте эту службу, если у Вас есть проблемы с устройством. Если рассматриваемое устройство работает от батареи, пожалуйста, убедитесь, что оно не находится в режиме сна и принимает команды, когда вы запускаете эту службу.",
|
||||||
|
"updateDeviceName": "Установите имя для этого устройства в реестре устройств."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -616,9 +617,11 @@
|
|||||||
"header": "Управление помещениями",
|
"header": "Управление помещениями",
|
||||||
"introduction": "Этот раздел используется для определения местоположения устройств. Данная информация будет использоваться в Home Assistant, чтобы помочь вам в организации вашего интерфейса, определении прав доступа и интеграции с другими системами.",
|
"introduction": "Этот раздел используется для определения местоположения устройств. Данная информация будет использоваться в Home Assistant, чтобы помочь вам в организации вашего интерфейса, определении прав доступа и интеграции с другими системами.",
|
||||||
"introduction2": "Чтобы назначить устройству местоположение, используйте указанную ниже ссылку для перехода на страницу интеграций, а затем откройте уже настроенную интеграцию.",
|
"introduction2": "Чтобы назначить устройству местоположение, используйте указанную ниже ссылку для перехода на страницу интеграций, а затем откройте уже настроенную интеграцию.",
|
||||||
"integrations_page": "Страница интеграций"
|
"integrations_page": "Страница интеграций",
|
||||||
|
"no_areas": "У Вас еще нет добавленных помещений.",
|
||||||
|
"create_area": "ДОБАВИТЬ ПОМЕЩЕНИЕ"
|
||||||
},
|
},
|
||||||
"no_areas": "Похоже, что у Вас пока ещё нет добавленных помещений!",
|
"no_areas": "У Вас еще нет добавленных помещений.",
|
||||||
"create_area": "ДОБАВИТЬ",
|
"create_area": "ДОБАВИТЬ",
|
||||||
"editor": {
|
"editor": {
|
||||||
"default_name": "Новое помещение",
|
"default_name": "Новое помещение",
|
||||||
@ -830,11 +833,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "Имя",
|
"name": "Имя",
|
||||||
"username": "Логин",
|
"username": "Логин",
|
||||||
"password": "Пароль"
|
"password": "Пароль",
|
||||||
|
"password_confirm": "Подтвердите пароль"
|
||||||
},
|
},
|
||||||
"create_account": "Создать учетную запись",
|
"create_account": "Создать учетную запись",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "Заполните все обязательные поля"
|
"required_fields": "Заполните все обязательные поля",
|
||||||
|
"password_not_match": "Пароли не совпадают"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -847,7 +852,7 @@
|
|||||||
},
|
},
|
||||||
"empty_state": {
|
"empty_state": {
|
||||||
"title": "Добро пожаловать домой",
|
"title": "Добро пожаловать домой",
|
||||||
"no_devices": "Эта страница позволяет вам управлять вашими устройствами, однако, похоже, у вас еще нет настроенных устройств. Перейдите на страницу интеграций, чтобы начать.",
|
"no_devices": "На этой странице можно управлять Вашими устройствами, однако похоже, что ни одно устройство еще не добавлено. Для начала перейдите на страницу интеграций.",
|
||||||
"go_to_integrations_page": "Перейти на страницу интеграций"
|
"go_to_integrations_page": "Перейти на страницу интеграций"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1156,5 +1161,10 @@
|
|||||||
"auto": "Авто"
|
"auto": "Авто"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "Администраторы",
|
||||||
|
"system-users": "Пользователи",
|
||||||
|
"system-read-only": "Системные пользователи"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -82,7 +82,7 @@
|
|||||||
},
|
},
|
||||||
"presence": {
|
"presence": {
|
||||||
"off": "ไม่อยู่",
|
"off": "ไม่อยู่",
|
||||||
"on": "อยูบ้าน"
|
"on": "อยู่บ้าน"
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
"off": "ปกติ",
|
"off": "ปกติ",
|
||||||
@ -173,11 +173,11 @@
|
|||||||
"not_home": "ไม่อยู่บ้าน",
|
"not_home": "ไม่อยู่บ้าน",
|
||||||
"open": "เปิด",
|
"open": "เปิด",
|
||||||
"opening": "กำลังเปิด",
|
"opening": "กำลังเปิด",
|
||||||
"closed": "ปิด",
|
"closed": "ปิดแล้ว",
|
||||||
"closing": "กำลังปิด",
|
"closing": "กำลังปิด",
|
||||||
"stopped": "หยุด",
|
"stopped": "หยุดแล้ว",
|
||||||
"locked": "ล็อค",
|
"locked": "ล็อคแล้ว",
|
||||||
"unlocked": "ปลดล็อค",
|
"unlocked": "ปลดล็อคแล้ว",
|
||||||
"ok": "พร้อมใช้งาน",
|
"ok": "พร้อมใช้งาน",
|
||||||
"problem": "มีปัญหา"
|
"problem": "มีปัญหา"
|
||||||
},
|
},
|
||||||
@ -236,7 +236,7 @@
|
|||||||
"ready": "พร้อมใช้งาน"
|
"ready": "พร้อมใช้งาน"
|
||||||
},
|
},
|
||||||
"query_stage": {
|
"query_stage": {
|
||||||
"initializing": "กำลังเริ่มต้น ({query_stage})",
|
"initializing": "กำลังเตรียมการ ({query_stage})",
|
||||||
"dead": "ไม่พร้อมใช้งาน ({query_stage})"
|
"dead": "ไม่พร้อมใช้งาน ({query_stage})"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -284,9 +284,9 @@
|
|||||||
"alarm_control_panel": {
|
"alarm_control_panel": {
|
||||||
"armed": "เปิดการป้องกัน",
|
"armed": "เปิดการป้องกัน",
|
||||||
"disarmed": "ปลดการป้องกัน",
|
"disarmed": "ปลดการป้องกัน",
|
||||||
"armed_home": "เปิดการป้องกัน-โหมดอยู่บ้าน",
|
"armed_home": "เฝ้าระวังอยู่",
|
||||||
"armed_away": "เปิดการป้องกัน-โหมดไม่อยู่บ้าน",
|
"armed_away": "เฝ้าระวังอยู่",
|
||||||
"armed_night": "เปิดการป้องกัน-โหมดกลางคืน",
|
"armed_night": "เฝ้าระวังอยู่",
|
||||||
"pending": "ค้างอยู่",
|
"pending": "ค้างอยู่",
|
||||||
"arming": "กำลังเปิดการป้องกัน",
|
"arming": "กำลังเปิดการป้องกัน",
|
||||||
"disarming": "กำลังปลดการป้องกัน",
|
"disarming": "กำลังปลดการป้องกัน",
|
||||||
@ -370,7 +370,7 @@
|
|||||||
"description": "สร้างและแก้ไขระบบอัตโนมัติ",
|
"description": "สร้างและแก้ไขระบบอัตโนมัติ",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "เครื่องมือแก้ไข ระบบอัตโนมัติ",
|
"header": "เครื่องมือแก้ไข ระบบอัตโนมัติ",
|
||||||
"introduction": "ตัวแก้ไขอัตโนมัติช่วยให้คุณสามารถสร้างและแก้ไขระบบอัตโนมัติได้. \nโปรดอ่าน [คำแนะนำ] (https:\/\/home-assistant.io\/docs\/automation\/editor\/) เพื่อให้แน่ใจว่าคุณได้กำหนดค่า Home Assistant อย่างถูกต้อง.",
|
"introduction": "เป็นตัวช่วยแก้ไขที่ทำให้คุณสามารถสร้างหรือแก้ไขการทำงานอัตโนมัติ\nโปรดอ่าน [คำแนะนำ] (https:\/\/home-assistant.io\/docs\/automation\/editor\/) เพื่อให้การกำหนดค่าของคุณเป็นไปได้อย่างถูกต้อง",
|
||||||
"pick_automation": "เลือกระบบอัตโนมัติเพื่อแก้ไข",
|
"pick_automation": "เลือกระบบอัตโนมัติเพื่อแก้ไข",
|
||||||
"no_automations": "เราไม่พบระบบอัตโนมัติใด ๆ สามารถแก้ไขได้",
|
"no_automations": "เราไม่พบระบบอัตโนมัติใด ๆ สามารถแก้ไขได้",
|
||||||
"add_automation": "เพิ่มระบบอัตโนมัติ",
|
"add_automation": "เพิ่มระบบอัตโนมัติ",
|
||||||
@ -471,7 +471,7 @@
|
|||||||
"duplicate": "แยกออกมาเป็นอันใหม่",
|
"duplicate": "แยกออกมาเป็นอันใหม่",
|
||||||
"delete": "ลบ",
|
"delete": "ลบ",
|
||||||
"delete_confirm": "คุณแน่ใจหรือไม่ว่าจะลบสิ่งนี้ทิ้ง?",
|
"delete_confirm": "คุณแน่ใจหรือไม่ว่าจะลบสิ่งนี้ทิ้ง?",
|
||||||
"unsupported_condition": "เงื่อนไขที่ไม่สนับสนุน: {condition}",
|
"unsupported_condition": "ไม่รองรับเงื่อนไข: {condition}",
|
||||||
"type_select": "ประเภทเงื่อนไข",
|
"type_select": "ประเภทเงื่อนไข",
|
||||||
"type": {
|
"type": {
|
||||||
"state": {
|
"state": {
|
||||||
@ -485,7 +485,7 @@
|
|||||||
"value_template": "ค่าของรูปแบบ (ปล่อยว่างได้)"
|
"value_template": "ค่าของรูปแบบ (ปล่อยว่างได้)"
|
||||||
},
|
},
|
||||||
"sun": {
|
"sun": {
|
||||||
"label": "พระอาทิตย์",
|
"label": "ดวงอาทิตย์",
|
||||||
"before": "ก่อน:",
|
"before": "ก่อน:",
|
||||||
"after": "หลังจาก:",
|
"after": "หลังจาก:",
|
||||||
"before_offset": "ก่อนช่วงเวลา(เลือกเพิ่ม)",
|
"before_offset": "ก่อนช่วงเวลา(เลือกเพิ่ม)",
|
||||||
@ -512,7 +512,7 @@
|
|||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"header": "การกระทำ",
|
"header": "การกระทำ",
|
||||||
"introduction": "การดำเนินการเป็นสิ่งที่ผู้ช่วยโฮมจะทำเมื่อระบบอัตโนมัติถูกเรียกใช้งาน \n\n [เรียนรู้เพิ่มเติมเกี่ยวกับการดำเนินการ] (https:\/\/home-assistant.io\/docs\/automation\/action\/)",
|
"introduction": "ใช้สำหรับการกระทำที่ Home Assistant จะทำต่อเมื่อระบบอัตโนมัติมีการเรียกใช้งาน",
|
||||||
"add": "เพิ่มการกระทำ",
|
"add": "เพิ่มการกระทำ",
|
||||||
"duplicate": "แยกออกมาเป็นอันใหม่",
|
"duplicate": "แยกออกมาเป็นอันใหม่",
|
||||||
"delete": "ลบ",
|
"delete": "ลบ",
|
||||||
@ -606,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "การจัดการระบบอัติโนมัติของ Zigbee",
|
"description": "การจัดการระบบอัติโนมัติของ Zigbee",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "กำหนดค่าอุปกรณ์ ZHA อีกครั้ง (เพื่อรักษาอุปกรณ์) ใช้สิ่งนี้หากคุณมีปัญหากับอุปกรณ์ หากอุปกรณ์ดังกล่าวเป็นอุปกรณ์ที่ใช้พลังงานจากแบตเตอรี่โปรดตรวจสอบให้แน่ใจว่าอุปกรณ์นั้นเปิดอยู่และยอมรับคำสั่งเมื่อคุณใช้บริการ"
|
"reconfigure": "กำหนดค่าอุปกรณ์ ZHA อีกครั้ง (เพื่อรักษาอุปกรณ์) ใช้สิ่งนี้หากคุณมีปัญหากับอุปกรณ์ หากอุปกรณ์ดังกล่าวเป็นอุปกรณ์ที่ใช้พลังงานจากแบตเตอรี่โปรดตรวจสอบให้แน่ใจว่าอุปกรณ์นั้นเปิดอยู่และยอมรับคำสั่งเมื่อคุณใช้บริการ",
|
||||||
|
"updateDeviceName": "ตั้งชื่อที่กำหนดเองสำหรับอุปกรณ์นี้ในการลงทะเบียนอุปกรณ์"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -616,7 +617,9 @@
|
|||||||
"header": "ค่าของห้อง",
|
"header": "ค่าของห้อง",
|
||||||
"introduction": "Areas are used to organize where devices are. This information will be used throughout Home Assistant to help you in organizing your interface, permissions and integrations with other systems.",
|
"introduction": "Areas are used to organize where devices are. This information will be used throughout Home Assistant to help you in organizing your interface, permissions and integrations with other systems.",
|
||||||
"introduction2": "สำหรับการวางอุปกรณ์นี้ลงในห้องนี้ ใช้ลิงค์ด้านล่างเพื่อไปยังหน้า 'การทำงานร่วมกัน' และคลิ๊กทีุ่่ม 'ตั้งค่าให้ทำงานร่วมกัน' เพื่อที่จะให้แสดงการ์ดสำหรับอุปกรณ์นั้น",
|
"introduction2": "สำหรับการวางอุปกรณ์นี้ลงในห้องนี้ ใช้ลิงค์ด้านล่างเพื่อไปยังหน้า 'การทำงานร่วมกัน' และคลิ๊กทีุ่่ม 'ตั้งค่าให้ทำงานร่วมกัน' เพื่อที่จะให้แสดงการ์ดสำหรับอุปกรณ์นั้น",
|
||||||
"integrations_page": "หน้าการทำงานร่วมกัน"
|
"integrations_page": "หน้าการทำงานร่วมกัน",
|
||||||
|
"no_areas": "ดูเหมือนว่าคุณยังไม่มีห้องเลย!",
|
||||||
|
"create_area": "สร้างห้องใหม่"
|
||||||
},
|
},
|
||||||
"no_areas": "ดูเหมือนว่าคุณยังไม่มีห้องเลย!",
|
"no_areas": "ดูเหมือนว่าคุณยังไม่มีห้องเลย!",
|
||||||
"create_area": "สร้างห้องใหม่",
|
"create_area": "สร้างห้องใหม่",
|
||||||
@ -829,11 +832,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "ชื่อ",
|
"name": "ชื่อ",
|
||||||
"username": "ชื่อผู้ใช้",
|
"username": "ชื่อผู้ใช้",
|
||||||
"password": "รหัสผ่าน"
|
"password": "รหัสผ่าน",
|
||||||
|
"password_confirm": "ยืนยันรหัสผ่าน"
|
||||||
},
|
},
|
||||||
"create_account": "สร้างบัญชี",
|
"create_account": "สร้างบัญชี",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "กรอกข้อมูลในฟิลด์ที่จำเป็นทั้งหมด"
|
"required_fields": "กรอกข้อมูลในฟิลด์ที่จำเป็นทั้งหมด",
|
||||||
|
"password_not_match": "รหัสผ่านไม่ตรงกัน"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1154,5 +1159,10 @@
|
|||||||
"auto": "อัตโนมัติ"
|
"auto": "อัตโนมัติ"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "ผู้ดูแลระบบ",
|
||||||
|
"system-users": "ผู้ใช้",
|
||||||
|
"system-read-only": "ผู้ใช้ที่สามารถดูได้อย่างเดียว"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -606,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Zigbee 智能家居(ZHA) 网络管理",
|
"description": "Zigbee 智能家居(ZHA) 网络管理",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "重新配置ZHA设备(唤醒设备)。如果您的设备遇到问题,请使用此项。如果有问题的设备是电池供电的,请确保在使用此服务时它处于唤醒状态并可以接受指令。"
|
"reconfigure": "重新配置ZHA设备(唤醒设备)。如果您的设备遇到问题,请使用此项。如果有问题的设备是电池供电的,请确保在使用此服务时它处于唤醒状态并可以接受指令。",
|
||||||
|
"updateDeviceName": "在设备注册表中为此设备设置自定义名称。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -616,7 +617,9 @@
|
|||||||
"header": "区域注册表",
|
"header": "区域注册表",
|
||||||
"introduction": "区域用于组织设备所在的位置。此信息将用于 Home Assistant 的各个地方,以帮助您组织界面、权限和与其他系统的集成。",
|
"introduction": "区域用于组织设备所在的位置。此信息将用于 Home Assistant 的各个地方,以帮助您组织界面、权限和与其他系统的集成。",
|
||||||
"introduction2": "要将设备置入某个区域,请使用下面的链接导航到集成页面,然后点击一个已配置的集成以进入设备卡片。",
|
"introduction2": "要将设备置入某个区域,请使用下面的链接导航到集成页面,然后点击一个已配置的集成以进入设备卡片。",
|
||||||
"integrations_page": "集成页面"
|
"integrations_page": "集成页面",
|
||||||
|
"no_areas": "看来你还没有建立区域!",
|
||||||
|
"create_area": "创建区域"
|
||||||
},
|
},
|
||||||
"no_areas": "看来你还没有建立区域!",
|
"no_areas": "看来你还没有建立区域!",
|
||||||
"create_area": "创建区域",
|
"create_area": "创建区域",
|
||||||
@ -830,11 +833,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "姓名",
|
"name": "姓名",
|
||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"password": "密码"
|
"password": "密码",
|
||||||
|
"password_confirm": "确认密码"
|
||||||
},
|
},
|
||||||
"create_account": "创建帐户",
|
"create_account": "创建帐户",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "请填写所有必填字段"
|
"required_fields": "请填写所有必填字段",
|
||||||
|
"password_not_match": "密码不匹配"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1156,5 +1161,10 @@
|
|||||||
"auto": "自动"
|
"auto": "自动"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "管理员",
|
||||||
|
"system-users": "用户",
|
||||||
|
"system-read-only": "只读用户"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -606,7 +606,8 @@
|
|||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Zigbee 家庭自動化網路管理",
|
"description": "Zigbee 家庭自動化網路管理",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "重新設定 ZHA Zibgee 裝置(健康裝置)。假如遇到裝置問題,請使用此選項。假如有問題的裝置為使用電池的裝置,請先確定裝置已喚醒並處於接受命令狀態。"
|
"reconfigure": "重新設定 ZHA Zibgee 裝置(健康裝置)。假如遇到裝置問題,請使用此選項。假如有問題的裝置為使用電池的裝置,請先確定裝置已喚醒並處於接受命令狀態。",
|
||||||
|
"updateDeviceName": "於物件 ID 中自訂此設備名稱。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -616,7 +617,9 @@
|
|||||||
"header": "分區 ID",
|
"header": "分區 ID",
|
||||||
"introduction": "分區主要用以管理裝置所在位置。此資訊將會於 Home Assistant 中使用以協助您管理介面、權限,並與其他系統進行整合。",
|
"introduction": "分區主要用以管理裝置所在位置。此資訊將會於 Home Assistant 中使用以協助您管理介面、權限,並與其他系統進行整合。",
|
||||||
"introduction2": "欲於分區中放置裝置,請使用下方連結至整合頁面,並點選設定整合以設定裝置卡片。",
|
"introduction2": "欲於分區中放置裝置,請使用下方連結至整合頁面,並點選設定整合以設定裝置卡片。",
|
||||||
"integrations_page": "整合頁面"
|
"integrations_page": "整合頁面",
|
||||||
|
"no_areas": "看起來你還沒有建立分區!",
|
||||||
|
"create_area": "建立分區"
|
||||||
},
|
},
|
||||||
"no_areas": "看起來你還沒有建立分區!",
|
"no_areas": "看起來你還沒有建立分區!",
|
||||||
"create_area": "建立分區",
|
"create_area": "建立分區",
|
||||||
@ -830,11 +833,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"name": "名字",
|
"name": "名字",
|
||||||
"username": "使用者名稱",
|
"username": "使用者名稱",
|
||||||
"password": "使用者密碼"
|
"password": "使用者密碼",
|
||||||
|
"password_confirm": "確認密碼"
|
||||||
},
|
},
|
||||||
"create_account": "創建帳號",
|
"create_account": "創建帳號",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "填寫所有所需欄位"
|
"required_fields": "填寫所有所需欄位",
|
||||||
|
"password_not_match": "密碼不相符"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1156,5 +1161,10 @@
|
|||||||
"auto": "自動模式"
|
"auto": "自動模式"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "管理員",
|
||||||
|
"system-users": "用戶",
|
||||||
|
"system-read-only": "唯讀用戶"
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user