mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-02 14:07:55 +00:00
commit
8daeaab40b
@ -22,6 +22,7 @@ const BUILT_IN_PANEL_ICONS = [
|
|||||||
"mailbox", // Mailbox
|
"mailbox", // Mailbox
|
||||||
"tooltip-account", // Map
|
"tooltip-account", // Map
|
||||||
"cart", // Shopping List
|
"cart", // Shopping List
|
||||||
|
"hammer", // developer-tools
|
||||||
];
|
];
|
||||||
|
|
||||||
// Given an icon name, load the SVG file
|
// Given an icon name, load the SVG file
|
||||||
|
@ -36,6 +36,7 @@ customElements.get("paper-icon-button").prototype._keyBindings = {};
|
|||||||
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@property() public panel!: HassioPanelInfo;
|
@property() public panel!: HassioPanelInfo;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
protected routerOptions: RouterOptions = {
|
protected routerOptions: RouterOptions = {
|
||||||
// Hass.io has a page with tabs, so we route all non-matching routes to it.
|
// Hass.io has a page with tabs, so we route all non-matching routes to it.
|
||||||
@ -108,6 +109,7 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
// As long as we have Polymer pages
|
// As long as we have Polymer pages
|
||||||
(el as PolymerElement).setProperties({
|
(el as PolymerElement).setProperties({
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
|
narrow: this.narrow,
|
||||||
supervisorInfo: this._supervisorInfo,
|
supervisorInfo: this._supervisorInfo,
|
||||||
hostInfo: this._hostInfo,
|
hostInfo: this._hostInfo,
|
||||||
hassInfo: this._hassInfo,
|
hassInfo: this._hassInfo,
|
||||||
@ -115,6 +117,7 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
el.hass = this.hass;
|
el.hass = this.hass;
|
||||||
|
el.narrow = this.narrow;
|
||||||
el.supervisorInfo = this._supervisorInfo;
|
el.supervisorInfo = this._supervisorInfo;
|
||||||
el.hostInfo = this._hostInfo;
|
el.hostInfo = this._hostInfo;
|
||||||
el.hassInfo = this._hassInfo;
|
el.hassInfo = this._hassInfo;
|
||||||
|
@ -34,6 +34,7 @@ const HAS_REFRESH_BUTTON = ["store", "snapshots"];
|
|||||||
@customElement("hassio-pages-with-tabs")
|
@customElement("hassio-pages-with-tabs")
|
||||||
class HassioPagesWithTabs extends LitElement {
|
class HassioPagesWithTabs extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
@property() public supervisorInfo!: HassioSupervisorInfo;
|
@property() public supervisorInfo!: HassioSupervisorInfo;
|
||||||
@property() public hostInfo!: HassioHostInfo;
|
@property() public hostInfo!: HassioHostInfo;
|
||||||
@ -45,7 +46,11 @@ class HassioPagesWithTabs extends LitElement {
|
|||||||
<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 hassio></ha-menu-button>
|
<ha-menu-button
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
hassio
|
||||||
|
></ha-menu-button>
|
||||||
<div main-title>Hass.io</div>
|
<div main-title>Hass.io</div>
|
||||||
${HAS_REFRESH_BUTTON.includes(page)
|
${HAS_REFRESH_BUTTON.includes(page)
|
||||||
? html`
|
? html`
|
||||||
|
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="20190627.0",
|
version="20190630.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",
|
||||||
|
@ -102,6 +102,7 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
switch (domain) {
|
switch (domain) {
|
||||||
case "binary_sensor":
|
case "binary_sensor":
|
||||||
case "device_tracker":
|
case "device_tracker":
|
||||||
|
case "person":
|
||||||
case "updater":
|
case "updater":
|
||||||
case "sun":
|
case "sun":
|
||||||
case "alarm_control_panel":
|
case "alarm_control_panel":
|
||||||
|
@ -5,34 +5,113 @@ import {
|
|||||||
LitElement,
|
LitElement,
|
||||||
html,
|
html,
|
||||||
customElement,
|
customElement,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import { subscribeNotifications } from "../data/persistent_notification";
|
||||||
|
import computeDomain from "../common/entity/compute_domain";
|
||||||
|
|
||||||
@customElement("ha-menu-button")
|
@customElement("ha-menu-button")
|
||||||
class HaMenuButton extends LitElement {
|
class HaMenuButton extends LitElement {
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean }) public hassio = false;
|
||||||
public hassio = false;
|
@property() public narrow!: boolean;
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() private _hasNotifications = false;
|
||||||
|
private _attachNotifOnConnect = false;
|
||||||
|
private _unsubNotifications?: UnsubscribeFunc;
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (this._attachNotifOnConnect) {
|
||||||
|
this._attachNotifOnConnect = false;
|
||||||
|
this._subscribeNotifications();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
if (this._unsubNotifications) {
|
||||||
|
this._attachNotifOnConnect = true;
|
||||||
|
this._unsubNotifications();
|
||||||
|
this._unsubNotifications = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
|
const hasNotifications =
|
||||||
|
this.narrow &&
|
||||||
|
(this._hasNotifications ||
|
||||||
|
Object.keys(this.hass.states).some(
|
||||||
|
(entityId) => computeDomain(entityId) === "configurator"
|
||||||
|
));
|
||||||
return html`
|
return html`
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
aria-label="Sidebar Toggle"
|
aria-label="Sidebar Toggle"
|
||||||
.icon=${this.hassio ? "hassio:menu" : "hass:menu"}
|
.icon=${this.hassio ? "hassio:menu" : "hass:menu"}
|
||||||
@click=${this._toggleMenu}
|
@click=${this._toggleMenu}
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
|
${hasNotifications
|
||||||
|
? html`
|
||||||
|
<div class="dot"></div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are not going to use ShadowDOM as we're rendering a single element
|
protected updated(changedProps) {
|
||||||
// without any CSS used.
|
super.updated(changedProps);
|
||||||
protected createRenderRoot(): Element | ShadowRoot {
|
|
||||||
return this;
|
if (!changedProps.has("narrow")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.style.visibility = this.narrow ? "initial" : "hidden";
|
||||||
|
|
||||||
|
if (!this.narrow) {
|
||||||
|
this._hasNotifications = false;
|
||||||
|
if (this._unsubNotifications) {
|
||||||
|
this._unsubNotifications();
|
||||||
|
this._unsubNotifications = undefined;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._subscribeNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _subscribeNotifications() {
|
||||||
|
this._unsubNotifications = subscribeNotifications(
|
||||||
|
this.hass.connection,
|
||||||
|
(notifications) => {
|
||||||
|
this._hasNotifications = notifications.length > 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleMenu(): void {
|
private _toggleMenu(): void {
|
||||||
fireEvent(this, "hass-toggle-menu");
|
fireEvent(this, "hass-toggle-menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.dot {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
top: 8px;
|
||||||
|
right: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -14,7 +14,7 @@ import "@polymer/paper-listbox/paper-listbox";
|
|||||||
import "./ha-icon";
|
import "./ha-icon";
|
||||||
|
|
||||||
import "../components/user/ha-user-badge";
|
import "../components/user/ha-user-badge";
|
||||||
import isComponentLoaded from "../common/config/is_component_loaded";
|
import "../components/ha-menu-button";
|
||||||
import { HomeAssistant, PanelInfo } from "../types";
|
import { HomeAssistant, PanelInfo } from "../types";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { DEFAULT_PANEL } from "../common/const";
|
import { DEFAULT_PANEL } from "../common/const";
|
||||||
@ -22,30 +22,30 @@ import {
|
|||||||
getExternalConfig,
|
getExternalConfig,
|
||||||
ExternalConfig,
|
ExternalConfig,
|
||||||
} from "../external_app/external_config";
|
} from "../external_app/external_config";
|
||||||
|
import {
|
||||||
|
PersistentNotification,
|
||||||
|
subscribeNotifications,
|
||||||
|
} from "../data/persistent_notification";
|
||||||
|
import computeDomain from "../common/entity/compute_domain";
|
||||||
|
|
||||||
|
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
||||||
|
|
||||||
const computeUrl = (urlPath) => `/${urlPath}`;
|
const computeUrl = (urlPath) => `/${urlPath}`;
|
||||||
|
|
||||||
const computePanels = (hass: HomeAssistant) => {
|
const SORT_VALUE = {
|
||||||
const panels = hass.panels;
|
|
||||||
if (!panels) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const sortValue = {
|
|
||||||
map: 1,
|
map: 1,
|
||||||
logbook: 2,
|
logbook: 2,
|
||||||
history: 3,
|
history: 3,
|
||||||
};
|
"developer-tools": 9,
|
||||||
const result: PanelInfo[] = Object.values(panels).filter(
|
configuration: 10,
|
||||||
(panel) => panel.title
|
};
|
||||||
);
|
|
||||||
|
|
||||||
result.sort((a, b) => {
|
const panelSorter = (a, b) => {
|
||||||
const aBuiltIn = a.component_name in sortValue;
|
const aBuiltIn = a.component_name in SORT_VALUE;
|
||||||
const bBuiltIn = b.component_name in sortValue;
|
const bBuiltIn = b.component_name in SORT_VALUE;
|
||||||
|
|
||||||
if (aBuiltIn && bBuiltIn) {
|
if (aBuiltIn && bBuiltIn) {
|
||||||
return sortValue[a.component_name] - sortValue[b.component_name];
|
return SORT_VALUE[a.component_name] - SORT_VALUE[b.component_name];
|
||||||
}
|
}
|
||||||
if (aBuiltIn) {
|
if (aBuiltIn) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -61,9 +61,31 @@ const computePanels = (hass: HomeAssistant) => {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const computePanels = (hass: HomeAssistant): [PanelInfo[], PanelInfo[]] => {
|
||||||
|
const panels = hass.panels;
|
||||||
|
if (!panels) {
|
||||||
|
return [[], []];
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeSpacer: PanelInfo[] = [];
|
||||||
|
const afterSpacer: PanelInfo[] = [];
|
||||||
|
|
||||||
|
Object.values(panels).forEach((panel) => {
|
||||||
|
if (!panel.title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(SHOW_AFTER_SPACER.includes(panel.component_name)
|
||||||
|
? afterSpacer
|
||||||
|
: beforeSpacer
|
||||||
|
).push(panel);
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
beforeSpacer.sort(panelSorter);
|
||||||
|
afterSpacer.sort(panelSorter);
|
||||||
|
|
||||||
|
return [beforeSpacer, afterSpacer];
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderPanel = (hass, panel) => html`
|
const renderPanel = (hass, panel) => html`
|
||||||
@ -86,12 +108,15 @@ const renderPanel = (hass, panel) => html`
|
|||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
class HaSidebar extends LitElement {
|
class HaSidebar extends LitElement {
|
||||||
@property() public hass?: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
@property({ type: Boolean }) public alwaysExpand = false;
|
@property({ type: Boolean }) public alwaysExpand = false;
|
||||||
@property({ type: Boolean, reflect: true }) public expanded = false;
|
@property({ type: Boolean, reflect: true }) public expanded = false;
|
||||||
@property() public _defaultPage?: string =
|
@property() public _defaultPage?: string =
|
||||||
localStorage.defaultPage || DEFAULT_PANEL;
|
localStorage.defaultPage || DEFAULT_PANEL;
|
||||||
@property() private _externalConfig?: ExternalConfig;
|
@property() private _externalConfig?: ExternalConfig;
|
||||||
|
@property() private _notifications?: PersistentNotification[];
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const hass = this.hass;
|
const hass = this.hass;
|
||||||
@ -100,29 +125,30 @@ class HaSidebar extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const panels = computePanels(hass);
|
const [beforeSpacer, afterSpacer] = computePanels(hass);
|
||||||
const configPanelIdx = panels.findIndex(
|
|
||||||
(panel) => panel.component_name === "config"
|
let notificationCount = this._notifications
|
||||||
);
|
? this._notifications.length
|
||||||
const configPanel =
|
: 0;
|
||||||
configPanelIdx === -1 ? undefined : panels.splice(configPanelIdx, 1)[0];
|
for (const entityId in hass.states) {
|
||||||
|
if (computeDomain(entityId) === "configurator") {
|
||||||
|
notificationCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.expanded
|
<div class="menu">
|
||||||
|
${!this.narrow
|
||||||
? html`
|
? html`
|
||||||
<app-toolbar>
|
<paper-icon-button
|
||||||
<div main-title>Home Assistant</div>
|
aria-label="Sidebar Toggle"
|
||||||
</app-toolbar>
|
.icon=${hass.dockedSidebar ? "hass:menu-open" : "hass:menu"}
|
||||||
|
@click=${this._toggleSidebar}
|
||||||
|
></paper-icon-button>
|
||||||
`
|
`
|
||||||
: html`
|
: ""}
|
||||||
<div class="logo">
|
<span class="title">Home Assistant</span>
|
||||||
<img
|
|
||||||
id="logo"
|
|
||||||
src="/static/icons/favicon-192x192.png"
|
|
||||||
alt="Home Assistant logo"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
`}
|
|
||||||
|
|
||||||
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}>
|
<paper-listbox attr-for-selected="data-panel" .selected=${hass.panelUrl}>
|
||||||
<a
|
<a
|
||||||
@ -137,69 +163,11 @@ class HaSidebar extends LitElement {
|
|||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
${panels.map((panel) => renderPanel(hass, panel))}
|
${beforeSpacer.map((panel) => renderPanel(hass, panel))}
|
||||||
|
|
||||||
<div class="spacer" disabled></div>
|
<div class="spacer" disabled></div>
|
||||||
|
|
||||||
${this.expanded && hass.user && hass.user.is_admin
|
${afterSpacer.map((panel) => renderPanel(hass, panel))}
|
||||||
? html`
|
|
||||||
<div class="divider" disabled></div>
|
|
||||||
|
|
||||||
<div class="subheader" disabled>
|
|
||||||
${hass.localize("ui.sidebar.developer_tools")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dev-tools" disabled>
|
|
||||||
<a href="/dev-service" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:remote"
|
|
||||||
alt="${hass.localize("panel.dev-services")}"
|
|
||||||
title="${hass.localize("panel.dev-services")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
<a href="/dev-state" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:code-tags"
|
|
||||||
alt="${hass.localize("panel.dev-states")}"
|
|
||||||
title="${hass.localize("panel.dev-states")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
<a href="/dev-event" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:radio-tower"
|
|
||||||
alt="${hass.localize("panel.dev-events")}"
|
|
||||||
title="${hass.localize("panel.dev-events")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
<a href="/dev-template" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:file-xml"
|
|
||||||
alt="${hass.localize("panel.dev-templates")}"
|
|
||||||
title="${hass.localize("panel.dev-templates")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
${isComponentLoaded(hass, "mqtt")
|
|
||||||
? html`
|
|
||||||
<a href="/dev-mqtt" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:altimeter"
|
|
||||||
alt="${hass.localize("panel.dev-mqtt")}"
|
|
||||||
title="${hass.localize("panel.dev-mqtt")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
`
|
|
||||||
: html``}
|
|
||||||
<a href="/dev-info" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:information-outline"
|
|
||||||
alt="${hass.localize("panel.dev-info")}"
|
|
||||||
title="${hass.localize("panel.dev-info")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="divider" disabled></div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
||||||
? html`
|
? html`
|
||||||
<a
|
<a
|
||||||
@ -214,58 +182,61 @@ class HaSidebar extends LitElement {
|
|||||||
slot="item-icon"
|
slot="item-icon"
|
||||||
icon="hass:cellphone-settings-variant"
|
icon="hass:cellphone-settings-variant"
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
<span class="item-text"
|
<span class="item-text">
|
||||||
>${hass.localize(
|
${hass.localize("ui.sidebar.external_app_configuration")}
|
||||||
"ui.sidebar.external_app_configuration"
|
</span>
|
||||||
)}</span
|
|
||||||
>
|
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</a>
|
</a>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${configPanel ? renderPanel(hass, configPanel) : ""}
|
|
||||||
${hass.user
|
<div disabled class="divider sticky-el"></div>
|
||||||
|
|
||||||
|
<paper-icon-item
|
||||||
|
class="notifications sticky-el"
|
||||||
|
aria-role="option"
|
||||||
|
@click=${this._handleShowNotificationDrawer}
|
||||||
|
>
|
||||||
|
<ha-icon slot="item-icon" icon="hass:bell"></ha-icon>
|
||||||
|
${notificationCount > 0
|
||||||
? html`
|
? html`
|
||||||
|
<span class="notification-badge" slot="item-icon">
|
||||||
|
${notificationCount}
|
||||||
|
</span>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<span class="item-text">
|
||||||
|
${hass.localize("ui.notification_drawer.title")}
|
||||||
|
</span>
|
||||||
|
</paper-icon-item>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
class="profile sticky-el"
|
||||||
href="/profile"
|
href="/profile"
|
||||||
data-panel="panel"
|
data-panel="panel"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
aria-role="option"
|
aria-role="option"
|
||||||
aria-label=${hass.localize("panel.profile")}
|
aria-label=${hass.localize("panel.profile")}
|
||||||
>
|
>
|
||||||
<paper-icon-item class="profile">
|
<paper-icon-item>
|
||||||
<ha-user-badge
|
<ha-user-badge slot="item-icon" .user=${hass.user}></ha-user-badge>
|
||||||
slot="item-icon"
|
|
||||||
.user=${hass.user}
|
|
||||||
></ha-user-badge>
|
|
||||||
|
|
||||||
<span class="item-text">
|
<span class="item-text">
|
||||||
${hass.user.name}
|
${hass.user ? hass.user.name : ""}
|
||||||
</span>
|
</span>
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</a>
|
</a>
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<paper-icon-item
|
|
||||||
@click=${this._handleLogOut}
|
|
||||||
class="logout"
|
|
||||||
aria-role="option"
|
|
||||||
>
|
|
||||||
<ha-icon slot="item-icon" icon="hass:exit-to-app"></ha-icon>
|
|
||||||
<span class="item-text"
|
|
||||||
>${hass.localize("ui.sidebar.log_out")}</span
|
|
||||||
>
|
|
||||||
</paper-icon-item>
|
|
||||||
`}
|
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (
|
if (
|
||||||
changedProps.has("_externalConfig") ||
|
|
||||||
changedProps.has("expanded") ||
|
changedProps.has("expanded") ||
|
||||||
changedProps.has("alwaysExpand")
|
changedProps.has("narrow") ||
|
||||||
|
changedProps.has("alwaysExpand") ||
|
||||||
|
changedProps.has("_externalConfig") ||
|
||||||
|
changedProps.has("_notifications")
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -280,9 +251,9 @@ class HaSidebar extends LitElement {
|
|||||||
return (
|
return (
|
||||||
hass.panels !== oldHass.panels ||
|
hass.panels !== oldHass.panels ||
|
||||||
hass.panelUrl !== oldHass.panelUrl ||
|
hass.panelUrl !== oldHass.panelUrl ||
|
||||||
hass.config.components !== oldHass.config.components ||
|
|
||||||
hass.user !== oldHass.user ||
|
hass.user !== oldHass.user ||
|
||||||
hass.localize !== oldHass.localize
|
hass.localize !== oldHass.localize ||
|
||||||
|
hass.states !== oldHass.states
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,15 +264,15 @@ class HaSidebar extends LitElement {
|
|||||||
this._externalConfig = conf;
|
this._externalConfig = conf;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.shadowRoot!.querySelector("paper-listbox")!.addEventListener(
|
this.addEventListener("mouseenter", () => {
|
||||||
"mouseenter",
|
|
||||||
() => {
|
|
||||||
this.expanded = true;
|
this.expanded = true;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
this.addEventListener("mouseleave", () => {
|
this.addEventListener("mouseleave", () => {
|
||||||
this._contract();
|
this._contract();
|
||||||
});
|
});
|
||||||
|
subscribeNotifications(this.hass.connection, (notifications) => {
|
||||||
|
this._notifications = notifications;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
@ -315,17 +286,21 @@ class HaSidebar extends LitElement {
|
|||||||
this.expanded = this.alwaysExpand || false;
|
this.expanded = this.alwaysExpand || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleLogOut() {
|
private _handleShowNotificationDrawer() {
|
||||||
fireEvent(this, "hass-logout");
|
fireEvent(this, "hass-show-notifications");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleExternalAppConfiguration(ev: Event) {
|
private _handleExternalAppConfiguration(ev: Event) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.hass!.auth.external!.fireMessage({
|
this.hass.auth.external!.fireMessage({
|
||||||
type: "config_screen/show",
|
type: "config_screen/show",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _toggleSidebar() {
|
||||||
|
fireEvent(this, "hass-toggle-menu");
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@ -344,31 +319,41 @@ class HaSidebar extends LitElement {
|
|||||||
transition: width 0.2s ease-in;
|
transition: width 0.2s ease-in;
|
||||||
will-change: width;
|
will-change: width;
|
||||||
contain: strict;
|
contain: strict;
|
||||||
|
transition-delay: 0.2s;
|
||||||
}
|
}
|
||||||
:host([expanded]) {
|
:host([expanded]) {
|
||||||
width: 256px;
|
width: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.menu {
|
||||||
height: 65px;
|
height: 64px;
|
||||||
box-sizing: border-box;
|
display: flex;
|
||||||
padding: 8px;
|
padding: 0 12px;
|
||||||
border-bottom: 1px solid transparent;
|
border-bottom: 1px solid transparent;
|
||||||
}
|
|
||||||
.logo img {
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-toolbar {
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
border-bottom: 1px solid var(--divider-color);
|
border-bottom: 1px solid var(--divider-color);
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
|
font-size: 20px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:host([expanded]) .menu {
|
||||||
|
width: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-toolbar a {
|
.menu paper-icon-button {
|
||||||
color: var(--primary-text-color);
|
color: var(--sidebar-icon-color);
|
||||||
|
}
|
||||||
|
:host([expanded]) .menu paper-icon-button {
|
||||||
|
margin-right: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:host([expanded]) .title {
|
||||||
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-listbox {
|
paper-listbox {
|
||||||
@ -435,35 +420,69 @@ class HaSidebar extends LitElement {
|
|||||||
color: var(--sidebar-selected-text-color);
|
color: var(--sidebar-selected-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a .item-text {
|
paper-icon-item .item-text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
:host([expanded]) a .item-text {
|
:host([expanded]) paper-icon-item .item-text {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-icon-item.logout {
|
.divider {
|
||||||
margin-top: 16px;
|
bottom: 88px;
|
||||||
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-icon-item.profile {
|
.divider::before {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notifications {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
bottom: 48px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.profile {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.profile paper-icon-item {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
.profile .item-text {
|
.profile .item-text {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sticky-el {
|
||||||
|
position: sticky;
|
||||||
|
background-color: var(
|
||||||
|
--sidebar-background-color,
|
||||||
|
var(--primary-background-color)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-badge {
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 400;
|
||||||
|
bottom: 14px;
|
||||||
|
left: 26px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0px 6px;
|
||||||
|
font-size: 0.65em;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
|
||||||
height: 1px;
|
|
||||||
background-color: var(--divider-color);
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subheader {
|
.subheader {
|
||||||
color: var(--sidebar-text-color);
|
color: var(--sidebar-text-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
43
src/data/persistent_notification.ts
Normal file
43
src/data/persistent_notification.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
createCollection,
|
||||||
|
Connection,
|
||||||
|
HassEntity,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
|
|
||||||
|
export interface PersitentNotificationEntity extends HassEntity {
|
||||||
|
notification_id?: string;
|
||||||
|
created_at?: string;
|
||||||
|
title?: string;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersistentNotification {
|
||||||
|
created_at: string;
|
||||||
|
message: string;
|
||||||
|
notification_id: string;
|
||||||
|
title: string;
|
||||||
|
status: "read" | "unread";
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchNotifications = (conn) =>
|
||||||
|
conn.sendMessagePromise({
|
||||||
|
type: "persistent_notification/get",
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscribeUpdates = (conn, store) =>
|
||||||
|
conn.subscribeEvents(
|
||||||
|
() => fetchNotifications(conn).then((ntf) => store.setState(ntf, true)),
|
||||||
|
"persistent_notifications_updated"
|
||||||
|
);
|
||||||
|
|
||||||
|
export const subscribeNotifications = (
|
||||||
|
conn: Connection,
|
||||||
|
onChange: (notifications: PersistentNotification[]) => void
|
||||||
|
) =>
|
||||||
|
createCollection<PersistentNotification[]>(
|
||||||
|
"_ntf",
|
||||||
|
fetchNotifications,
|
||||||
|
subscribeUpdates,
|
||||||
|
conn,
|
||||||
|
onChange
|
||||||
|
);
|
@ -1,24 +0,0 @@
|
|||||||
import { createCollection, Connection } from "home-assistant-js-websocket";
|
|
||||||
|
|
||||||
const fetchNotifications = (conn) =>
|
|
||||||
conn.sendMessagePromise({
|
|
||||||
type: "persistent_notification/get",
|
|
||||||
});
|
|
||||||
|
|
||||||
const subscribeUpdates = (conn, store) =>
|
|
||||||
conn.subscribeEvents(
|
|
||||||
() => fetchNotifications(conn).then((ntf) => store.setState(ntf, true)),
|
|
||||||
"persistent_notifications_updated"
|
|
||||||
);
|
|
||||||
|
|
||||||
export const subscribeNotifications = (
|
|
||||||
conn: Connection,
|
|
||||||
onChange: (notifications: Notification[]) => void
|
|
||||||
) =>
|
|
||||||
createCollection<Notification[]>(
|
|
||||||
"_ntf",
|
|
||||||
fetchNotifications,
|
|
||||||
subscribeUpdates,
|
|
||||||
conn,
|
|
||||||
onChange
|
|
||||||
);
|
|
@ -26,9 +26,20 @@ class MoreInfoCamera extends LitElement {
|
|||||||
@property() public hass?: HomeAssistant;
|
@property() public hass?: HomeAssistant;
|
||||||
@property() public stateObj?: CameraEntity;
|
@property() public stateObj?: CameraEntity;
|
||||||
@property() private _cameraPrefs?: CameraPreferences;
|
@property() private _cameraPrefs?: CameraPreferences;
|
||||||
|
@property() private _attached = false;
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._attached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._attached = false;
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this.hass || !this.stateObj) {
|
if (!this._attached || !this.hass || !this.stateObj) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,17 +7,17 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
|
|
||||||
import "./hui-notification-item-template";
|
import "./notification-item-template";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { HassNotification } from "./types";
|
import { PersitentNotificationEntity } from "../../data/persistent_notification";
|
||||||
|
|
||||||
@customElement("hui-configurator-notification-item")
|
@customElement("configurator-notification-item")
|
||||||
export class HuiConfiguratorNotificationItem extends LitElement {
|
export class HuiConfiguratorNotificationItem extends LitElement {
|
||||||
@property() public hass?: HomeAssistant;
|
@property() public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() public notification?: HassNotification;
|
@property() public notification?: PersitentNotificationEntity;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this.hass || !this.notification) {
|
if (!this.hass || !this.notification) {
|
||||||
@ -25,7 +25,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hui-notification-item-template>
|
<notification-item-template>
|
||||||
<span slot="header">${this.hass.localize("domain.configurator")}</span>
|
<span slot="header">${this.hass.localize("domain.configurator")}</span>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -41,7 +41,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
|||||||
`state.configurator.${this.notification.state}`
|
`state.configurator.${this.notification.state}`
|
||||||
)}</mwc-button
|
)}</mwc-button
|
||||||
>
|
>
|
||||||
</hui-notification-item-template>
|
</notification-item-template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +54,6 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-configurator-notification-item": HuiConfiguratorNotificationItem;
|
"configurator-notification-item": HuiConfiguratorNotificationItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,13 +5,14 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "./hui-notification-item";
|
import "./notification-item";
|
||||||
import "../../../../components/ha-paper-icon-button-next";
|
import "../../components/ha-paper-icon-button-next";
|
||||||
|
|
||||||
import { EventsMixin } from "../../../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../../../mixins/localize-mixin";
|
|
||||||
import { computeRTL } from "../../../../common/util/compute_rtl";
|
|
||||||
|
|
||||||
|
import { EventsMixin } from "../../mixins/events-mixin";
|
||||||
|
import LocalizeMixin from "../../mixins/localize-mixin";
|
||||||
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
|
import { subscribeNotifications } from "../../data/persistent_notification";
|
||||||
|
import computeDomain from "../../common/entity/compute_domain";
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
@ -129,7 +130,7 @@ export class HuiNotificationDrawer extends EventsMixin(
|
|||||||
<dom-repeat items="[[notifications]]">
|
<dom-repeat items="[[notifications]]">
|
||||||
<template>
|
<template>
|
||||||
<div class="notification">
|
<div class="notification">
|
||||||
<hui-notification-item hass="[[hass]]" notification="[[item]]"></hui-notification-item>
|
<notification-item hass="[[hass]]" notification="[[item]]"></notification-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dom-repeat>
|
</dom-repeat>
|
||||||
@ -160,6 +161,10 @@ export class HuiNotificationDrawer extends EventsMixin(
|
|||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
|
type: Array,
|
||||||
|
computed: "_computeNotifications(open, hass, _notificationsBackend)",
|
||||||
|
},
|
||||||
|
_notificationsBackend: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: [],
|
value: [],
|
||||||
},
|
},
|
||||||
@ -171,6 +176,16 @@ export class HuiNotificationDrawer extends EventsMixin(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ready() {
|
||||||
|
super.ready();
|
||||||
|
window.addEventListener("location-changed", () => {
|
||||||
|
// close drawer when we navigate away.
|
||||||
|
if (this.open) {
|
||||||
|
this.open = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_closeDrawer(ev) {
|
_closeDrawer(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.open = false;
|
this.open = false;
|
||||||
@ -188,17 +203,44 @@ export class HuiNotificationDrawer extends EventsMixin(
|
|||||||
this._openTimer = setTimeout(() => {
|
this._openTimer = setTimeout(() => {
|
||||||
this.classList.add("open");
|
this.classList.add("open");
|
||||||
}, 50);
|
}, 50);
|
||||||
|
this._unsubNotifications = subscribeNotifications(
|
||||||
|
this.hass.connection,
|
||||||
|
(notifications) => {
|
||||||
|
this._notificationsBackend = notifications;
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Animate closed then hide
|
// Animate closed then hide
|
||||||
this.classList.remove("open");
|
this.classList.remove("open");
|
||||||
this._openTimer = setTimeout(() => {
|
this._openTimer = setTimeout(() => {
|
||||||
this.hidden = true;
|
this.hidden = true;
|
||||||
}, 250);
|
}, 250);
|
||||||
|
if (this._unsubNotifications) {
|
||||||
|
this._unsubNotifications();
|
||||||
|
this._unsubNotifications = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeRTL(hass) {
|
_computeRTL(hass) {
|
||||||
return computeRTL(hass);
|
return computeRTL(hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeNotifications(open, hass, notificationsBackend) {
|
||||||
|
if (!open) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const configuratorEntities = Object.keys(hass.states)
|
||||||
|
.filter((entityId) => computeDomain(entityId) === "configurator")
|
||||||
|
.map((entityId) => hass.states[entityId]);
|
||||||
|
|
||||||
|
return notificationsBackend.concat(configuratorEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog({ narrow }) {
|
||||||
|
this.open = true;
|
||||||
|
this.narrow = narrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-notification-drawer", HuiNotificationDrawer);
|
customElements.define("notification-drawer", HuiNotificationDrawer);
|
@ -7,9 +7,9 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import "../../../../components/ha-card";
|
import "../../components/ha-card";
|
||||||
|
|
||||||
@customElement("hui-notification-item-template")
|
@customElement("notification-item-template")
|
||||||
export class HuiNotificationItemTemplate extends LitElement {
|
export class HuiNotificationItemTemplate extends LitElement {
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
@ -60,6 +60,6 @@ export class HuiNotificationItemTemplate extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-notification-item-template": HuiNotificationItemTemplate;
|
"notification-item-template": HuiNotificationItemTemplate;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,18 +6,19 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
html,
|
html,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
|
||||||
import "./hui-configurator-notification-item";
|
import "./configurator-notification-item";
|
||||||
import "./hui-persistent-notification-item";
|
import "./persistent-notification-item";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { HassNotification } from "./types";
|
import { PersistentNotification } from "../../data/persistent_notification";
|
||||||
|
|
||||||
@customElement("hui-notification-item")
|
@customElement("notification-item")
|
||||||
export class HuiNotificationItem extends LitElement {
|
export class HuiNotificationItem extends LitElement {
|
||||||
@property() public hass?: HomeAssistant;
|
@property() public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() public notification?: HassNotification;
|
@property() public notification?: HassEntity | PersistentNotification;
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
if (!this.hass || !this.notification || changedProps.has("notification")) {
|
if (!this.hass || !this.notification || changedProps.has("notification")) {
|
||||||
@ -32,24 +33,24 @@ export class HuiNotificationItem extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.notification.entity_id
|
return "entity_id" in this.notification
|
||||||
? html`
|
? html`
|
||||||
<hui-configurator-notification-item
|
<configurator-notification-item
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.notification="${this.notification}"
|
.notification="${this.notification}"
|
||||||
></hui-configurator-notification-item>
|
></configurator-notification-item>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<hui-persistent-notification-item
|
<persistent-notification-item
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.notification="${this.notification}"
|
.notification="${this.notification}"
|
||||||
></hui-persistent-notification-item>
|
></persistent-notification-item>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-notification-item": HuiNotificationItem;
|
"notification-item": HuiNotificationItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,18 +10,18 @@ import {
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
|
|
||||||
import "../../../../components/ha-relative-time";
|
import "../../components/ha-relative-time";
|
||||||
import "../../../../components/ha-markdown";
|
import "../../components/ha-markdown";
|
||||||
import "./hui-notification-item-template";
|
import "./notification-item-template";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { HassNotification } from "./types";
|
import { PersistentNotification } from "../../data/persistent_notification";
|
||||||
|
|
||||||
@customElement("hui-persistent-notification-item")
|
@customElement("persistent-notification-item")
|
||||||
export class HuiPersistentNotificationItem extends LitElement {
|
export class HuiPersistentNotificationItem extends LitElement {
|
||||||
@property() public hass?: HomeAssistant;
|
@property() public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() public notification?: HassNotification;
|
@property() public notification?: PersistentNotification;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this.hass || !this.notification) {
|
if (!this.hass || !this.notification) {
|
||||||
@ -29,8 +29,10 @@ export class HuiPersistentNotificationItem extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hui-notification-item-template>
|
<notification-item-template>
|
||||||
<span slot="header">${this._computeTitle(this.notification)}</span>
|
<span slot="header">
|
||||||
|
${this.notification.title || this.notification.notification_id}
|
||||||
|
</span>
|
||||||
|
|
||||||
<ha-markdown content="${this.notification.message}"></ha-markdown>
|
<ha-markdown content="${this.notification.message}"></ha-markdown>
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ export class HuiPersistentNotificationItem extends LitElement {
|
|||||||
"ui.card.persistent_notification.dismiss"
|
"ui.card.persistent_notification.dismiss"
|
||||||
)}</mwc-button
|
)}</mwc-button
|
||||||
>
|
>
|
||||||
</hui-notification-item-template>
|
</notification-item-template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,13 +82,9 @@ export class HuiPersistentNotificationItem extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeTitle(notification: HassNotification): string | undefined {
|
|
||||||
return notification.title || notification.notification_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _computeTooltip(
|
private _computeTooltip(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
notification: HassNotification
|
notification: PersistentNotification
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
if (!hass || !notification) {
|
if (!hass || !notification) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -105,6 +103,6 @@ export class HuiPersistentNotificationItem extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"hui-persistent-notification-item": HuiPersistentNotificationItem;
|
"persistent-notification-item": HuiPersistentNotificationItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
16
src/dialogs/notifications/show-notification-drawer.ts
Normal file
16
src/dialogs/notifications/show-notification-drawer.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
|
export interface NotificationDrawerParams {
|
||||||
|
narrow: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showNotificationDrawer = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: NotificationDrawerParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "notification-drawer" as any, // Not in TS yet
|
||||||
|
dialogImport: () => import("./notification-drawer"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -12,17 +12,23 @@ import {
|
|||||||
import "../components/ha-menu-button";
|
import "../components/ha-menu-button";
|
||||||
import "../components/ha-paper-icon-button-arrow-prev";
|
import "../components/ha-paper-icon-button-arrow-prev";
|
||||||
import { haStyle } from "../resources/styles";
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("hass-loading-screen")
|
@customElement("hass-loading-screen")
|
||||||
class HassLoadingScreen extends LitElement {
|
class HassLoadingScreen extends LitElement {
|
||||||
@property({ type: Boolean }) public rootnav? = false;
|
@property({ type: Boolean }) public rootnav? = false;
|
||||||
|
@property() public hass?: HomeAssistant;
|
||||||
|
@property() public narrow?: boolean;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
${this.rootnav
|
${this.rootnav
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-button></ha-menu-button>
|
<ha-menu-button
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></ha-menu-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-paper-icon-button-arrow-prev
|
<ha-paper-icon-button-arrow-prev
|
||||||
|
@ -15,25 +15,16 @@ class HassSubpage extends LitElement {
|
|||||||
@property()
|
@property()
|
||||||
public header?: string;
|
public header?: string;
|
||||||
|
|
||||||
@property({ type: Boolean })
|
|
||||||
public root = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
public hassio = false;
|
public hassio = false;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
${this.root
|
|
||||||
? html`
|
|
||||||
<ha-menu-button .hassio=${this.hassio}></ha-menu-button>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<ha-paper-icon-button-arrow-prev
|
<ha-paper-icon-button-arrow-prev
|
||||||
.hassio=${this.hassio}
|
.hassio=${this.hassio}
|
||||||
@click=${this._backTapped}
|
@click=${this._backTapped}
|
||||||
></ha-paper-icon-button-arrow-prev>
|
></ha-paper-icon-button-arrow-prev>
|
||||||
`}
|
|
||||||
|
|
||||||
<div main-title>${this.header}</div>
|
<div main-title>${this.header}</div>
|
||||||
<slot name="toolbar-icon"></slot>
|
<slot name="toolbar-icon"></slot>
|
||||||
|
@ -20,6 +20,7 @@ import { fireEvent } from "../common/dom/fire_event";
|
|||||||
import { PolymerChangedEvent } from "../polymer-types";
|
import { PolymerChangedEvent } from "../polymer-types";
|
||||||
// tslint:disable-next-line: no-duplicate-imports
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
import { AppDrawerLayoutElement } from "@polymer/app-layout/app-drawer-layout/app-drawer-layout";
|
import { AppDrawerLayoutElement } from "@polymer/app-layout/app-drawer-layout/app-drawer-layout";
|
||||||
|
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
||||||
|
|
||||||
const NON_SWIPABLE_PANELS = ["kiosk", "map"];
|
const NON_SWIPABLE_PANELS = ["kiosk", "map"];
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ declare global {
|
|||||||
// for fire event
|
// for fire event
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"hass-toggle-menu": undefined;
|
"hass-toggle-menu": undefined;
|
||||||
|
"hass-show-notifications": undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +68,7 @@ class HomeAssistantMain extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-sidebar
|
<ha-sidebar
|
||||||
.hass=${hass}
|
.hass=${hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
.alwaysExpand=${this.narrow || hass.dockedSidebar}
|
.alwaysExpand=${this.narrow || hass.dockedSidebar}
|
||||||
></ha-sidebar>
|
></ha-sidebar>
|
||||||
</app-drawer>
|
</app-drawer>
|
||||||
@ -96,6 +99,12 @@ class HomeAssistantMain extends LitElement {
|
|||||||
setTimeout(() => this.appLayout.resetLayout());
|
setTimeout(() => this.appLayout.resetLayout());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.addEventListener("hass-show-notifications", () => {
|
||||||
|
showNotificationDrawer(this, {
|
||||||
|
narrow: this.narrow!,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} from "./hass-router-page";
|
} from "./hass-router-page";
|
||||||
import { removeInitSkeleton } from "../util/init-skeleton";
|
import { removeInitSkeleton } from "../util/init-skeleton";
|
||||||
|
|
||||||
const CACHE_COMPONENTS = ["lovelace", "states"];
|
const CACHE_COMPONENTS = ["lovelace", "states", "developer-tools"];
|
||||||
const COMPONENTS = {
|
const COMPONENTS = {
|
||||||
calendar: () =>
|
calendar: () =>
|
||||||
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
|
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
|
||||||
@ -17,18 +17,8 @@ const COMPONENTS = {
|
|||||||
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
|
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
|
||||||
custom: () =>
|
custom: () =>
|
||||||
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
|
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
|
||||||
"dev-event": () =>
|
"developer-tools": () =>
|
||||||
import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
|
import(/* webpackChunkName: "panel-developer-tools" */ "../panels/developer-tools/ha-panel-developer-tools"),
|
||||||
"dev-info": () =>
|
|
||||||
import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
|
|
||||||
"dev-mqtt": () =>
|
|
||||||
import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
|
|
||||||
"dev-service": () =>
|
|
||||||
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
|
|
||||||
"dev-state": () =>
|
|
||||||
import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
|
|
||||||
"dev-template": () =>
|
|
||||||
import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
|
|
||||||
lovelace: () =>
|
lovelace: () =>
|
||||||
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
|
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
|
||||||
states: () =>
|
states: () =>
|
||||||
@ -52,14 +42,17 @@ const COMPONENTS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getRoutes = (panels: Panels): RouterOptions => {
|
const getRoutes = (panels: Panels): RouterOptions => {
|
||||||
const routes: { [route: string]: RouteOptions } = {};
|
const routes: RouterOptions["routes"] = {};
|
||||||
|
|
||||||
Object.values(panels).forEach((panel) => {
|
Object.values(panels).forEach((panel) => {
|
||||||
routes[panel.url_path] = {
|
const data: RouteOptions = {
|
||||||
load: COMPONENTS[panel.component_name],
|
|
||||||
tag: `ha-panel-${panel.component_name}`,
|
tag: `ha-panel-${panel.component_name}`,
|
||||||
cache: CACHE_COMPONENTS.includes(panel.component_name),
|
cache: CACHE_COMPONENTS.includes(panel.component_name),
|
||||||
};
|
};
|
||||||
|
if (panel.component_name in COMPONENTS) {
|
||||||
|
data.load = COMPONENTS[panel.component_name];
|
||||||
|
}
|
||||||
|
routes[panel.url_path] = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -93,6 +86,8 @@ class PartialPanelResolver extends HassRouterPage {
|
|||||||
protected createLoadingScreen() {
|
protected createLoadingScreen() {
|
||||||
const el = super.createLoadingScreen();
|
const el = super.createLoadingScreen();
|
||||||
el.rootnav = true;
|
el.rootnav = true;
|
||||||
|
el.hass = this.hass;
|
||||||
|
el.narrow = this.narrow;
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,10 @@ 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
|
||||||
|
hass="[[hass]]"
|
||||||
|
narrow="[[narrow]]"
|
||||||
|
></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>
|
||||||
|
@ -47,7 +47,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></ha-menu-button>
|
<ha-menu-button hass='[[hass]]' narrow='[[narrow]]'></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>
|
||||||
@ -125,6 +125,7 @@ class HaConfigDashboard extends NavigateMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
narrow: Boolean,
|
||||||
isWide: Boolean,
|
isWide: Boolean,
|
||||||
cloudStatus: Object,
|
cloudStatus: Object,
|
||||||
showAdvanced: Boolean,
|
showAdvanced: Boolean,
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
CoreFrontendUserData,
|
CoreFrontendUserData,
|
||||||
getOptimisticFrontendUserDataCollection,
|
getOptimisticFrontendUserDataCollection,
|
||||||
} from "../../data/frontend";
|
} from "../../data/frontend";
|
||||||
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -142,13 +143,30 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected updatePageEl(el) {
|
protected updatePageEl(el) {
|
||||||
|
const showAdvanced = !!(
|
||||||
|
this._coreUserData && this._coreUserData.showAdvanced
|
||||||
|
);
|
||||||
|
const isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
|
||||||
|
|
||||||
|
if ("setProperties" in el) {
|
||||||
|
// As long as we have Polymer panels
|
||||||
|
(el as PolymerElement).setProperties({
|
||||||
|
route: this.routeTail,
|
||||||
|
hass: this.hass,
|
||||||
|
showAdvanced,
|
||||||
|
isWide,
|
||||||
|
narrow: this.narrow,
|
||||||
|
cloudStatus: this._cloudStatus,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
el.route = this.routeTail;
|
el.route = this.routeTail;
|
||||||
el.hass = this.hass;
|
el.hass = this.hass;
|
||||||
el.showAdvanced = !!(this._coreUserData && this._coreUserData.showAdvanced);
|
el.showAdvanced = showAdvanced;
|
||||||
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
|
el.isWide = isWide;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
el.cloudStatus = this._cloudStatus;
|
el.cloudStatus = this._cloudStatus;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _updateCloudStatus() {
|
private async _updateCloudStatus() {
|
||||||
this._cloudStatus = await fetchCloudStatus(this.hass);
|
this._cloudStatus = await fetchCloudStatus(this.hass);
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
import {
|
|
||||||
LitElement,
|
|
||||||
html,
|
|
||||||
PropertyDeclarations,
|
|
||||||
CSSResult,
|
|
||||||
css,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit-element";
|
|
||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import "../../components/ha-menu-button";
|
|
||||||
|
|
||||||
import { HomeAssistant } from "../../types";
|
|
||||||
import { haStyle } from "../../resources/styles";
|
|
||||||
|
|
||||||
import "./system-log-card";
|
|
||||||
import "./error-log-card";
|
|
||||||
import "./system-health-card";
|
|
||||||
|
|
||||||
const JS_VERSION = __BUILD__;
|
|
||||||
const OPT_IN_PANEL = "states";
|
|
||||||
|
|
||||||
class HaPanelDevInfo extends LitElement {
|
|
||||||
public hass?: HomeAssistant;
|
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
|
||||||
return {
|
|
||||||
hass: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
|
||||||
const hass = this.hass;
|
|
||||||
if (!hass) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
const customUiList: Array<{ name: string; url: string; version: string }> =
|
|
||||||
(window as any).CUSTOM_UI_LIST || [];
|
|
||||||
|
|
||||||
const nonDefaultLink =
|
|
||||||
localStorage.defaultPage === OPT_IN_PANEL && OPT_IN_PANEL === "states"
|
|
||||||
? "/lovelace"
|
|
||||||
: "/states";
|
|
||||||
|
|
||||||
const nonDefaultLinkText =
|
|
||||||
localStorage.defaultPage === OPT_IN_PANEL && OPT_IN_PANEL === "states"
|
|
||||||
? "Go to the Lovelace UI"
|
|
||||||
: "Go to the states UI";
|
|
||||||
|
|
||||||
const defaultPageText = `${
|
|
||||||
localStorage.defaultPage === OPT_IN_PANEL ? "Remove" : "Set"
|
|
||||||
} ${OPT_IN_PANEL} as default page on this device`;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<app-header-layout has-scrolling-region>
|
|
||||||
<app-header slot="header" fixed>
|
|
||||||
<app-toolbar>
|
|
||||||
<ha-menu-button></ha-menu-button>
|
|
||||||
<div main-title>About</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class="about">
|
|
||||||
<p class="version">
|
|
||||||
<a href="https://www.home-assistant.io" target="_blank">
|
|
||||||
<img
|
|
||||||
src="/static/icons/favicon-192x192.png"
|
|
||||||
height="192"
|
|
||||||
alt="Home Assistant logo"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
Home Assistant<br />
|
|
||||||
${hass.config.version}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Path to configuration.yaml: ${hass.config.config_dir}
|
|
||||||
</p>
|
|
||||||
<p class="develop">
|
|
||||||
<a
|
|
||||||
href="https://www.home-assistant.io/developers/credits/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Developed by a bunch of awesome people.
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Published under the Apache 2.0 license<br />
|
|
||||||
Source:
|
|
||||||
<a
|
|
||||||
href="https://github.com/home-assistant/home-assistant"
|
|
||||||
target="_blank"
|
|
||||||
>server</a
|
|
||||||
>
|
|
||||||
—
|
|
||||||
<a
|
|
||||||
href="https://github.com/home-assistant/home-assistant-polymer"
|
|
||||||
target="_blank"
|
|
||||||
>frontend-ui</a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Built using
|
|
||||||
<a href="https://www.python.org">Python 3</a>,
|
|
||||||
<a href="https://www.polymer-project.org" target="_blank"
|
|
||||||
>Polymer</a
|
|
||||||
>, Icons by
|
|
||||||
<a href="https://www.google.com/design/icons/" target="_blank"
|
|
||||||
>Google</a
|
|
||||||
>
|
|
||||||
and
|
|
||||||
<a href="https://MaterialDesignIcons.com" target="_blank"
|
|
||||||
>MaterialDesignIcons.com</a
|
|
||||||
>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Frontend JavaScript version: ${JS_VERSION}
|
|
||||||
${customUiList.length > 0
|
|
||||||
? html`
|
|
||||||
<div>
|
|
||||||
Custom UIs:
|
|
||||||
${customUiList.map(
|
|
||||||
(item) => html`
|
|
||||||
<div>
|
|
||||||
<a href="${item.url}" target="_blank">
|
|
||||||
${item.name}</a
|
|
||||||
>: ${item.version}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="${nonDefaultLink}">${nonDefaultLinkText}</a><br />
|
|
||||||
<mwc-button @click="${this._toggleDefaultPage}" raised>
|
|
||||||
${defaultPageText}
|
|
||||||
</mwc-button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<system-health-card .hass=${this.hass}></system-health-card>
|
|
||||||
<system-log-card .hass=${this.hass}></system-log-card>
|
|
||||||
<error-log-card .hass=${this.hass}></error-log-card>
|
|
||||||
</div>
|
|
||||||
</app-header-layout>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProps): void {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
|
|
||||||
// Legacy custom UI can be slow to register, give them time.
|
|
||||||
const customUI = ((window as any).CUSTOM_UI_LIST || []).length;
|
|
||||||
setTimeout(() => {
|
|
||||||
if (((window as any).CUSTOM_UI_LIST || []).length !== customUI.length) {
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _toggleDefaultPage(): void {
|
|
||||||
if (localStorage.defaultPage === OPT_IN_PANEL) {
|
|
||||||
delete localStorage.defaultPage;
|
|
||||||
} else {
|
|
||||||
localStorage.defaultPage = OPT_IN_PANEL;
|
|
||||||
}
|
|
||||||
this.requestUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
-ms-user-select: initial;
|
|
||||||
-webkit-user-select: initial;
|
|
||||||
-moz-user-select: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 16px 0px 16px 0;
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about {
|
|
||||||
text-align: center;
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version {
|
|
||||||
@apply --paper-font-headline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.develop {
|
|
||||||
@apply --paper-font-subhead;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about a {
|
|
||||||
color: var(--dark-primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
system-health-card {
|
|
||||||
display: block;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-panel-dev-info": HaPanelDevInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-panel-dev-info", HaPanelDevInfo);
|
|
@ -1,89 +0,0 @@
|
|||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import "@polymer/paper-input/paper-textarea";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
import "../../components/ha-card";
|
|
||||||
import "../../components/ha-menu-button";
|
|
||||||
import "../../resources/ha-style";
|
|
||||||
import "../../util/app-localstorage-document";
|
|
||||||
|
|
||||||
class HaPanelDevMqtt extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="ha-style">
|
|
||||||
:host {
|
|
||||||
-ms-user-select: initial;
|
|
||||||
-webkit-user-select: initial;
|
|
||||||
-moz-user-select: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 24px 0 32px;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mwc-button {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
|
||||||
<app-header slot="header" fixed>
|
|
||||||
<app-toolbar>
|
|
||||||
<ha-menu-button></ha-menu-button>
|
|
||||||
<div main-title>MQTT</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<app-localstorage-document key="panel-dev-mqtt-topic" data="{{topic}}">
|
|
||||||
</app-localstorage-document>
|
|
||||||
<app-localstorage-document
|
|
||||||
key="panel-dev-mqtt-payload"
|
|
||||||
data="{{payload}}"
|
|
||||||
>
|
|
||||||
</app-localstorage-document>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<ha-card header="Publish a packet">
|
|
||||||
<div class="card-content">
|
|
||||||
<paper-input label="topic" value="{{topic}}"></paper-input>
|
|
||||||
|
|
||||||
<paper-textarea
|
|
||||||
always-float-label
|
|
||||||
label="Payload (template allowed)"
|
|
||||||
value="{{payload}}"
|
|
||||||
></paper-textarea>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<mwc-button on-click="_publish">Publish</mwc-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
</app-header-layout>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
topic: String,
|
|
||||||
payload: String,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_publish() {
|
|
||||||
this.hass.callService("mqtt", "publish", {
|
|
||||||
topic: this.topic,
|
|
||||||
payload_template: this.payload,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-panel-dev-mqtt", HaPanelDevMqtt);
|
|
68
src/panels/developer-tools/developer-tools-router.ts
Normal file
68
src/panels/developer-tools/developer-tools-router.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||||
|
import { customElement, property } from "lit-element";
|
||||||
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
@customElement("developer-tools-router")
|
||||||
|
class DeveloperToolsRouter extends HassRouterPage {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
|
protected routerOptions: RouterOptions = {
|
||||||
|
// defaultPage: "info",
|
||||||
|
beforeRender: (page) => {
|
||||||
|
if (!page || page === "not_found") {
|
||||||
|
// If we can, we are going to restore the last visited page.
|
||||||
|
return this._currentPage ? this._currentPage : "info";
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
cacheAll: true,
|
||||||
|
showLoading: true,
|
||||||
|
routes: {
|
||||||
|
event: {
|
||||||
|
tag: "developer-tools-event",
|
||||||
|
load: () => import("./event/developer-tools-event"),
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
tag: "developer-tools-info",
|
||||||
|
load: () => import("./info/developer-tools-info"),
|
||||||
|
},
|
||||||
|
mqtt: {
|
||||||
|
tag: "developer-tools-mqtt",
|
||||||
|
load: () => import("./mqtt/developer-tools-mqtt"),
|
||||||
|
},
|
||||||
|
service: {
|
||||||
|
tag: "developer-tools-service",
|
||||||
|
load: () => import("./service/developer-tools-service"),
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
tag: "developer-tools-state",
|
||||||
|
load: () => import("./state/developer-tools-state"),
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
tag: "developer-tools-template",
|
||||||
|
load: () => import("./template/developer-tools-template"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
protected updatePageEl(el) {
|
||||||
|
if ("setProperties" in el) {
|
||||||
|
// As long as we have Polymer pages
|
||||||
|
(el as PolymerElement).setProperties({
|
||||||
|
hass: this.hass,
|
||||||
|
narrow: this.narrow,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
el.hass = this.hass;
|
||||||
|
el.narrow = this.narrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"developer-tools-router": DeveloperToolsRouter;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,3 @@
|
|||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
@ -8,11 +5,10 @@ import "@polymer/paper-input/paper-textarea";
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../components/ha-menu-button";
|
import "../../../resources/ha-style";
|
||||||
import "../../resources/ha-style";
|
|
||||||
import "./events-list";
|
import "./events-list";
|
||||||
import "./event-subscribe-card";
|
import "./event-subscribe-card";
|
||||||
import { EventsMixin } from "../../mixins/events-mixin";
|
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
@ -26,12 +22,10 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
|||||||
-ms-user-select: initial;
|
-ms-user-select: initial;
|
||||||
-webkit-user-select: initial;
|
-webkit-user-select: initial;
|
||||||
-moz-user-select: initial;
|
-moz-user-select: initial;
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
@apply --paper-font-body1;
|
@apply --paper-font-body1;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ha-form {
|
.ha-form {
|
||||||
@ -49,15 +43,6 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
|
||||||
<app-header slot="header" fixed>
|
|
||||||
<app-toolbar>
|
|
||||||
<ha-menu-button></ha-menu-button>
|
|
||||||
<div main-title>Events</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class$="[[computeFormClasses(narrow)]]">
|
<div class$="[[computeFormClasses(narrow)]]">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<p>Fire an event on the event bus.</p>
|
<p>Fire an event on the event bus.</p>
|
||||||
@ -86,8 +71,6 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<event-subscribe-card hass="[[hass]]"></event-subscribe-card>
|
<event-subscribe-card hass="[[hass]]"></event-subscribe-card>
|
||||||
</div>
|
|
||||||
</app-header-layout>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,4 +122,4 @@ class HaPanelDevEvent extends EventsMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-panel-dev-event", HaPanelDevEvent);
|
customElements.define("developer-tools-event", HaPanelDevEvent);
|
@ -10,10 +10,10 @@ import {
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { HassEvent } from "home-assistant-js-websocket";
|
import { HassEvent } from "home-assistant-js-websocket";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
import "../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import format_time from "../../common/datetime/format_time";
|
import format_time from "../../../common/datetime/format_time";
|
||||||
|
|
||||||
@customElement("event-subscribe-card")
|
@customElement("event-subscribe-card")
|
||||||
class EventSubscribeCard extends LitElement {
|
class EventSubscribeCard extends LitElement {
|
@ -1,7 +1,7 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import { EventsMixin } from "../../mixins/events-mixin";
|
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
124
src/panels/developer-tools/ha-panel-developer-tools.ts
Normal file
124
src/panels/developer-tools/ha-panel-developer-tools.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
CSSResultArray,
|
||||||
|
css,
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||||
|
import "@polymer/app-layout/app-header/app-header";
|
||||||
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
|
import "@polymer/paper-tabs/paper-tab";
|
||||||
|
import "@polymer/paper-tabs/paper-tabs";
|
||||||
|
|
||||||
|
import "../../components/ha-menu-button";
|
||||||
|
import "../../resources/ha-style";
|
||||||
|
import "./developer-tools-router";
|
||||||
|
|
||||||
|
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||||
|
|
||||||
|
import { haStyle } from "../../resources/styles";
|
||||||
|
import { HomeAssistant, Route } from "../../types";
|
||||||
|
import { navigate } from "../../common/navigate";
|
||||||
|
import isComponentLoaded from "../../common/config/is_component_loaded";
|
||||||
|
|
||||||
|
@customElement("ha-panel-developer-tools")
|
||||||
|
class PanelDeveloperTools extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public route!: Route;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
const page = this._page;
|
||||||
|
return html`
|
||||||
|
<app-header-layout has-scrolling-region>
|
||||||
|
<app-header fixed slot="header">
|
||||||
|
<app-toolbar>
|
||||||
|
<ha-menu-button
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></ha-menu-button>
|
||||||
|
<div main-title>Developer Tools</div>
|
||||||
|
</app-toolbar>
|
||||||
|
<paper-tabs
|
||||||
|
scrollable
|
||||||
|
attr-for-selected="page-name"
|
||||||
|
.selected=${page}
|
||||||
|
@iron-activate=${this.handlePageSelected}
|
||||||
|
>
|
||||||
|
<paper-tab page-name="info">
|
||||||
|
${this.hass.localize("panel.dev-info")}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="event">
|
||||||
|
${this.hass.localize("panel.dev-events")}
|
||||||
|
</paper-tab>
|
||||||
|
${isComponentLoaded(this.hass, "mqtt")
|
||||||
|
? html`
|
||||||
|
<paper-tab page-name="mqtt">
|
||||||
|
${this.hass.localize("panel.dev-mqtt")}
|
||||||
|
</paper-tab>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<paper-tab page-name="service">
|
||||||
|
${this.hass.localize("panel.dev-services")}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="state">
|
||||||
|
${this.hass.localize("panel.dev-states")}
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tab page-name="template">
|
||||||
|
${this.hass.localize("panel.dev-templates")}
|
||||||
|
</paper-tab>
|
||||||
|
</paper-tabs>
|
||||||
|
</app-header>
|
||||||
|
<developer-tools-router
|
||||||
|
.route=${this.route}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></developer-tools-router>
|
||||||
|
</app-header-layout>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handlePageSelected(ev) {
|
||||||
|
const newPage = ev.detail.item.getAttribute("page-name");
|
||||||
|
if (newPage !== this._page) {
|
||||||
|
navigate(this, `/developer-tools/${newPage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToTarget(
|
||||||
|
this,
|
||||||
|
// @ts-ignore
|
||||||
|
this.shadowRoot!.querySelector("app-header-layout").header.scrollTarget
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _page() {
|
||||||
|
return this.route.path.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultArray {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
--paper-card-header-color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
paper-tabs {
|
||||||
|
margin-left: 12px;
|
||||||
|
--paper-tabs-selection-bar-color: #fff;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-panel-developer-tools": PanelDeveloperTools;
|
||||||
|
}
|
||||||
|
}
|
194
src/panels/developer-tools/info/developer-tools-info.ts
Normal file
194
src/panels/developer-tools/info/developer-tools-info.ts
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
|
||||||
|
import "./system-log-card";
|
||||||
|
import "./error-log-card";
|
||||||
|
import "./system-health-card";
|
||||||
|
|
||||||
|
const JS_VERSION = __BUILD__;
|
||||||
|
const OPT_IN_PANEL = "states";
|
||||||
|
|
||||||
|
class HaPanelDevInfo extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
const hass = this.hass;
|
||||||
|
const customUiList: Array<{ name: string; url: string; version: string }> =
|
||||||
|
(window as any).CUSTOM_UI_LIST || [];
|
||||||
|
|
||||||
|
const nonDefaultLink =
|
||||||
|
localStorage.defaultPage === OPT_IN_PANEL && OPT_IN_PANEL === "states"
|
||||||
|
? "/lovelace"
|
||||||
|
: "/states";
|
||||||
|
|
||||||
|
const nonDefaultLinkText =
|
||||||
|
localStorage.defaultPage === OPT_IN_PANEL && OPT_IN_PANEL === "states"
|
||||||
|
? "Go to the Lovelace UI"
|
||||||
|
: "Go to the states UI";
|
||||||
|
|
||||||
|
const defaultPageText = `${
|
||||||
|
localStorage.defaultPage === OPT_IN_PANEL ? "Remove" : "Set"
|
||||||
|
} ${OPT_IN_PANEL} as default page on this device`;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="about">
|
||||||
|
<p class="version">
|
||||||
|
<a href="https://www.home-assistant.io" target="_blank"
|
||||||
|
><img
|
||||||
|
src="/static/icons/favicon-192x192.png"
|
||||||
|
height="192"
|
||||||
|
alt="Home Assistant logo"
|
||||||
|
/></a>
|
||||||
|
<br />
|
||||||
|
Home Assistant<br />
|
||||||
|
${hass.config.version}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Path to configuration.yaml: ${hass.config.config_dir}
|
||||||
|
</p>
|
||||||
|
<p class="develop">
|
||||||
|
<a
|
||||||
|
href="https://www.home-assistant.io/developers/credits/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Developed by a bunch of awesome people.
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Published under the Apache 2.0 license<br />
|
||||||
|
Source:
|
||||||
|
<a
|
||||||
|
href="https://github.com/home-assistant/home-assistant"
|
||||||
|
target="_blank"
|
||||||
|
>server</a
|
||||||
|
>
|
||||||
|
—
|
||||||
|
<a
|
||||||
|
href="https://github.com/home-assistant/home-assistant-polymer"
|
||||||
|
target="_blank"
|
||||||
|
>frontend-ui</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Built using
|
||||||
|
<a href="https://www.python.org">Python 3</a>,
|
||||||
|
<a href="https://www.polymer-project.org" target="_blank">Polymer</a>,
|
||||||
|
Icons by
|
||||||
|
<a href="https://www.google.com/design/icons/" target="_blank"
|
||||||
|
>Google</a
|
||||||
|
>
|
||||||
|
and
|
||||||
|
<a href="https://MaterialDesignIcons.com" target="_blank"
|
||||||
|
>MaterialDesignIcons.com</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Frontend JavaScript version: ${JS_VERSION}
|
||||||
|
${customUiList.length > 0
|
||||||
|
? html`
|
||||||
|
<div>
|
||||||
|
Custom UIs:
|
||||||
|
${customUiList.map(
|
||||||
|
(item) => html`
|
||||||
|
<div>
|
||||||
|
<a href="${item.url}" target="_blank"> ${item.name}</a>:
|
||||||
|
${item.version}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="${nonDefaultLink}">${nonDefaultLinkText}</a><br />
|
||||||
|
<mwc-button @click="${this._toggleDefaultPage}" raised>
|
||||||
|
${defaultPageText}
|
||||||
|
</mwc-button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<system-health-card .hass=${this.hass}></system-health-card>
|
||||||
|
<system-log-card .hass=${this.hass}></system-log-card>
|
||||||
|
<error-log-card .hass=${this.hass}></error-log-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps): void {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
|
// Legacy custom UI can be slow to register, give them time.
|
||||||
|
const customUI = ((window as any).CUSTOM_UI_LIST || []).length;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (((window as any).CUSTOM_UI_LIST || []).length !== customUI.length) {
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _toggleDefaultPage(): void {
|
||||||
|
if (localStorage.defaultPage === OPT_IN_PANEL) {
|
||||||
|
delete localStorage.defaultPage;
|
||||||
|
} else {
|
||||||
|
localStorage.defaultPage = OPT_IN_PANEL;
|
||||||
|
}
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
-ms-user-select: initial;
|
||||||
|
-webkit-user-select: initial;
|
||||||
|
-moz-user-select: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 16px 0px 16px 0;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version {
|
||||||
|
@apply --paper-font-headline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.develop {
|
||||||
|
@apply --paper-font-subhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about a {
|
||||||
|
color: var(--dark-primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
system-health-card {
|
||||||
|
display: block;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"developer-tools-info": HaPanelDevInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("developer-tools-info", HaPanelDevInfo);
|
@ -8,11 +8,11 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
|
|
||||||
import "../../components/dialog/ha-paper-dialog";
|
import "../../../components/dialog/ha-paper-dialog";
|
||||||
|
|
||||||
import { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
|
import { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../../resources/styles";
|
||||||
|
|
||||||
class DialogSystemLogDetail extends LitElement {
|
class DialogSystemLogDetail extends LitElement {
|
||||||
private _params?: SystemLogDetailDialogParams;
|
private _params?: SystemLogDetailDialogParams;
|
@ -9,8 +9,8 @@ import {
|
|||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { fetchErrorLog } from "../../data/error_log";
|
import { fetchErrorLog } from "../../../data/error_log";
|
||||||
|
|
||||||
class ErrorLogCard extends LitElement {
|
class ErrorLogCard extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
@ -1,5 +1,5 @@
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { LoggedError } from "../../data/system_log";
|
import { LoggedError } from "../../../data/system_log";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
@ -7,13 +7,13 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import "../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import {
|
import {
|
||||||
SystemHealthInfo,
|
SystemHealthInfo,
|
||||||
fetchSystemHealthInfo,
|
fetchSystemHealthInfo,
|
||||||
} from "../../data/system_health";
|
} from "../../../data/system_health";
|
||||||
|
|
||||||
const sortKeys = (a: string, b: string) => {
|
const sortKeys = (a: string, b: string) => {
|
||||||
if (a === "homeassistant") {
|
if (a === "homeassistant") {
|
@ -10,13 +10,13 @@ import "@polymer/paper-icon-button/paper-icon-button";
|
|||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import "../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../components/buttons/ha-call-service-button";
|
import "../../../components/buttons/ha-call-service-button";
|
||||||
import "../../components/buttons/ha-progress-button";
|
import "../../../components/buttons/ha-progress-button";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LoggedError, fetchSystemLog } from "../../data/system_log";
|
import { LoggedError, fetchSystemLog } from "../../../data/system_log";
|
||||||
import formatDateTime from "../../common/datetime/format_date_time";
|
import formatDateTime from "../../../common/datetime/format_date_time";
|
||||||
import formatTime from "../../common/datetime/format_time";
|
import formatTime from "../../../common/datetime/format_time";
|
||||||
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
|
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
|
||||||
|
|
||||||
const formatLogTime = (date, language: string) => {
|
const formatLogTime = (date, language: string) => {
|
74
src/panels/developer-tools/mqtt/developer-tools-mqtt.js
Normal file
74
src/panels/developer-tools/mqtt/developer-tools-mqtt.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../resources/ha-style";
|
||||||
|
import "../../../util/app-localstorage-document";
|
||||||
|
|
||||||
|
class HaPanelDevMqtt extends PolymerElement {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style include="ha-style">
|
||||||
|
:host {
|
||||||
|
-ms-user-select: initial;
|
||||||
|
-webkit-user-select: initial;
|
||||||
|
-moz-user-select: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 24px 0 32px;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-button {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<app-localstorage-document key="panel-dev-mqtt-topic" data="{{topic}}">
|
||||||
|
</app-localstorage-document>
|
||||||
|
<app-localstorage-document
|
||||||
|
key="panel-dev-mqtt-payload"
|
||||||
|
data="{{payload}}"
|
||||||
|
>
|
||||||
|
</app-localstorage-document>
|
||||||
|
|
||||||
|
<ha-card header="Publish a packet">
|
||||||
|
<div class="card-content">
|
||||||
|
<paper-input label="topic" value="{{topic}}"></paper-input>
|
||||||
|
|
||||||
|
<paper-textarea
|
||||||
|
always-float-label
|
||||||
|
label="Payload (template allowed)"
|
||||||
|
value="{{payload}}"
|
||||||
|
></paper-textarea>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button on-click="_publish">Publish</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
topic: String,
|
||||||
|
payload: String,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_publish() {
|
||||||
|
this.hass.callService("mqtt", "publish", {
|
||||||
|
topic: this.topic,
|
||||||
|
payload_template: this.payload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("developer-tools-mqtt", HaPanelDevMqtt);
|
@ -1,17 +1,13 @@
|
|||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import { ENTITY_COMPONENT_DOMAINS } from "../../data/entity";
|
import { ENTITY_COMPONENT_DOMAINS } from "../../../data/entity";
|
||||||
import "../../components/entity/ha-entity-picker";
|
import "../../../components/entity/ha-entity-picker";
|
||||||
import "../../components/ha-menu-button";
|
import "../../../components/ha-service-picker";
|
||||||
import "../../components/ha-service-picker";
|
import "../../../resources/ha-style";
|
||||||
import "../../resources/ha-style";
|
import "../../../util/app-localstorage-document";
|
||||||
import "../../util/app-localstorage-document";
|
|
||||||
|
|
||||||
const ERROR_SENTINEL = {};
|
const ERROR_SENTINEL = {};
|
||||||
class HaPanelDevService extends PolymerElement {
|
class HaPanelDevService extends PolymerElement {
|
||||||
@ -22,9 +18,7 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
-ms-user-select: initial;
|
-ms-user-select: initial;
|
||||||
-webkit-user-select: initial;
|
-webkit-user-select: initial;
|
||||||
-moz-user-select: initial;
|
-moz-user-select: initial;
|
||||||
}
|
display: block;
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
@ -81,14 +75,6 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
|
||||||
<app-header slot="header" fixed>
|
|
||||||
<app-toolbar>
|
|
||||||
<ha-menu-button></ha-menu-button>
|
|
||||||
<div main-title>Services</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<app-localstorage-document
|
<app-localstorage-document
|
||||||
key="panel-dev-service-state-domain-service"
|
key="panel-dev-service-state-domain-service"
|
||||||
data="{{domainService}}"
|
data="{{domainService}}"
|
||||||
@ -102,8 +88,8 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>
|
<p>
|
||||||
The service dev tool allows you to call any available service in
|
The service dev tool allows you to call any available service in Home
|
||||||
Home Assistant.
|
Assistant.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="ha-form">
|
<div class="ha-form">
|
||||||
@ -159,11 +145,7 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
<td colspan="3">This service takes no parameters.</td>
|
<td colspan="3">This service takes no parameters.</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template is="dom-repeat" items="[[_attributes]]" as="attribute">
|
||||||
is="dom-repeat"
|
|
||||||
items="[[_attributes]]"
|
|
||||||
as="attribute"
|
|
||||||
>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><pre>[[attribute.key]]</pre></td>
|
<td><pre>[[attribute.key]]</pre></td>
|
||||||
<td>[[attribute.description]]</td>
|
<td>[[attribute.description]]</td>
|
||||||
@ -174,7 +156,6 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</app-header-layout>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,4 +285,4 @@ class HaPanelDevService extends PolymerElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-panel-dev-service", HaPanelDevService);
|
customElements.define("developer-tools-service", HaPanelDevService);
|
@ -1,6 +1,3 @@
|
|||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import "@polymer/paper-checkbox/paper-checkbox";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
@ -8,10 +5,9 @@ import "@polymer/paper-input/paper-textarea";
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../components/entity/ha-entity-picker";
|
import "../../../components/entity/ha-entity-picker";
|
||||||
import "../../components/ha-menu-button";
|
import "../../../resources/ha-style";
|
||||||
import "../../resources/ha-style";
|
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||||
import { EventsMixin } from "../../mixins/events-mixin";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
@ -24,9 +20,7 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
|||||||
-ms-user-select: initial;
|
-ms-user-select: initial;
|
||||||
-webkit-user-select: initial;
|
-webkit-user-select: initial;
|
||||||
-moz-user-select: initial;
|
-moz-user-select: initial;
|
||||||
}
|
display: block;
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
@ -70,15 +64,6 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
|
||||||
<app-header slot="header" fixed>
|
|
||||||
<app-toolbar>
|
|
||||||
<ha-menu-button></ha-menu-button>
|
|
||||||
<div main-title>States</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Set the representation of a device within Home Assistant.<br />
|
Set the representation of a device within Home Assistant.<br />
|
||||||
@ -169,8 +154,6 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
|||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
</app-header-layout>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,4 +334,4 @@ class HaPanelDevState extends EventsMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-panel-dev-state", HaPanelDevState);
|
customElements.define("developer-tools-state", HaPanelDevState);
|
@ -1,6 +1,3 @@
|
|||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
|
||||||
import "@polymer/app-layout/app-header/app-header";
|
|
||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
||||||
@ -8,8 +5,7 @@ import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
import "../../components/ha-menu-button";
|
import "../../../resources/ha-style";
|
||||||
import "../../resources/ha-style";
|
|
||||||
|
|
||||||
class HaPanelDevTemplate extends PolymerElement {
|
class HaPanelDevTemplate extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
@ -67,14 +63,6 @@ class HaPanelDevTemplate extends PolymerElement {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-header-layout has-scrolling-region>
|
|
||||||
<app-header slot="header" fixed>
|
|
||||||
<app-toolbar>
|
|
||||||
<ha-menu-button></ha-menu-button>
|
|
||||||
<div main-title>Templates</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<div class$="[[computeFormClasses(narrow)]]">
|
<div class$="[[computeFormClasses(narrow)]]">
|
||||||
<div class="edit-pane">
|
<div class="edit-pane">
|
||||||
<p>
|
<p>
|
||||||
@ -112,7 +100,6 @@ class HaPanelDevTemplate extends PolymerElement {
|
|||||||
<pre class$="[[computeRenderedClasses(error)]]">[[processed]]</pre>
|
<pre class$="[[computeRenderedClasses(error)]]">[[processed]]</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</app-header-layout>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,4 +194,4 @@ For loop example:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-panel-dev-template", HaPanelDevTemplate);
|
customElements.define("developer-tools-template", HaPanelDevTemplate);
|
@ -65,7 +65,10 @@ 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
|
||||||
|
hass="[[hass]]"
|
||||||
|
narrow="[[narrow]]"
|
||||||
|
></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>
|
||||||
@ -116,9 +119,8 @@ class HaPanelHistory extends LocalizeMixin(PolymerElement) {
|
|||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: Object,
|
||||||
type: Object,
|
narrow: Boolean,
|
||||||
},
|
|
||||||
|
|
||||||
stateHistory: {
|
stateHistory: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -16,7 +16,7 @@ class HaPanelIframe extends PolymerElement {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button></ha-menu-button>
|
<ha-menu-button hass="[[hass]]" narrow="[[narrow]]"></ha-menu-button>
|
||||||
<div main-title>[[panel.title]]</div>
|
<div main-title>[[panel.title]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
@ -32,9 +32,9 @@ class HaPanelIframe extends PolymerElement {
|
|||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
panel: {
|
hass: Object,
|
||||||
type: Object,
|
narrow: Boolean,
|
||||||
},
|
panel: Object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,10 @@ 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
|
||||||
|
hass="[[hass]]"
|
||||||
|
narrow="[[narrow]]"
|
||||||
|
></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"
|
||||||
@ -157,9 +160,8 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: Object,
|
||||||
type: Object,
|
narrow: Boolean,
|
||||||
},
|
|
||||||
|
|
||||||
// ISO8601 formatted date string
|
// ISO8601 formatted date string
|
||||||
_currentDate: {
|
_currentDate: {
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import { HassEntities, HassEntity } from "home-assistant-js-websocket";
|
|
||||||
|
|
||||||
import computeDomain from "../../../common/entity/compute_domain";
|
|
||||||
|
|
||||||
export const computeNotifications = (states: HassEntities): HassEntity[] => {
|
|
||||||
return Object.keys(states)
|
|
||||||
.filter((entityId) => computeDomain(entityId) === "configurator")
|
|
||||||
.map((entityId) => states[entityId]);
|
|
||||||
};
|
|
@ -1,81 +0,0 @@
|
|||||||
import {
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
TemplateResult,
|
|
||||||
css,
|
|
||||||
CSSResult,
|
|
||||||
property,
|
|
||||||
} from "lit-element";
|
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"opened-changed": { value: boolean };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HuiNotificationsButton extends LitElement {
|
|
||||||
@property() public notifications?: string[];
|
|
||||||
@property() public opened?: boolean;
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
|
||||||
return html`
|
|
||||||
<paper-icon-button
|
|
||||||
aria-label="Show Notifications"
|
|
||||||
icon="hass:bell"
|
|
||||||
@click="${this._clicked}"
|
|
||||||
></paper-icon-button>
|
|
||||||
${this.notifications && this.notifications.length > 0
|
|
||||||
? html`
|
|
||||||
<span class="indicator">
|
|
||||||
<div>${this.notifications.length}</div>
|
|
||||||
</span>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
|
||||||
return [
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indicator {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
right: -3px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--accent-color);
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indicator > div {
|
|
||||||
right: 7px;
|
|
||||||
top: 3px;
|
|
||||||
position: absolute;
|
|
||||||
font-size: 0.55em;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _clicked() {
|
|
||||||
this.opened = true;
|
|
||||||
fireEvent(this, "opened-changed", { value: this.opened });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-notifications-button": HuiNotificationsButton;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("hui-notifications-button", HuiNotificationsButton);
|
|
@ -1,8 +0,0 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
|
||||||
|
|
||||||
export declare type HassNotification = HassEntity & {
|
|
||||||
notification_id?: string;
|
|
||||||
created_at?: string;
|
|
||||||
title?: string;
|
|
||||||
message?: string;
|
|
||||||
};
|
|
@ -95,7 +95,11 @@ class LovelacePanel extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-loading-screen rootnav></hass-loading-screen>
|
<hass-loading-screen
|
||||||
|
rootnav
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></hass-loading-screen>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import "@polymer/paper-listbox/paper-listbox";
|
|||||||
import "@polymer/paper-menu-button/paper-menu-button";
|
import "@polymer/paper-menu-button/paper-menu-button";
|
||||||
import "@polymer/paper-tabs/paper-tab";
|
import "@polymer/paper-tabs/paper-tab";
|
||||||
import "@polymer/paper-tabs/paper-tabs";
|
import "@polymer/paper-tabs/paper-tabs";
|
||||||
import { HassEntities } from "home-assistant-js-websocket";
|
|
||||||
|
|
||||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||||
|
|
||||||
@ -30,17 +29,13 @@ import "../../components/ha-paper-icon-button-arrow-next";
|
|||||||
import "../../components/ha-paper-icon-button-arrow-prev";
|
import "../../components/ha-paper-icon-button-arrow-prev";
|
||||||
import "../../components/ha-icon";
|
import "../../components/ha-icon";
|
||||||
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
|
||||||
import { subscribeNotifications } from "../../data/ws-notifications";
|
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { LovelaceConfig } from "../../data/lovelace";
|
import { LovelaceConfig } from "../../data/lovelace";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeNotifications } from "./common/compute-notifications";
|
|
||||||
import { swapView } from "./editor/config-util";
|
import { swapView } from "./editor/config-util";
|
||||||
|
|
||||||
import "./components/notifications/hui-notification-drawer";
|
|
||||||
import "./components/notifications/hui-notifications-button";
|
|
||||||
import "./hui-view";
|
import "./hui-view";
|
||||||
// Not a duplicate import, this one is for type
|
// Not a duplicate import, this one is for type
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
@ -65,12 +60,9 @@ class HUIRoot extends LitElement {
|
|||||||
@property() public route?: { path: string; prefix: string };
|
@property() public route?: { path: string; prefix: string };
|
||||||
@property() private _routeData?: { view: string };
|
@property() private _routeData?: { view: string };
|
||||||
@property() private _curView?: number | "hass-unused-entities";
|
@property() private _curView?: number | "hass-unused-entities";
|
||||||
@property() private _notificationsOpen = false;
|
|
||||||
@property() private _persistentNotifications?: Notification[];
|
|
||||||
private _viewCache?: { [viewId: string]: HUIView };
|
private _viewCache?: { [viewId: string]: HUIView };
|
||||||
|
|
||||||
private _debouncedConfigChanged: () => void;
|
private _debouncedConfigChanged: () => void;
|
||||||
private _unsubNotifications?: () => void;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -83,35 +75,11 @@ class HUIRoot extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback(): void {
|
|
||||||
super.connectedCallback();
|
|
||||||
this._unsubNotifications = subscribeNotifications(
|
|
||||||
this.hass!.connection,
|
|
||||||
(notifications) => {
|
|
||||||
this._persistentNotifications = notifications;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public disconnectedCallback(): void {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
if (this._unsubNotifications) {
|
|
||||||
this._unsubNotifications();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<app-route .route="${this.route}" pattern="/:view" data="${
|
<app-route .route="${this.route}" pattern="/:view" data="${
|
||||||
this._routeData
|
this._routeData
|
||||||
}" @data-changed="${this._routeDataChanged}"></app-route>
|
}" @data-changed="${this._routeDataChanged}"></app-route>
|
||||||
<hui-notification-drawer
|
|
||||||
.hass="${this.hass}"
|
|
||||||
.notifications="${this._notifications}"
|
|
||||||
.open="${this._notificationsOpen}"
|
|
||||||
@open-changed="${this._handleNotificationsOpenChanged}"
|
|
||||||
.narrow="${this.narrow}"
|
|
||||||
></hui-notification-drawer>
|
|
||||||
<ha-app-layout id="layout">
|
<ha-app-layout id="layout">
|
||||||
<app-header slot="header" effects="waterfall" class="${classMap({
|
<app-header slot="header" effects="waterfall" class="${classMap({
|
||||||
"edit-mode": this._editMode,
|
"edit-mode": this._editMode,
|
||||||
@ -163,14 +131,11 @@ class HUIRoot extends LitElement {
|
|||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button></ha-menu-button>
|
<ha-menu-button
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></ha-menu-button>
|
||||||
<div main-title>${this.config.title || "Home Assistant"}</div>
|
<div main-title>${this.config.title || "Home Assistant"}</div>
|
||||||
<hui-notifications-button
|
|
||||||
.hass="${this.hass}"
|
|
||||||
.opened="${this._notificationsOpen}"
|
|
||||||
@opened-changed="${this._handleNotificationsOpenChanged}"
|
|
||||||
.notifications="${this._notifications}"
|
|
||||||
></hui-notifications-button>
|
|
||||||
<ha-start-voice-button
|
<ha-start-voice-button
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
></ha-start-voice-button>
|
></ha-start-voice-button>
|
||||||
@ -449,13 +414,6 @@ class HUIRoot extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _notifications() {
|
|
||||||
return this._updateNotifications(
|
|
||||||
this.hass!.states,
|
|
||||||
this._persistentNotifications! || []
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private get config(): LovelaceConfig {
|
private get config(): LovelaceConfig {
|
||||||
return this.lovelace!.config;
|
return this.lovelace!.config;
|
||||||
}
|
}
|
||||||
@ -480,18 +438,6 @@ class HUIRoot extends LitElement {
|
|||||||
this._routeData = ev.detail.value;
|
this._routeData = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleNotificationsOpenChanged(ev): void {
|
|
||||||
this._notificationsOpen = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateNotifications(
|
|
||||||
states: HassEntities,
|
|
||||||
persistent: unknown[]
|
|
||||||
): unknown[] {
|
|
||||||
const configurator = computeNotifications(states);
|
|
||||||
return persistent.concat(configurator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleRefresh(): void {
|
private _handleRefresh(): void {
|
||||||
fireEvent(this, "config-refresh");
|
fireEvent(this, "config-refresh");
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,10 @@ 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
|
||||||
|
hass="[[hass]]"
|
||||||
|
narrow="[[narrow]]"
|
||||||
|
></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)]]">
|
||||||
@ -128,9 +131,8 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: {
|
hass: Object,
|
||||||
type: Object,
|
narrow: Boolean,
|
||||||
},
|
|
||||||
|
|
||||||
platforms: {
|
platforms: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -27,7 +27,7 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button></ha-menu-button>
|
<ha-menu-button hass="[[hass]]" narrow="[[narrow]]"></ha-menu-button>
|
||||||
<div main-title>[[localize('panel.map')]]</div>
|
<div main-title>[[localize('panel.map')]]</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ class HaPanelMap extends LocalizeMixin(PolymerElement) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
observer: "drawEntities",
|
observer: "drawEntities",
|
||||||
},
|
},
|
||||||
|
narrow: Boolean,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,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 hass='[[hass]]' narrow='[[narrow]]'></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>
|
||||||
|
@ -67,7 +67,10 @@ 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
|
||||||
|
hass="[[hass]]"
|
||||||
|
narrow="[[narrow]]"
|
||||||
|
></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]]"
|
||||||
@ -139,6 +142,7 @@ class HaPanelShoppingList extends LocalizeMixin(PolymerElement) {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
narrow: Boolean,
|
||||||
canListen: Boolean,
|
canListen: Boolean,
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
@ -65,7 +65,10 @@ 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
|
||||||
|
hass="[[hass]]"
|
||||||
|
narrow="[[narrow]]"
|
||||||
|
></ha-menu-button>
|
||||||
<div main-title="">
|
<div main-title="">
|
||||||
[[computeTitle(views, defaultView, locationName)]]
|
[[computeTitle(views, defaultView, locationName)]]
|
||||||
</div>
|
</div>
|
||||||
|
@ -997,6 +997,8 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"lights": "אורות",
|
"lights": "אורות",
|
||||||
"information": "מידע",
|
"information": "מידע",
|
||||||
|
"morning_commute": "נסיעת בוקר",
|
||||||
|
"commute_home": "זמן נסיעה הביתה",
|
||||||
"entertainment": "בידור",
|
"entertainment": "בידור",
|
||||||
"activity": "פעילות",
|
"activity": "פעילות",
|
||||||
"hdmi_input": "כניסת HDMI",
|
"hdmi_input": "כניסת HDMI",
|
||||||
@ -1007,6 +1009,7 @@
|
|||||||
"air": "אוויר"
|
"air": "אוויר"
|
||||||
},
|
},
|
||||||
"unit": {
|
"unit": {
|
||||||
|
"watching": "צופה",
|
||||||
"minutes_abbr": "דקות"
|
"minutes_abbr": "דקות"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,24 +327,24 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"header": "Home Assistant 설정",
|
"header": "Home Assistant 설정",
|
||||||
"introduction": "여기에서 구성요소와 Home Assistant 를 설정 할 수 있습니다. 아직 여기서 모두 설정 할 수는 없지만, 곧 구현되도록 작업 중입니다",
|
"introduction": "여기에서 구성요소와 Home Assistant 를 설정 할 수 있습니다. 아직 여기서 모두 설정 할 수는 없지만, 모든 내용을 설정 할 수 있도록 작업 중입니다.",
|
||||||
"core": {
|
"core": {
|
||||||
"caption": "일반",
|
"caption": "일반",
|
||||||
"description": "설정 파일의 유효성을 검사하고 서버를 제어합니다",
|
"description": "구성 내용 파일의 유효성을 검사하고 서버를 제어합니다",
|
||||||
"section": {
|
"section": {
|
||||||
"core": {
|
"core": {
|
||||||
"header": "설정 및 서버 제어",
|
"header": "구성 내용 설정 및 서버 제어",
|
||||||
"introduction": "설정을 변경하는 것은 귀찮은 작업입니다. 이 곳은 설정 변경을 좀 더 쉽게 도와줍니다",
|
"introduction": "구성 내용의 설정을 변경하는 것은 때때로 난해하고 귀찮은 작업입니다. 여기서 설정 변경을 좀 더 쉽게 하실 수 있습니다.",
|
||||||
"validation": {
|
"validation": {
|
||||||
"heading": "설정 유효성 검사",
|
"heading": "구성 내용 유효성 검사",
|
||||||
"introduction": "최근에 설정 내용을 일부 변경하고 모두 정상 작동이 되는지 확인하려는 경우 설정 내용의 유효성을 검사합니다",
|
"introduction": "최근에 구성 내용을 추가 혹은 변경하셨다면, 설정 확인 버튼을 눌러 구성 내용이 올바른지 검사하고 Home Assistant 가 정상 작동 되는지 확인하실 수 있습니다.",
|
||||||
"check_config": "설정 확인",
|
"check_config": "설정 확인",
|
||||||
"valid": "설정 내용이 유효합니다!",
|
"valid": "구성 내용이 모두 올바릅니다!",
|
||||||
"invalid": "설정 내용이 잘못되었습니다"
|
"invalid": "구성 내용이 잘못되었습니다"
|
||||||
},
|
},
|
||||||
"reloading": {
|
"reloading": {
|
||||||
"heading": "설정 새로고침",
|
"heading": "구성 내용 새로고침",
|
||||||
"introduction": "Home Assistant 의 일부 설정은 재시작 없이 다시 읽어들일 수 있습니다. 새로고침을 누르면 현재 구성을 내리고 새로운 설정 내용을 읽어들입니다",
|
"introduction": "Home Assistant 의 일부 구성 내용은 재시작 없이 다시 읽어들일 수 있습니다. 새로고침을 누르면 현재 구성 내용을 내리고 새로운 구성 내용을 읽어들입니다",
|
||||||
"core": "코어 새로고침",
|
"core": "코어 새로고침",
|
||||||
"group": "그룹 새로고침",
|
"group": "그룹 새로고침",
|
||||||
"automation": "자동화 새로고침",
|
"automation": "자동화 새로고침",
|
||||||
@ -379,7 +379,7 @@
|
|||||||
"description": "구성요소를 사용자화 합니다",
|
"description": "구성요소를 사용자화 합니다",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "사용자화",
|
"header": "사용자화",
|
||||||
"introduction": "구성요소의 속성을 조정할 수 있습니다. 추가 및 수정 된 사용자화 정의는 즉시 적용되며, 제거 된 사용자화 정의는 구성요소가 업데이트 될 때 적용됩니다."
|
"introduction": "구성요소의 속성값을 입맛에 맞게 변경할 수 있습니다. 추가 혹은 수정 된 사용자 정의 내용은 즉시 적용되지만, 제거 된 내용은 구성요소가 업데이트 될 때 적용됩니다."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"automation": {
|
"automation": {
|
||||||
@ -634,7 +634,7 @@
|
|||||||
"description": "Zigbee 홈 자동화 네트워크 관리",
|
"description": "Zigbee 홈 자동화 네트워크 관리",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "ZHA 기기를 다시 구성 합니다. (장치 복구). 기기에 문제가 있는 경우 사용해주세요. 기기가 배터리로 작동하는 경우, 이 서비스를 사용할 때 기기가 켜져있고 통신이 가능한 상태인지 확인해주세요.",
|
"reconfigure": "ZHA 기기를 다시 구성 합니다. (장치 복구). 기기에 문제가 있는 경우 사용해주세요. 기기가 배터리로 작동하는 경우, 이 서비스를 사용할 때 기기가 켜져있고 통신이 가능한 상태인지 확인해주세요.",
|
||||||
"updateDeviceName": "이 기기의 사용자 정의 이름을 장치 레지스트리에 설정합니다.",
|
"updateDeviceName": "이 기기의 사용자 정의 이름을 기기 레지스트리에 설정합니다.",
|
||||||
"remove": "Zigbee 네트워크에서 기기 제거"
|
"remove": "Zigbee 네트워크에서 기기 제거"
|
||||||
},
|
},
|
||||||
"device_card": {
|
"device_card": {
|
||||||
@ -653,7 +653,7 @@
|
|||||||
"description": "영역을 만들고 편집합니다",
|
"description": "영역을 만들고 편집합니다",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "영역 등록",
|
"header": "영역 등록",
|
||||||
"introduction": "영역은 기기가있는 위치를 구성하는데 사용합니다. 이 정보는 Home Assistant 의 인터페이스 정리, 권한 및 다른 시스템과의 통합 구성에 도움을 줍니다.",
|
"introduction": "영역은 기기가 있는 위치를 설정하는데 사용합니다. 이 정보는 Home Assistant 의 인터페이스 정리, 권한 및 다른 시스템과의 통합 구성에 도움을 줍니다.",
|
||||||
"introduction2": "특정 영역에 기기를 배치하려면 아래 링크를 따라 통합 구성요소 페이지로 이동 한 다음, 설정된 구성요소의 기기를 클릭하여 영역을 설정해주세요.",
|
"introduction2": "특정 영역에 기기를 배치하려면 아래 링크를 따라 통합 구성요소 페이지로 이동 한 다음, 설정된 구성요소의 기기를 클릭하여 영역을 설정해주세요.",
|
||||||
"integrations_page": "통합 구성요소 페이지",
|
"integrations_page": "통합 구성요소 페이지",
|
||||||
"no_areas": "등록된 영역이 없습니다. 거실, 침실과 같이 영역을 등록해보세요!",
|
"no_areas": "등록된 영역이 없습니다. 거실, 침실과 같이 영역을 등록해보세요!",
|
||||||
@ -675,7 +675,7 @@
|
|||||||
"header": "구성요소",
|
"header": "구성요소",
|
||||||
"unavailable": "(사용불가)",
|
"unavailable": "(사용불가)",
|
||||||
"introduction": "Home Assistant 는 구성요소의 식별을 위해 모든 구성요소에 고유한 레지스트리를 부여합니다. 각각의 구성요소들은 자신만의 고유한 구성요소 ID 를 가집니다.",
|
"introduction": "Home Assistant 는 구성요소의 식별을 위해 모든 구성요소에 고유한 레지스트리를 부여합니다. 각각의 구성요소들은 자신만의 고유한 구성요소 ID 를 가집니다.",
|
||||||
"introduction2": "구성요소를 편집하여 이름을 대체하거나 구성요소 ID를 변경하거나 Home Assistant 에서 항목을 제거할 수 있습니다. 단, 구성요소 편집창에서 구성요소를 삭제해도 구성요소가 완전히 제거되는것은 아닙니다. 완전히 제거하려면, 아래 링크를 따라 통합 구성요소 페이지에서 제거해주세요.",
|
"introduction2": "구성요소를 편집하여 이름을 대체하거나 구성요소 ID를 변경하고 Home Assistant 에서 항목을 제거하실수도 있습니다. 단, 구성요소 편집창에서 구성요소를 삭제해도 구성요소가 완전히 제거되는것은 아닙니다. 완전히 제거하려면, 아래 링크를 따라 통합 구성요소 페이지에서 제거해주세요.",
|
||||||
"integrations_page": "통합 구성요소 페이지"
|
"integrations_page": "통합 구성요소 페이지"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
@ -927,7 +927,7 @@
|
|||||||
"move": "이동"
|
"move": "이동"
|
||||||
},
|
},
|
||||||
"migrate": {
|
"migrate": {
|
||||||
"header": "설정이 호환되지 않습니다",
|
"header": "구성 내용이 호환되지 않습니다",
|
||||||
"para_no_id": "이 구성요소에는 ID가 없습니다. 'ui-lovelace.yaml' 에 구성요소의 ID를 추가해주세요.",
|
"para_no_id": "이 구성요소에는 ID가 없습니다. 'ui-lovelace.yaml' 에 구성요소의 ID를 추가해주세요.",
|
||||||
"para_migrate": "Home Assistant 는 '설정 마이그레이션' 버튼을 눌러 자동으로 모든 카드와 보기에 ID를 추가 할 수 있습니다.",
|
"para_migrate": "Home Assistant 는 '설정 마이그레이션' 버튼을 눌러 자동으로 모든 카드와 보기에 ID를 추가 할 수 있습니다.",
|
||||||
"migrate": "설정 마이그레이션"
|
"migrate": "설정 마이그레이션"
|
||||||
|
@ -258,13 +258,13 @@
|
|||||||
},
|
},
|
||||||
"vacuum": {
|
"vacuum": {
|
||||||
"cleaning": "A limpar",
|
"cleaning": "A limpar",
|
||||||
"docked": "Ancorado",
|
"docked": "Encaixado",
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"idle": "Em espera",
|
"idle": "Em espera",
|
||||||
"off": "Desligado",
|
"off": "Desligado",
|
||||||
"on": "Ligado",
|
"on": "Ligado",
|
||||||
"paused": "Em pausa",
|
"paused": "Em pausa",
|
||||||
"returning": "A regressar ao cais"
|
"returning": "A regressar à doca"
|
||||||
},
|
},
|
||||||
"timer": {
|
"timer": {
|
||||||
"active": "ativo",
|
"active": "ativo",
|
||||||
@ -280,7 +280,8 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"unknown": "Desc",
|
"unknown": "Desc",
|
||||||
"unavailable": "Indisp",
|
"unavailable": "Indisp",
|
||||||
"error": "Erro"
|
"error": "Erro",
|
||||||
|
"entity_not_found": "Entidade não encontrada"
|
||||||
},
|
},
|
||||||
"alarm_control_panel": {
|
"alarm_control_panel": {
|
||||||
"armed": "Armado",
|
"armed": "Armado",
|
||||||
@ -336,7 +337,7 @@
|
|||||||
"introduction": "Alterar a configuração pode ser um processo repetitivo. Nós sabemos. Esta secção pretende tornar a sua vida um pouco mais fácil.",
|
"introduction": "Alterar a configuração pode ser um processo repetitivo. Nós sabemos. Esta secção pretende tornar a sua vida um pouco mais fácil.",
|
||||||
"validation": {
|
"validation": {
|
||||||
"heading": "Validar a configuração",
|
"heading": "Validar a configuração",
|
||||||
"introduction": "Valide a sua configuração caso tenha alterado recentemente a mesma e quiser certificar-se de que tudo é válido.",
|
"introduction": "Valide a sua configuração caso a tenha alterado recentemente e quiser certificar-se de que tudo está válido",
|
||||||
"check_config": "Verificar a configuração",
|
"check_config": "Verificar a configuração",
|
||||||
"valid": "Configuração válida!",
|
"valid": "Configuração válida!",
|
||||||
"invalid": "Configuração inválida"
|
"invalid": "Configuração inválida"
|
||||||
@ -354,6 +355,21 @@
|
|||||||
"introduction": "Controle o seu servidor Home Assistant... a partir do Home Assistant",
|
"introduction": "Controle o seu servidor Home Assistant... a partir do Home Assistant",
|
||||||
"restart": "Reiniciar",
|
"restart": "Reiniciar",
|
||||||
"stop": "Parar"
|
"stop": "Parar"
|
||||||
|
},
|
||||||
|
"core_config": {
|
||||||
|
"edit_requires_storage": "Editor desativado por causa da configuração existente em configuration.yaml.",
|
||||||
|
"location_name": "Nome da instalação do seu Home Assistant",
|
||||||
|
"latitude": "Latitude",
|
||||||
|
"longitude": "Longitude",
|
||||||
|
"elevation": "Elevação",
|
||||||
|
"elevation_meters": "metros",
|
||||||
|
"time_zone": "Fuso horário",
|
||||||
|
"unit_system": "Unidades do Sistema",
|
||||||
|
"unit_system_imperial": "Imperial",
|
||||||
|
"unit_system_metric": "Métrico",
|
||||||
|
"imperial_example": "Fahrenheit, libras",
|
||||||
|
"metric_example": "Celsius, quilogramas",
|
||||||
|
"save_button": "Guardar"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,7 +379,7 @@
|
|||||||
"description": "Personalizar as suas entidades",
|
"description": "Personalizar as suas entidades",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Personalização",
|
"header": "Personalização",
|
||||||
"introduction": "Ajustar atributos por entidade. Personalizações acrescentadas\/editadas terão efeitos imediatos. Remoção de personalizaçõe terão efeito quando a entidade for actualizada."
|
"introduction": "Ajustar atributos por entidade. Personalizações acrescentadas\/editadas terão efeitos imediatos. Remoção de personalizações terão efeito quando a entidade for atualizada."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"automation": {
|
"automation": {
|
||||||
@ -378,20 +394,20 @@
|
|||||||
"learn_more": "Saber mais sobre automações"
|
"learn_more": "Saber mais sobre automações"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"introduction": "Crie automatizações para tornar a sua casa viva",
|
"introduction": "Crie automações para tornar a sua casa viva",
|
||||||
"default_name": "Nova Automação",
|
"default_name": "Nova Automação",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"unsaved_confirm": "Existem alterações não guardas. Tem a certeza de que quer sair?",
|
"unsaved_confirm": "Existem alterações não guardadas. Tem a certeza de que quer sair?",
|
||||||
"alias": "Nome",
|
"alias": "Nome",
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"header": "Gatilhos",
|
"header": "Acionadores",
|
||||||
"introduction": "Os gatilhos são o que inicia o processamento de uma regra de automação. É possível especificar vários gatilhos para a mesma regra. Uma vez iniciado um gatilho, o Home Assistant irá validar as condições, e caso as mesmas ocorram, chamar a ação.\n\n[Saiba mais sobre gatilhos.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
"introduction": "Os acionadores são o que iniciam o processamento de uma regra de automação. É possível especificar vários acionadores para a mesma regra. Uma vez iniciado um acionador, o Home Assistant irá validar as condições, e caso as mesmas ocorram, chamar a ação.\n\n[Saiba mais sobre acionadores.](https:\/\/home-assistant.io\/docs\/automation\/trigger\/)",
|
||||||
"add": "Acrescentar gatilho",
|
"add": "Acrescentar acionador",
|
||||||
"duplicate": "Duplicar",
|
"duplicate": "Duplicar",
|
||||||
"delete": "Apagar",
|
"delete": "Apagar",
|
||||||
"delete_confirm": "Tem certeza que deseja apagar?",
|
"delete_confirm": "Tem certeza que deseja apagar?",
|
||||||
"unsupported_platform": "Plataforma não suportada: {platform}",
|
"unsupported_platform": "Plataforma não suportada: {platform}",
|
||||||
"type_select": "Tipo de gatilho",
|
"type_select": "Tipo de acionador",
|
||||||
"type": {
|
"type": {
|
||||||
"event": {
|
"event": {
|
||||||
"label": "Evento",
|
"label": "Evento",
|
||||||
@ -445,7 +461,7 @@
|
|||||||
"leave": "Sair"
|
"leave": "Sair"
|
||||||
},
|
},
|
||||||
"webhook": {
|
"webhook": {
|
||||||
"label": "Webhook ID",
|
"label": "Webhook",
|
||||||
"webhook_id": "Webhook ID"
|
"webhook_id": "Webhook ID"
|
||||||
},
|
},
|
||||||
"time_pattern": {
|
"time_pattern": {
|
||||||
@ -463,11 +479,11 @@
|
|||||||
"leave": "Sair"
|
"leave": "Sair"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"learn_more": "Saber mais sobre gatilhos"
|
"learn_more": "Saber mais sobre acionadores"
|
||||||
},
|
},
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"header": "Condições",
|
"header": "Condições",
|
||||||
"introduction": "As condições são uma parte opcional de uma regra de automação e podem ser usadas para impedir que uma ação ocorra quando um gatilho dispara. As condições embora pareçam muito semelhantes aos gatilhos, são muito diferentes. Um gatilho examinará os eventos que acontecem no sistema, enquanto uma condição apenas analisa a forma como o sistema parece no momento. Um gatilho pode observar que um interruptor está a ser ligado. Uma condição só pode ver se um interruptor está ligado ou desligado. \n\n [Saiba mais sobre as condições.] (Https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
"introduction": "As condições são uma parte opcional de uma regra de automação e podem ser usadas para impedir que uma ação ocorra quando um acionador é despoletado. As condições embora pareçam muito semelhantes aos acionadores, são muito diferentes. Um acionador examinará os eventos que acontecem no sistema, enquanto uma condição apenas analisa a forma como o sistema parece no momento. Um acionador pode observar que um interruptor está a ser ligado. Uma condição só pode ver se um interruptor está ligado ou desligado. \n\n [Saiba mais sobre as condições.] (Https:\/\/home-assistant.io\/docs\/scripts\/conditions\/)",
|
||||||
"add": "Acrescentar condição",
|
"add": "Acrescentar condição",
|
||||||
"duplicate": "Duplicar",
|
"duplicate": "Duplicar",
|
||||||
"delete": "Apagar",
|
"delete": "Apagar",
|
||||||
@ -544,7 +560,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"learn_more": "Saber mais sobre ações"
|
"learn_more": "Saber mais sobre ações"
|
||||||
}
|
},
|
||||||
|
"load_error_not_editable": "Apenas as automações em automations.yaml são editáveis.",
|
||||||
|
"load_error_unknown": "Erro ao carregar a automação ({err_no})."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"script": {
|
"script": {
|
||||||
@ -573,18 +591,19 @@
|
|||||||
"caption": "Adicionar Utilizador",
|
"caption": "Adicionar Utilizador",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"username": "Nome de Utilizador",
|
"username": "Nome de Utilizador",
|
||||||
"password": "Password",
|
"password": "Palavra-passe",
|
||||||
"create": "Criar"
|
"create": "Criar"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud": {
|
"cloud": {
|
||||||
"caption": "Home Assistant Cloud",
|
"caption": "Home Assistant Cloud",
|
||||||
"description_login": "Ligado como {email}",
|
"description_login": "Ligado como {email}",
|
||||||
"description_not_login": "Não está ligado"
|
"description_not_login": "Não está ligado",
|
||||||
|
"description_features": "Controle fora de casa, integre com a Alexa e o Google Assistant."
|
||||||
},
|
},
|
||||||
"integrations": {
|
"integrations": {
|
||||||
"caption": "Integrações",
|
"caption": "Integrações",
|
||||||
"description": "Gerir dispositivos e serviços ligados",
|
"description": "Gerir dispositivos e serviços conectados",
|
||||||
"discovered": "Detetados",
|
"discovered": "Detetados",
|
||||||
"configured": "Configurado",
|
"configured": "Configurado",
|
||||||
"new": "Configurar uma nova integração",
|
"new": "Configurar uma nova integração",
|
||||||
@ -600,18 +619,33 @@
|
|||||||
"firmware": "Firmware: {version}",
|
"firmware": "Firmware: {version}",
|
||||||
"device_unavailable": "Dispositivo indisponível",
|
"device_unavailable": "Dispositivo indisponível",
|
||||||
"entity_unavailable": "Entidade indisponível",
|
"entity_unavailable": "Entidade indisponível",
|
||||||
"no_area": "Nenhuma área"
|
"no_area": "Nenhuma Área",
|
||||||
|
"hub": "Conectado via"
|
||||||
|
},
|
||||||
|
"config_flow": {
|
||||||
|
"external_step": {
|
||||||
|
"description": "Para ser concluída, esta etapa exige que visite um site externo.",
|
||||||
|
"open_site": "Abrir site"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zha": {
|
"zha": {
|
||||||
"caption": "ZHA",
|
"caption": "ZHA",
|
||||||
"description": "Gestão de rede Zigbee Home Automation",
|
"description": "Gestão de rede Zigbee Home Automation",
|
||||||
"services": {
|
"services": {
|
||||||
"reconfigure": "Reconfigure o dispositivo ZHA (curar dispositivo). Use isto se estiver a ter problemas com o dispositivo. Se o dispositivo em questão for um dispositivo alimentado por bateria, certifique-se de que ele está ativo e a aceitar comandos ao usar este serviço.",
|
"reconfigure": "Reconfigure o dispositivo ZHA (curar dispositivo). Utilize isto se estiver a ter problemas com o dispositivo. Se o dispositivo em questão for um dispositivo alimentado por uma bateria, ao utilizar este serviço certifique-se de que o equipamento está ativo e a aceitar comandos.",
|
||||||
"updateDeviceName": "Definir um nome personalizado para este dispositivo no registo do dispositivo."
|
"updateDeviceName": "Definir um nome personalizado para este dispositivo no registo do dispositivo.",
|
||||||
|
"remove": "Remover um dispositivo da rede ZigBee."
|
||||||
},
|
},
|
||||||
"device_card": {
|
"device_card": {
|
||||||
"area_picker_label": "Área"
|
"device_name_placeholder": "Nome do utilizador",
|
||||||
|
"area_picker_label": "Área",
|
||||||
|
"update_name_button": "Atualizar Nome"
|
||||||
|
},
|
||||||
|
"add_device_page": {
|
||||||
|
"header": "Zigbee Home Automation - Adicionar Dispositivos",
|
||||||
|
"spinner": "À procura de dispositivos ZHA Zigbee...",
|
||||||
|
"discovery_text": "Os dispositivos descobertos aparecerão aqui. Siga as instruções para o(s) seu(s) dispositivo(s) e coloque o(s) dispositivo(s) em modo de emparelhamento."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"area_registry": {
|
"area_registry": {
|
||||||
@ -619,8 +653,8 @@
|
|||||||
"description": "Visão geral de todas as áreas da sua casa.",
|
"description": "Visão geral de todas as áreas da sua casa.",
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Registo de áreas",
|
"header": "Registo de áreas",
|
||||||
"introduction": "Áreas são usadas para organizar os dispositivos. Essas informações serão usadas no Home Assistant para ajudá-lo a organizar sua interface, permissões e integrações com outros sistemas.",
|
"introduction": "As áreas são utilizadas para organizar os dispositivos. Essas informações serão utilizadas no Home Assistant para o ajudar a organizar o seu interface, permissões e integrações com outros sistemas.",
|
||||||
"introduction2": "Para colocar dispositivos em uma área, use o link abaixo para navegar até a página de integrações e, em seguida, clique em uma integração configurada para acessar os cartões de dispositivos.",
|
"introduction2": "Para colocar dispositivos numa área, use o link abaixo para navegar até a página de integrações e em seguida, clique numa integração configurada para aceder aos cartões de dispositivos.",
|
||||||
"integrations_page": "Página de Integrações",
|
"integrations_page": "Página de Integrações",
|
||||||
"no_areas": "Parece que ainda não tem áreas!",
|
"no_areas": "Parece que ainda não tem áreas!",
|
||||||
"create_area": "CRIAR ÁREA"
|
"create_area": "CRIAR ÁREA"
|
||||||
@ -630,7 +664,7 @@
|
|||||||
"editor": {
|
"editor": {
|
||||||
"default_name": "Nova área",
|
"default_name": "Nova área",
|
||||||
"delete": "APAGAR",
|
"delete": "APAGAR",
|
||||||
"update": "ACTUALIZAR",
|
"update": "ATUALIZAR",
|
||||||
"create": "CRIAR"
|
"create": "CRIAR"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -640,20 +674,20 @@
|
|||||||
"picker": {
|
"picker": {
|
||||||
"header": "Registo de Entidades",
|
"header": "Registo de Entidades",
|
||||||
"unavailable": "(indisponível)",
|
"unavailable": "(indisponível)",
|
||||||
"introduction": "O Home Assistant mantém um registro de todas as entidades que já viu e que podem ser identificadas exclusivamente. Cada uma dessas entidades terá um ID de entidade atribuído, que será reservado apenas para essa entidade.",
|
"introduction": "O Home Assistant mantém um registo de todas as entidades que foram alguma vez detetadas e que podem ser identificadas de uma forma única. Cada uma dessas entidades terá um ID de entidade atribuído, que será reservado apenas para essa entidade.",
|
||||||
"introduction2": "Use o registo da entidade para substituir o nome, alterar o ID da entidade ou remover a entrada do Assistente Inicial. Observe que a remoção da entrada do registo de entidade não removerá a entidade. Para fazer isso, siga o link abaixo e remova-o da página de integrações.",
|
"introduction2": "Use o registo da entidade para substituir o nome, alterar o ID da entidade ou remover a entrada do Home Assistant. Note que a remoção da entrada do registo de entidade não removerá a entidade. Para fazer isso, siga o link abaixo e remova-o da página de integrações.",
|
||||||
"integrations_page": "Página de Integrações"
|
"integrations_page": "Página de Integrações"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"unavailable": "Esta entidade não está atualmente disponível.",
|
"unavailable": "Esta entidade não está atualmente disponível.",
|
||||||
"default_name": "Nova área",
|
"default_name": "Nova Área",
|
||||||
"delete": "APAGAR",
|
"delete": "APAGAR",
|
||||||
"update": "ACTUALIZAR"
|
"update": "ATUALIZAR"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"person": {
|
"person": {
|
||||||
"caption": "Pessoas",
|
"caption": "Pessoas",
|
||||||
"description": "Gerir as que pessoas que o Home Assistant segue.",
|
"description": "Gerir as pessoas que o Home Assistant segue.",
|
||||||
"detail": {
|
"detail": {
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"device_tracker_intro": "Selecione os dispositivos que pertencem a esta pessoa.",
|
"device_tracker_intro": "Selecione os dispositivos que pertencem a esta pessoa.",
|
||||||
@ -667,13 +701,13 @@
|
|||||||
"header": "Notificações push",
|
"header": "Notificações push",
|
||||||
"description": "Enviar notificações para este dispositivo.",
|
"description": "Enviar notificações para este dispositivo.",
|
||||||
"error_load_platform": "Configurar notify.html5.",
|
"error_load_platform": "Configurar notify.html5.",
|
||||||
"error_use_https": "Requer interface com utilizador, com SSL activo.",
|
"error_use_https": "Requer SSL ativo para o interface principal",
|
||||||
"push_notifications": "Notificações push",
|
"push_notifications": "Notificações push",
|
||||||
"link_promo": "Saiba mais"
|
"link_promo": "Saiba mais"
|
||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"header": "Idioma",
|
"header": "Idioma",
|
||||||
"link_promo": "Ajude a traduzir",
|
"link_promo": "Ajude na tradução",
|
||||||
"dropdown_label": "Idioma"
|
"dropdown_label": "Idioma"
|
||||||
},
|
},
|
||||||
"themes": {
|
"themes": {
|
||||||
@ -684,7 +718,7 @@
|
|||||||
},
|
},
|
||||||
"refresh_tokens": {
|
"refresh_tokens": {
|
||||||
"header": "Atualizar Tokens",
|
"header": "Atualizar Tokens",
|
||||||
"description": "Cada \"refresh token\" representa a uma sessão de utilizador. Os \"refresh tokens\" serão automáticamente removidos quando clicar sair. Os seguintes \"refresh tokens\" estão activos para a sua conta.",
|
"description": "Cada \"refresh token\" representa uma sessão de utilizador. Os \"refresh tokens\" serão automaticamente removidos quando clicar em sair. Os seguintes \"refresh tokens\" estão activos para a sua conta.",
|
||||||
"token_title": "Atualizar o token de {clientId}",
|
"token_title": "Atualizar o token de {clientId}",
|
||||||
"created_at": "Criado a {date}",
|
"created_at": "Criado a {date}",
|
||||||
"confirm_delete": "Tem certeza de que deseja apagar o \"refresh token\" de {name} ?",
|
"confirm_delete": "Tem certeza de que deseja apagar o \"refresh token\" de {name} ?",
|
||||||
@ -695,7 +729,7 @@
|
|||||||
},
|
},
|
||||||
"long_lived_access_tokens": {
|
"long_lived_access_tokens": {
|
||||||
"header": "Tokens de acesso de longa duração",
|
"header": "Tokens de acesso de longa duração",
|
||||||
"description": "Crie tokens de acesso de longa duração para permitir que os seus scripts possam interagir com a sua instancia do Home Assistant. Cada token será válido durante 10 anos após criação. Os seguintes tokens de acesso de longa duração estão actualmente activos.",
|
"description": "Crie tokens de acesso de longa duração para permitir que os seus scripts possam interagir com a sua instância do Home Assistant. Cada token será válido durante 10 anos após criação. Os seguintes tokens de acesso de longa duração estão atualmente activos.",
|
||||||
"learn_auth_requests": "Aprenda a fazer pedidos autenticados.",
|
"learn_auth_requests": "Aprenda a fazer pedidos autenticados.",
|
||||||
"created_at": "Criado a {date}",
|
"created_at": "Criado a {date}",
|
||||||
"confirm_delete": "Tem certeza de que deseja apagar o token de acesso de {name} ?",
|
"confirm_delete": "Tem certeza de que deseja apagar o token de acesso de {name} ?",
|
||||||
@ -703,26 +737,26 @@
|
|||||||
"create": "Criar Token",
|
"create": "Criar Token",
|
||||||
"create_failed": "Falha ao criar o token de acesso.",
|
"create_failed": "Falha ao criar o token de acesso.",
|
||||||
"prompt_name": "Nome?",
|
"prompt_name": "Nome?",
|
||||||
"prompt_copy_token": "Copie seu token de acesso. Não será mostrado novamente.",
|
"prompt_copy_token": "Copie o seu token de acesso. Não será mostrado novamente.",
|
||||||
"empty_state": "Ainda não tem um token de longa duração",
|
"empty_state": "Ainda não tem um token de longa duração",
|
||||||
"last_used": "Última utilização a {date} em {location}",
|
"last_used": "Última utilização a {date} em {location}",
|
||||||
"not_used": "Nunca foi utilizado"
|
"not_used": "Nunca foi utilizado"
|
||||||
},
|
},
|
||||||
"current_user": "Esta actualmente ligado como {fullName}",
|
"current_user": "Esta atualmente ligado como {fullName}",
|
||||||
"is_owner": "Você é um proprietário.",
|
"is_owner": "Você é um proprietário.",
|
||||||
"logout": "Sair",
|
"logout": "Sair",
|
||||||
"change_password": {
|
"change_password": {
|
||||||
"header": "Alterar palavra-passe",
|
"header": "Alterar palavra-passe",
|
||||||
"current_password": "Palavra-passe actual",
|
"current_password": "Palavra-passe atual",
|
||||||
"new_password": "Nova palavra-passe",
|
"new_password": "Nova palavra-passe",
|
||||||
"confirm_new_password": "Confirmar nova palavra-passe",
|
"confirm_new_password": "Confirmar a nova palavra-passe",
|
||||||
"error_required": "Obrigatório",
|
"error_required": "Obrigatório",
|
||||||
"submit": "Enviar"
|
"submit": "Enviar"
|
||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"header": "Módulos de autenticação por multíplos-factores",
|
"header": "Módulos de Autenticação por Multíplos-fatores",
|
||||||
"disable": "Desactivar",
|
"disable": "Desativar",
|
||||||
"enable": "Activar",
|
"enable": "Ativar",
|
||||||
"confirm_disable": "Tem certeza de que deseja desativar {name} ?"
|
"confirm_disable": "Tem certeza de que deseja desativar {name} ?"
|
||||||
},
|
},
|
||||||
"mfa_setup": {
|
"mfa_setup": {
|
||||||
@ -737,11 +771,11 @@
|
|||||||
"initializing": "A inicializar",
|
"initializing": "A inicializar",
|
||||||
"authorizing_client": "Está prestes a dar acesso a {clientId} à sua instância do Home Assistant.",
|
"authorizing_client": "Está prestes a dar acesso a {clientId} à sua instância do Home Assistant.",
|
||||||
"logging_in_with": "Iniciar a sessão com **{authProviderName}**.",
|
"logging_in_with": "Iniciar a sessão com **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Ou faça login com",
|
"pick_auth_provider": "Ou entre com",
|
||||||
"abort_intro": "Login abortado",
|
"abort_intro": "Início de sessão cancelado",
|
||||||
"form": {
|
"form": {
|
||||||
"working": "Por favor, aguarde",
|
"working": "Por favor, aguarde",
|
||||||
"unknown_error": "Algo de errado aconteceu",
|
"unknown_error": "Algo correu mal",
|
||||||
"providers": {
|
"providers": {
|
||||||
"homeassistant": {
|
"homeassistant": {
|
||||||
"step": {
|
"step": {
|
||||||
@ -796,7 +830,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"user": "Utilizador"
|
"user": "Utilizador"
|
||||||
},
|
},
|
||||||
"description": "Por favor seleccione o utilizador com o qual pretende entrar:"
|
"description": "Por favor selecione o utilizador com o qual pretende entrar:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
@ -813,9 +847,9 @@
|
|||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Código de autenticações por dois factores"
|
"code": "Código de autenticações por dois fatores"
|
||||||
},
|
},
|
||||||
"description": "Abrir **{mfa_module_name}** no seu dispositivo para ver o código de autenticação por dois factores e verificar a sua identidade:"
|
"description": "Abrir o **{mfa_module_name}** no seu dispositivo para ver o código de autenticação por dois factores e validar a sua identidade:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@ -830,7 +864,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
"intro": "Está pronto para despertar a sua casa, reclamar a sua privacidade e juntar a uma comunidade mundial de tecnólogos?",
|
"intro": "Está pronto para despertar a sua casa, reclamar a sua privacidade e juntar-se a uma comunidade mundial de tecnólogos?",
|
||||||
"user": {
|
"user": {
|
||||||
"intro": "Vamos começar por criar um utilizador.",
|
"intro": "Vamos começar por criar um utilizador.",
|
||||||
"required_field": "Obrigatório",
|
"required_field": "Obrigatório",
|
||||||
@ -838,13 +872,26 @@
|
|||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"username": "Utilizador",
|
"username": "Utilizador",
|
||||||
"password": "Palavra-passe",
|
"password": "Palavra-passe",
|
||||||
"password_confirm": "Confirme Password"
|
"password_confirm": "Confirme a palavra-passe"
|
||||||
},
|
},
|
||||||
"create_account": "Criar conta",
|
"create_account": "Criar conta",
|
||||||
"error": {
|
"error": {
|
||||||
"required_fields": "Preencha todos os campos obrigatórios",
|
"required_fields": "Preencha todos os campos obrigatórios",
|
||||||
"password_not_match": "Password não coincide"
|
"password_not_match": "As palavras-passe não coincidem"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"integration": {
|
||||||
|
"intro": "Dispositivos e serviços são representados no Home Assistant como integrações. Pode configurá-los agora ou fazê-lo mais tarde no ecrã de configuração.",
|
||||||
|
"more_integrations": "Mais",
|
||||||
|
"finish": "Terminar"
|
||||||
|
},
|
||||||
|
"core-config": {
|
||||||
|
"intro": "Olá {name}, bem-vindo ao Home Assistant. Que nome quer dar à sua casa?",
|
||||||
|
"intro_location": "Gostaríamos de saber onde vive. Esta informação ajudará a exibir informações e a configurar automações baseadas no sol. Os dados nunca são partilhados fora da sua rede.",
|
||||||
|
"intro_location_detect": "Podemos ajudá-lo a preencher esta informação fazendo um pedido único a um serviço externo.",
|
||||||
|
"location_name_default": "Início",
|
||||||
|
"button_detect": "Detetar",
|
||||||
|
"finish": "Próxima"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lovelace": {
|
"lovelace": {
|
||||||
@ -857,14 +904,22 @@
|
|||||||
"empty_state": {
|
"empty_state": {
|
||||||
"title": "Bem-vindo a casa",
|
"title": "Bem-vindo a casa",
|
||||||
"no_devices": "Esta página permite-lhe controlar os seus dispositivos, no entanto, parece que ainda não tem dispositivos configurados. Vá para a página de integrações para começar.",
|
"no_devices": "Esta página permite-lhe controlar os seus dispositivos, no entanto, parece que ainda não tem dispositivos configurados. Vá para a página de integrações para começar.",
|
||||||
"go_to_integrations_page": "Ir para a página das integrações."
|
"go_to_integrations_page": "Ir para a página de integrações."
|
||||||
|
},
|
||||||
|
"picture-elements": {
|
||||||
|
"hold": "Mantenha:",
|
||||||
|
"tap": "Toque:",
|
||||||
|
"navigate_to": "Navegue até {location}",
|
||||||
|
"toggle": "Alternar {name}",
|
||||||
|
"call_service": "Chamar o serviço {name}",
|
||||||
|
"more_info": "Mostrar mais informações: {name}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"edit_card": {
|
"edit_card": {
|
||||||
"header": "Configuração do cartão",
|
"header": "Configuração do cartão",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"toggle_editor": "Alterar para editor",
|
"toggle_editor": "Alternar Editor",
|
||||||
"pick_card": "Escolha o cartão que deseja adicionar.",
|
"pick_card": "Escolha o cartão que deseja adicionar.",
|
||||||
"add": "Adicionar Cartão",
|
"add": "Adicionar Cartão",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
@ -874,8 +929,8 @@
|
|||||||
"migrate": {
|
"migrate": {
|
||||||
"header": "Configuração Incompatível",
|
"header": "Configuração Incompatível",
|
||||||
"para_no_id": "Este elemento não possui um ID. Por favor adicione um ID a este elemento em 'ui-lovelace.yaml'.",
|
"para_no_id": "Este elemento não possui um ID. Por favor adicione um ID a este elemento em 'ui-lovelace.yaml'.",
|
||||||
"para_migrate": "O Home Assistant pode adicionar IDs a todos os seus cartões e vistas automaticamente clicando no botão 'Migrar configuração'.",
|
"para_migrate": "Ao clicar no botão 'Migrar configuração', o Home Assistant pode adicionar IDs a todos os seus cartões e vistas automaticamente.",
|
||||||
"migrate": "Migrar configuração"
|
"migrate": "Migrar a configuração"
|
||||||
},
|
},
|
||||||
"header": "Editar UI",
|
"header": "Editar UI",
|
||||||
"edit_view": {
|
"edit_view": {
|
||||||
@ -886,19 +941,19 @@
|
|||||||
},
|
},
|
||||||
"save_config": {
|
"save_config": {
|
||||||
"header": "Assumir controle sobre a interface do Lovelace",
|
"header": "Assumir controle sobre a interface do Lovelace",
|
||||||
"para": "Por omissão o Home Assistant irá manter a sua interface de utilizador, actualizando sempre que uma entidade nova ou componentes Lovelace fiquem disponíveis. Se assumir o controle não será possivel fazer alterações automáticas por si.",
|
"para": "Por omissão o Home Assistant irá manter a sua interface de utilizador, atualizando-a sempre que uma nova entidade ou novos componentes Lovelace fiquem disponíveis. Se assumir o controlo, não faremos mais alterações automáticas por si.",
|
||||||
"para_sure": "Tem certeza que deseja assumir o controle sobre a interface de utilizador?",
|
"para_sure": "Tem certeza que deseja assumir o controlo sobre a interface de utilizador?",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"save": "Assumir o controle"
|
"save": "Assumir o controlo"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"raw_editor": "Editor de configuração fonte."
|
"raw_editor": "Editor de configuração do código-fonte"
|
||||||
},
|
},
|
||||||
"raw_editor": {
|
"raw_editor": {
|
||||||
"header": "Editar configuração",
|
"header": "Editar configuração",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"unsaved_changes": "Alterações não gravadas",
|
"unsaved_changes": "Alterações não guardadas",
|
||||||
"saved": "Guardou"
|
"saved": "Guardada"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
@ -908,14 +963,63 @@
|
|||||||
"refresh": "Atualizar"
|
"refresh": "Atualizar"
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"entity_not_found": "Entidade indisponível",
|
"entity_not_found": "Entidade não disponível: {entity}",
|
||||||
"entity_non_numeric": "Entidade é não numérica"
|
"entity_non_numeric": "A entidade é não numérica: {entity}"
|
||||||
|
},
|
||||||
|
"changed_toast": {
|
||||||
|
"message": "A configuração do Lovelace foi atualizada, gostaria de atualizar?",
|
||||||
|
"refresh": "Atualizar"
|
||||||
|
},
|
||||||
|
"reload_lovelace": "Recarregar Lovelace"
|
||||||
|
},
|
||||||
|
"page-demo": {
|
||||||
|
"cards": {
|
||||||
|
"demo": {
|
||||||
|
"demo_by": "por {nome}",
|
||||||
|
"next_demo": "Próxima demonstração",
|
||||||
|
"introduction": "Bem vindo a casa! Chegou à demonstração do Home Assistant, onde exibimos as melhores UIs criadas pela nossa comunidade.",
|
||||||
|
"learn_more": "Saiba mais sobre o Home Assistant"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"arsaboo": {
|
||||||
|
"names": {
|
||||||
|
"upstairs": "Andar de cima",
|
||||||
|
"family_room": "Sala",
|
||||||
|
"kitchen": "Cozinha",
|
||||||
|
"patio": "Pátio",
|
||||||
|
"hallway": "Corredor",
|
||||||
|
"master_bedroom": "Quarto principal",
|
||||||
|
"left": "Esquerda",
|
||||||
|
"right": "Direita",
|
||||||
|
"mirror": "Espelho"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"lights": "Luzes",
|
||||||
|
"information": "Informação",
|
||||||
|
"morning_commute": "Trajeto da manhã",
|
||||||
|
"commute_home": "Deslocação até casa",
|
||||||
|
"entertainment": "Entretenimento",
|
||||||
|
"activity": "Atividade",
|
||||||
|
"hdmi_input": "Entrada HDMI",
|
||||||
|
"hdmi_switcher": "Comutador HDMI",
|
||||||
|
"volume": "Volume",
|
||||||
|
"total_tv_time": "Tempo total de TV",
|
||||||
|
"turn_tv_off": "Desligar a televisão",
|
||||||
|
"air": "Ar"
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"watching": "a assistir",
|
||||||
|
"minutes_abbr": "min"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"log_out": "Sair",
|
"log_out": "Sair",
|
||||||
"developer_tools": "Ferramentas de programação"
|
"developer_tools": "Ferramentas de programação",
|
||||||
|
"external_app_configuration": "Configuração da Aplicação"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"loading": "A carregar",
|
"loading": "A carregar",
|
||||||
@ -983,7 +1087,7 @@
|
|||||||
"arm_away": "Armado ausente",
|
"arm_away": "Armado ausente",
|
||||||
"arm_night": "Armado noite",
|
"arm_night": "Armado noite",
|
||||||
"armed_custom_bypass": "Desvio personalizado",
|
"armed_custom_bypass": "Desvio personalizado",
|
||||||
"arm_custom_bypass": "bypass personalizado"
|
"arm_custom_bypass": "Desvio personalizado"
|
||||||
},
|
},
|
||||||
"automation": {
|
"automation": {
|
||||||
"last_triggered": "Última ocorrência",
|
"last_triggered": "Última ocorrência",
|
||||||
@ -1001,7 +1105,7 @@
|
|||||||
"light": {
|
"light": {
|
||||||
"brightness": "Brilho",
|
"brightness": "Brilho",
|
||||||
"color_temperature": "Temperatura de cor",
|
"color_temperature": "Temperatura de cor",
|
||||||
"white_value": "Quantidade de branco",
|
"white_value": "Quantidade de brancos",
|
||||||
"effect": "Efeito"
|
"effect": "Efeito"
|
||||||
},
|
},
|
||||||
"media_player": {
|
"media_player": {
|
||||||
@ -1022,13 +1126,13 @@
|
|||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"code": "Código",
|
"code": "Código",
|
||||||
"lock": "Trancar",
|
"lock": "Bloquear",
|
||||||
"unlock": "Destrancar"
|
"unlock": "Desbloquear"
|
||||||
},
|
},
|
||||||
"vacuum": {
|
"vacuum": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"resume_cleaning": "Retomar a limpeza",
|
"resume_cleaning": "Retomar a limpeza",
|
||||||
"return_to_base": "Regressar ao cais",
|
"return_to_base": "Voltar à doca",
|
||||||
"start_cleaning": "Iniciar a limpeza",
|
"start_cleaning": "Iniciar a limpeza",
|
||||||
"turn_on": "Ligar",
|
"turn_on": "Ligar",
|
||||||
"turn_off": "Desligar"
|
"turn_off": "Desligar"
|
||||||
@ -1077,13 +1181,13 @@
|
|||||||
},
|
},
|
||||||
"dialogs": {
|
"dialogs": {
|
||||||
"more_info_settings": {
|
"more_info_settings": {
|
||||||
"save": "Salvar",
|
"save": "Guardar",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"entity_id": "ID da entidade"
|
"entity_id": "ID da entidade"
|
||||||
},
|
},
|
||||||
"more_info_control": {
|
"more_info_control": {
|
||||||
"script": {
|
"script": {
|
||||||
"last_action": "Última ação"
|
"last_action": "Última ocorrência"
|
||||||
},
|
},
|
||||||
"sun": {
|
"sun": {
|
||||||
"elevation": "Elevação",
|
"elevation": "Elevação",
|
||||||
@ -1147,7 +1251,7 @@
|
|||||||
"hassio": "Hass.io",
|
"hassio": "Hass.io",
|
||||||
"homeassistant": "Home Assistant",
|
"homeassistant": "Home Assistant",
|
||||||
"lovelace": "Lovelace",
|
"lovelace": "Lovelace",
|
||||||
"system_health": "Saúde do sistema",
|
"system_health": "Integridade do Sistema",
|
||||||
"person": "Pessoa"
|
"person": "Pessoa"
|
||||||
},
|
},
|
||||||
"attribute": {
|
"attribute": {
|
||||||
@ -1160,15 +1264,15 @@
|
|||||||
"state_attributes": {
|
"state_attributes": {
|
||||||
"climate": {
|
"climate": {
|
||||||
"fan_mode": {
|
"fan_mode": {
|
||||||
"off": "Off",
|
"off": "Desligar",
|
||||||
"on": "On",
|
"on": "Ligar",
|
||||||
"auto": "Auto"
|
"auto": "Automático"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"system-admin": "Administradores",
|
"system-admin": "Administradores",
|
||||||
"system-users": "Utilizadores",
|
"system-users": "Utilizadores",
|
||||||
"system-read-only": "Utilizadores somente de leitura"
|
"system-read-only": "Utilizadores só com permissões de leitura"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -133,7 +133,7 @@
|
|||||||
"climate": {
|
"climate": {
|
||||||
"off": "Выкл",
|
"off": "Выкл",
|
||||||
"on": "Вкл",
|
"on": "Вкл",
|
||||||
"heat": "Нагрев",
|
"heat": "Обогрев",
|
||||||
"cool": "Охлаждение",
|
"cool": "Охлаждение",
|
||||||
"idle": "Бездействие",
|
"idle": "Бездействие",
|
||||||
"auto": "Авто",
|
"auto": "Авто",
|
||||||
@ -990,17 +990,17 @@
|
|||||||
"patio": "Внутренний дворик",
|
"patio": "Внутренний дворик",
|
||||||
"hallway": "Прихожая",
|
"hallway": "Прихожая",
|
||||||
"master_bedroom": "Спальня",
|
"master_bedroom": "Спальня",
|
||||||
"left": "Влево",
|
"left": "Левая сторона",
|
||||||
"right": "Вправо",
|
"right": "Правая сторона",
|
||||||
"mirror": "Зеркало"
|
"mirror": "Зеркало"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"lights": "Свет",
|
"lights": "Освещение",
|
||||||
"information": "Информация",
|
"information": "Информация",
|
||||||
"morning_commute": "Утренняя поездка",
|
"morning_commute": "Утренняя поездка",
|
||||||
"commute_home": "Поездка домой",
|
"commute_home": "Поездка домой",
|
||||||
"entertainment": "Развлечения",
|
"entertainment": "Развлечения",
|
||||||
"activity": "Активность",
|
"activity": "Процесс",
|
||||||
"hdmi_input": "Вход HDMI",
|
"hdmi_input": "Вход HDMI",
|
||||||
"hdmi_switcher": "Переключатель HDMI",
|
"hdmi_switcher": "Переключатель HDMI",
|
||||||
"volume": "Громкость",
|
"volume": "Громкость",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user