mirror of
https://github.com/home-assistant/frontend.git
synced 2026-08-30 20:00:09 +00:00
Compare commits
60
Commits
20200820.0
...
hide-stop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaeb9cde7b | ||
|
|
b39b54e0ac | ||
|
|
119c5c9071 | ||
|
|
7a4c9b128c | ||
|
|
dc5b92030f | ||
|
|
db0a010d7c | ||
|
|
a117a19bdf | ||
|
|
5f46fdb406 | ||
|
|
f0201de4cc | ||
|
|
6cd51a318b | ||
|
|
c1a4b27bc7 | ||
|
|
5113222050 | ||
|
|
90f12eea5e | ||
|
|
2403743701 | ||
|
|
3e6a759309 | ||
|
|
35a430e9f4 | ||
|
|
b644f7d23d | ||
|
|
7702a05464 | ||
|
|
493af5fe82 | ||
|
|
ac66a59cec | ||
|
|
e10c8faa47 | ||
|
|
9b7d17433c | ||
|
|
a40eb1ff43 | ||
|
|
04df6c3e9e | ||
|
|
1b970e5a66 | ||
|
|
75406c2d01 | ||
|
|
64d3511fbc | ||
|
|
c610f54977 | ||
|
|
358c5205d2 | ||
|
|
5503cd0589 | ||
|
|
dae42b1bd9 | ||
|
|
06a25284e8 | ||
|
|
5989560f15 | ||
|
|
63c995e5da | ||
|
|
dc5607f554 | ||
|
|
d49302c032 | ||
|
|
63fef9bd4b | ||
|
|
6599351d45 | ||
|
|
47e9531972 | ||
|
|
3ba31483f4 | ||
|
|
f4ca94f2e1 | ||
|
|
67f9be2b77 | ||
|
|
e2fd155e1b | ||
|
|
931068dede | ||
|
|
bc4c9cc40d | ||
|
|
294665fbe8 | ||
|
|
e8f6a79c8f | ||
|
|
5fd8b5c5b9 | ||
|
|
226b2a73af | ||
|
|
42d421a6fc | ||
|
|
a90203f256 | ||
|
|
c3ef79caa9 | ||
|
|
1439afcd9c | ||
|
|
d263b19910 | ||
|
|
1e477226ea | ||
|
|
026fc1d2e3 | ||
|
|
2d4bd9857a | ||
|
|
8f48f5b45c | ||
|
|
22210b7400 | ||
|
|
7d05855ee0 |
@@ -9,7 +9,6 @@ import {
|
||||
mdiExclamationThick,
|
||||
mdiFlask,
|
||||
mdiHomeAssistant,
|
||||
mdiInformation,
|
||||
mdiKey,
|
||||
mdiNetwork,
|
||||
mdiPound,
|
||||
@@ -53,6 +52,7 @@ import { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/hassio-card-content";
|
||||
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
|
||||
const STAGE_ICON = {
|
||||
stable: mdiCheckCircle,
|
||||
@@ -386,67 +386,94 @@ class HassioAddonInfo extends LitElement {
|
||||
|
||||
${this.addon.version
|
||||
? html`
|
||||
<div class="state">
|
||||
<div>Start on boot</div>
|
||||
<ha-switch
|
||||
@change=${this._startOnBootToggled}
|
||||
.checked=${this.addon.boot === "auto"}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</div>
|
||||
${this.addon.auto_update || this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<div class="state">
|
||||
<div>Auto update</div>
|
||||
<ha-switch
|
||||
@change=${this._autoUpdateToggled}
|
||||
.checked=${this.addon.auto_update}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<div class="state">
|
||||
<div>Show in sidebar</div>
|
||||
<ha-switch
|
||||
@change=${this._panelToggled}
|
||||
.checked=${this.addon.ingress_panel}
|
||||
.disabled=${this._computeCannotIngressSidebar}
|
||||
haptic
|
||||
></ha-switch>
|
||||
${this._computeCannotIngressSidebar
|
||||
? html`
|
||||
<span>
|
||||
This option requires Home Assistant 0.92 or
|
||||
later.
|
||||
</span>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this._computeUsesProtectedOptions
|
||||
? html`
|
||||
<div class="state">
|
||||
<div>
|
||||
Protection mode
|
||||
<span>
|
||||
<ha-svg-icon path=${mdiInformation}></ha-svg-icon>
|
||||
<paper-tooltip>
|
||||
Grant the add-on elevated system access.
|
||||
</paper-tooltip>
|
||||
<div class="addon-options">
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Start on boot
|
||||
</span>
|
||||
<span slot="description">
|
||||
Make the add-on start during a system boot
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._startOnBootToggled}
|
||||
.checked=${this.addon.boot === "auto"}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
|
||||
${this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Watchdog
|
||||
</span>
|
||||
</div>
|
||||
<ha-switch
|
||||
@change=${this._protectionToggled}
|
||||
.checked=${this.addon.protected}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<span slot="description">
|
||||
This will start the add-on if it crashes
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._watchdogToggled}
|
||||
.checked=${this.addon.watchdog}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auto_update || this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Auto update
|
||||
</span>
|
||||
<span slot="description">
|
||||
Auto update the add-on when there is a new version
|
||||
available
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._autoUpdateToggled}
|
||||
.checked=${this.addon.auto_update}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Show in sidebar
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this._computeCannotIngressSidebar
|
||||
? "This option requires Home Assistant 0.92 or later."
|
||||
: "Add this add-on to your sidebar"}
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._panelToggled}
|
||||
.checked=${this.addon.ingress_panel}
|
||||
.disabled=${this._computeCannotIngressSidebar}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
${this._computeUsesProtectedOptions
|
||||
? html`
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
Protection mode
|
||||
</span>
|
||||
<span slot="description">
|
||||
Blocks elevated system access from the add-on
|
||||
</span>
|
||||
<ha-switch
|
||||
@change=${this._protectionToggled}
|
||||
.checked=${this.addon.protected}
|
||||
haptic
|
||||
></ha-switch>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
@@ -552,137 +579,6 @@ class HassioAddonInfo extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
ha-card {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
ha-card.warning {
|
||||
background-color: var(--error-color);
|
||||
color: white;
|
||||
}
|
||||
ha-card.warning .card-header {
|
||||
color: white;
|
||||
}
|
||||
ha-card.warning .card-content {
|
||||
color: white;
|
||||
}
|
||||
ha-card.warning mwc-button {
|
||||
--mdc-theme-primary: white !important;
|
||||
}
|
||||
.warning {
|
||||
color: var(--error-color);
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
.light-color {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.addon-header {
|
||||
padding-left: 8px;
|
||||
font-size: 24px;
|
||||
color: var(--ha-card-header-color, --primary-text-color);
|
||||
}
|
||||
.addon-version {
|
||||
float: right;
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.description {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
img.logo {
|
||||
max-height: 60px;
|
||||
margin: 16px 0;
|
||||
display: block;
|
||||
}
|
||||
.state {
|
||||
display: flex;
|
||||
margin: 33px 0;
|
||||
}
|
||||
.state div {
|
||||
width: 180px;
|
||||
display: inline-block;
|
||||
}
|
||||
.state ha-svg-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-switch {
|
||||
display: flex;
|
||||
}
|
||||
ha-svg-icon.running {
|
||||
color: var(--paper-green-400);
|
||||
}
|
||||
ha-svg-icon.stopped {
|
||||
color: var(--google-red-300);
|
||||
}
|
||||
ha-call-api-button {
|
||||
font-weight: 500;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
protection-enable mwc-button {
|
||||
--mdc-theme-primary: white;
|
||||
}
|
||||
.description a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.red {
|
||||
--ha-label-badge-color: var(--label-badge-red, #df4c1e);
|
||||
}
|
||||
.blue {
|
||||
--ha-label-badge-color: var(--label-badge-blue, #039be5);
|
||||
}
|
||||
.green {
|
||||
--ha-label-badge-color: var(--label-badge-green, #0da035);
|
||||
}
|
||||
.yellow {
|
||||
--ha-label-badge-color: var(--label-badge-yellow, #f4b400);
|
||||
}
|
||||
.security {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-actions {
|
||||
display: flow-root;
|
||||
}
|
||||
.security h3 {
|
||||
margin-bottom: 8px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.security ha-label-badge {
|
||||
cursor: pointer;
|
||||
margin-right: 4px;
|
||||
--ha-label-badge-padding: 8px 0 0 0;
|
||||
}
|
||||
.changelog {
|
||||
display: contents;
|
||||
}
|
||||
.changelog-link {
|
||||
color: var(--primary-color);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-markdown {
|
||||
padding: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private get _computeHassioApi(): boolean {
|
||||
return (
|
||||
this.addon.hassio_api &&
|
||||
@@ -771,6 +667,24 @@ class HassioAddonInfo extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _watchdogToggled(): Promise<void> {
|
||||
this._error = undefined;
|
||||
const data: HassioAddonSetOptionParams = {
|
||||
watchdog: !this.addon.watchdog,
|
||||
};
|
||||
try {
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "option",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err) {
|
||||
this._error = `Failed to set addon option, ${err.body?.message || err}`;
|
||||
}
|
||||
}
|
||||
|
||||
private async _autoUpdateToggled(): Promise<void> {
|
||||
this._error = undefined;
|
||||
const data: HassioAddonSetOptionParams = {
|
||||
@@ -887,6 +801,146 @@ class HassioAddonInfo extends LitElement {
|
||||
this._error = `Failed to uninstall addon, ${err.body?.message || err}`;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
hassioStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
ha-card {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
ha-card.warning {
|
||||
background-color: var(--error-color);
|
||||
color: white;
|
||||
}
|
||||
ha-card.warning .card-header {
|
||||
color: white;
|
||||
}
|
||||
ha-card.warning .card-content {
|
||||
color: white;
|
||||
}
|
||||
ha-card.warning mwc-button {
|
||||
--mdc-theme-primary: white !important;
|
||||
}
|
||||
.warning {
|
||||
color: var(--error-color);
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
.light-color {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.addon-header {
|
||||
padding-left: 8px;
|
||||
font-size: 24px;
|
||||
color: var(--ha-card-header-color, --primary-text-color);
|
||||
}
|
||||
.addon-version {
|
||||
float: right;
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.description {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
img.logo {
|
||||
max-height: 60px;
|
||||
margin: 16px 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
ha-switch {
|
||||
display: flex;
|
||||
}
|
||||
ha-svg-icon.running {
|
||||
color: var(--paper-green-400);
|
||||
}
|
||||
ha-svg-icon.stopped {
|
||||
color: var(--google-red-300);
|
||||
}
|
||||
ha-call-api-button {
|
||||
font-weight: 500;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
protection-enable mwc-button {
|
||||
--mdc-theme-primary: white;
|
||||
}
|
||||
.description a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.red {
|
||||
--ha-label-badge-color: var(--label-badge-red, #df4c1e);
|
||||
}
|
||||
.blue {
|
||||
--ha-label-badge-color: var(--label-badge-blue, #039be5);
|
||||
}
|
||||
.green {
|
||||
--ha-label-badge-color: var(--label-badge-green, #0da035);
|
||||
}
|
||||
.yellow {
|
||||
--ha-label-badge-color: var(--label-badge-yellow, #f4b400);
|
||||
}
|
||||
.security {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-actions {
|
||||
display: flow-root;
|
||||
}
|
||||
.security h3 {
|
||||
margin-bottom: 8px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.security ha-label-badge {
|
||||
cursor: pointer;
|
||||
margin-right: 4px;
|
||||
--ha-label-badge-padding: 8px 0 0 0;
|
||||
}
|
||||
.changelog {
|
||||
display: contents;
|
||||
}
|
||||
.changelog-link {
|
||||
color: var(--primary-color);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-markdown {
|
||||
padding: 16px;
|
||||
}
|
||||
ha-settings-row {
|
||||
padding: 0;
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
}
|
||||
ha-settings-row > span[slot="description"] {
|
||||
white-space: normal;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-settings-row[three-line] {
|
||||
height: 74px;
|
||||
}
|
||||
|
||||
.addon-options {
|
||||
max-width: 50%;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.addon-options {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/buttons/ha-call-api-button";
|
||||
import "../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
||||
@@ -21,6 +21,11 @@ import {
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
import {
|
||||
showConfirmationDialog,
|
||||
showAlertDialog,
|
||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import { HassioResponse } from "../../../src/data/hassio/common";
|
||||
|
||||
@customElement("hassio-update")
|
||||
export class HassioUpdate extends LitElement {
|
||||
@@ -126,31 +131,43 @@ export class HassioUpdate extends LitElement {
|
||||
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
||||
<mwc-button>Release notes</mwc-button>
|
||||
</a>
|
||||
<ha-call-api-button
|
||||
.hass=${this.hass}
|
||||
.path=${apiPath}
|
||||
@hass-api-called=${this._apiCalled}
|
||||
<ha-progress-button
|
||||
.apiPath=${apiPath}
|
||||
.name=${name}
|
||||
.version=${lastVersion}
|
||||
@click=${this._confirmUpdate}
|
||||
>
|
||||
Update
|
||||
</ha-call-api-button>
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _apiCalled(ev): void {
|
||||
if (ev.detail.success) {
|
||||
this._error = "";
|
||||
private async _confirmUpdate(ev): Promise<void> {
|
||||
const item = ev.target;
|
||||
item.progress = true;
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: `Update ${item.name}`,
|
||||
text: `Are you sure you want to upgrade ${item.name} to version ${item.version}?`,
|
||||
confirmText: "update",
|
||||
dismissText: "cancel",
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
item.progress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const response = ev.detail.response;
|
||||
|
||||
if (typeof response.body === "object") {
|
||||
this._error = response.body.message || "Unknown error";
|
||||
} else {
|
||||
this._error = response.body;
|
||||
try {
|
||||
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Update failed",
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
item.progress = false;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-icon-button";
|
||||
import "@material/mwc-tab-bar";
|
||||
import "@material/mwc-tab";
|
||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { cache } from "lit-html/directives/cache";
|
||||
|
||||
import {
|
||||
updateNetworkInterface,
|
||||
NetworkInterface,
|
||||
} from "../../../../src/data/hassio/network";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { HassioNetworkDialogParams } from "./show-dialog-network";
|
||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import type { HaRadio } from "../../../../src/components/ha-radio";
|
||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-header-bar";
|
||||
import "../../../../src/components/ha-radio";
|
||||
import "../../../../src/components/ha-related-items";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
|
||||
@customElement("dialog-hassio-network")
|
||||
export class DialogHassioNetwork extends LitElement implements HassDialog {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _prosessing = false;
|
||||
|
||||
@internalProperty() private _params?: HassioNetworkDialogParams;
|
||||
|
||||
@internalProperty() private _network!: {
|
||||
interface: string;
|
||||
data: NetworkInterface;
|
||||
}[];
|
||||
|
||||
@internalProperty() private _curTabIndex = 0;
|
||||
|
||||
@internalProperty() private _device?: {
|
||||
interface: string;
|
||||
data: NetworkInterface;
|
||||
};
|
||||
|
||||
@internalProperty() private _dirty = false;
|
||||
|
||||
public async showDialog(params: HassioNetworkDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
this._dirty = false;
|
||||
this._curTabIndex = 0;
|
||||
this._network = Object.keys(params.network?.interfaces)
|
||||
.map((device) => ({
|
||||
interface: device,
|
||||
data: params.network.interfaces[device],
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
return a.data.primary > b.data.primary ? -1 : 1;
|
||||
});
|
||||
this._device = this._network[this._curTabIndex];
|
||||
this._device.data.nameservers = String(this._device.data.nameservers);
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._params = undefined;
|
||||
this._prosessing = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params || !this._network) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog open .heading=${true} hideActions @closed=${this.closeDialog}>
|
||||
<div slot="heading">
|
||||
<ha-header-bar>
|
||||
<span slot="title">
|
||||
Network settings
|
||||
</span>
|
||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</ha-header-bar>
|
||||
${this._network.length > 1
|
||||
? html` <mwc-tab-bar
|
||||
.activeIndex=${this._curTabIndex}
|
||||
@MDCTabBar:activated=${this._handleTabActivated}
|
||||
>${this._network.map(
|
||||
(device) =>
|
||||
html`<mwc-tab
|
||||
.id=${device.interface}
|
||||
.label=${device.interface}
|
||||
>
|
||||
</mwc-tab>`
|
||||
)}
|
||||
</mwc-tab-bar>`
|
||||
: ""}
|
||||
</div>
|
||||
${cache(this._renderTab())}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderTab() {
|
||||
return html` <div class="form container">
|
||||
<ha-formfield label="DHCP">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
value="dhcp"
|
||||
name="method"
|
||||
?checked=${this._device!.data.method === "dhcp"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Static">
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
value="static"
|
||||
name="method"
|
||||
?checked=${this._device!.data.method === "static"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
${this._device!.data.method !== "dhcp"
|
||||
? html` <paper-input
|
||||
class="flex-auto"
|
||||
id="ip_address"
|
||||
label="IP address/Netmask"
|
||||
.value="${this._device!.data.ip_address}"
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="gateway"
|
||||
label="Gateway address"
|
||||
.value="${this._device!.data.gateway}"
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
class="flex-auto"
|
||||
id="nameservers"
|
||||
label="DNS servers"
|
||||
.value="${this._device!.data.nameservers as string}"
|
||||
@value-changed=${this._handleInputValueChanged}
|
||||
></paper-input>
|
||||
NB!: If you are changing IP or gateway addresses, you might lose
|
||||
the connection.`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<mwc-button label="close" @click=${this.closeDialog}> </mwc-button>
|
||||
<mwc-button @click=${this._updateNetwork} ?disabled=${!this._dirty}>
|
||||
${this._prosessing
|
||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||
: "Update"}
|
||||
</mwc-button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private async _updateNetwork() {
|
||||
this._prosessing = true;
|
||||
let options: Partial<NetworkInterface> = {
|
||||
method: this._device!.data.method,
|
||||
};
|
||||
if (options.method !== "dhcp") {
|
||||
options = {
|
||||
...options,
|
||||
address: this._device!.data.ip_address,
|
||||
gateway: this._device!.data.gateway,
|
||||
dns: String(this._device!.data.nameservers).split(","),
|
||||
};
|
||||
}
|
||||
try {
|
||||
await updateNetworkInterface(this.hass, this._device!.interface, options);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to change network settings",
|
||||
text:
|
||||
typeof err === "object" ? err.body.message || "Unkown error" : err,
|
||||
});
|
||||
this._prosessing = false;
|
||||
return;
|
||||
}
|
||||
this._params?.loadData();
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private async _handleTabActivated(ev: CustomEvent): Promise<void> {
|
||||
if (this._dirty) {
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
text:
|
||||
"You have unsaved changes, these will get lost if you change tabs, do you want to continue?",
|
||||
confirmText: "yes",
|
||||
dismissText: "no",
|
||||
});
|
||||
if (!confirm) {
|
||||
this.requestUpdate("_device");
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._curTabIndex = ev.detail.index;
|
||||
this._device = this._network[ev.detail.index];
|
||||
this._device.data.nameservers = String(this._device.data.nameservers);
|
||||
}
|
||||
|
||||
private _handleRadioValueChanged(ev: CustomEvent): void {
|
||||
const value = (ev.target as HaRadio).value as "dhcp" | "static";
|
||||
|
||||
if (!value || !this._device || this._device!.data.method === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._dirty = true;
|
||||
|
||||
this._device!.data.method = value;
|
||||
this.requestUpdate("_device");
|
||||
}
|
||||
|
||||
private _handleInputValueChanged(ev: CustomEvent): void {
|
||||
const value: string | null | undefined = (ev.target as PaperInputElement)
|
||||
.value;
|
||||
const id = (ev.target as PaperInputElement).id;
|
||||
|
||||
if (!value || !this._device || this._device.data[id] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._dirty = true;
|
||||
|
||||
this._device.data[id] = value;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-header-bar {
|
||||
--mdc-theme-on-primary: var(--primary-text-color);
|
||||
--mdc-theme-primary: var(--mdc-theme-surface);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
mwc-tab-bar {
|
||||
border-bottom: 1px solid
|
||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
ha-dialog {
|
||||
--dialog-content-position: static;
|
||||
--dialog-content-padding: 0;
|
||||
--dialog-z-index: 6;
|
||||
}
|
||||
|
||||
@media all and (min-width: 451px) and (min-height: 501px) {
|
||||
.container {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
/* overrule the ha-style-dialog max-height on small screens */
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-header-bar {
|
||||
--mdc-theme-primary: var(--app-header-background-color);
|
||||
--mdc-theme-on-primary: var(--app-header-text-color, white);
|
||||
}
|
||||
}
|
||||
|
||||
mwc-button.warning {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
|
||||
:host([rtl]) app-toolbar {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
.container {
|
||||
padding: 20px 24px;
|
||||
}
|
||||
.form {
|
||||
margin-bottom: 53px;
|
||||
}
|
||||
.buttons {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid
|
||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
padding-bottom: max(env(safe-area-inset-bottom), 8px);
|
||||
background-color: var(--mdc-theme-surface, #fff);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-network": DialogHassioNetwork;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { NetworkInfo } from "../../../../src/data/hassio/network";
|
||||
import "./dialog-hassio-network";
|
||||
|
||||
export interface HassioNetworkDialogParams {
|
||||
network: NetworkInfo;
|
||||
loadData: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const showNetworkDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioNetworkDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-network",
|
||||
dialogImport: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dialog-hassio-network" */ "./dialog-hassio-network"
|
||||
),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
fetchHassioSnapshotInfo,
|
||||
HassioSnapshotDetail,
|
||||
} from "../../../../src/data/hassio/snapshot";
|
||||
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { PolymerChangedEvent } from "../../../../src/polymer-types";
|
||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
@@ -266,8 +267,12 @@ class HassioSnapshotDialog extends LitElement {
|
||||
this._snapshotPassword = ev.detail.value;
|
||||
}
|
||||
|
||||
private _partialRestoreClicked() {
|
||||
if (!confirm("Are you sure you want to restore this snapshot?")) {
|
||||
private async _partialRestoreClicked() {
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want partially to restore this snapshot?",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -312,8 +317,13 @@ class HassioSnapshotDialog extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _fullRestoreClicked() {
|
||||
if (!confirm("Are you sure you want to restore this snapshot?")) {
|
||||
private async _fullRestoreClicked() {
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title:
|
||||
"Are you sure you want to wipe your system and restore this snapshot?",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,8 +348,12 @@ class HassioSnapshotDialog extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _deleteClicked() {
|
||||
if (!confirm("Are you sure you want to delete this snapshot?")) {
|
||||
private async _deleteClicked() {
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want to delete this snapshot?",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import "@material/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import { safeDump } from "js-yaml";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/buttons/ha-call-api-button";
|
||||
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
|
||||
|
||||
import {
|
||||
changeHostOptions,
|
||||
configSyncOS,
|
||||
fetchHassioHostInfo,
|
||||
HassioHassOSInfo,
|
||||
HassioHostInfo as HassioHostInfoType,
|
||||
@@ -20,16 +25,26 @@ import {
|
||||
shutdownHost,
|
||||
updateOS,
|
||||
} from "../../../src/data/hassio/host";
|
||||
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
|
||||
import {
|
||||
fetchNetworkInfo,
|
||||
NetworkInfo,
|
||||
} from "../../../src/data/hassio/network";
|
||||
import { HassioInfo } from "../../../src/data/hassio/supervisor";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
|
||||
|
||||
import "../../../src/components/ha-button-menu";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-settings-row";
|
||||
|
||||
@customElement("hassio-host-info")
|
||||
class HassioHostInfo extends LitElement {
|
||||
@@ -41,86 +56,130 @@ class HassioHostInfo extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
|
||||
|
||||
@internalProperty() private _errors?: string;
|
||||
@internalProperty() public _networkInfo?: NetworkInfo;
|
||||
|
||||
public render(): TemplateResult | void {
|
||||
const primaryIpAddress = this.hostInfo.features.includes("network")
|
||||
? this._primaryIpAddress(this._networkInfo!)
|
||||
: "";
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-card header="Host System">
|
||||
<div class="card-content">
|
||||
<h2>Host system</h2>
|
||||
<table class="info">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Hostname</td>
|
||||
<td>${this.hostInfo.hostname}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>System</td>
|
||||
<td>${this.hostInfo.operating_system}</td>
|
||||
</tr>
|
||||
${!this.hostInfo.features.includes("hassos")
|
||||
? html`<tr>
|
||||
<td>Docker version</td>
|
||||
<td>${this.hassioInfo.docker}</td>
|
||||
</tr>`
|
||||
: ""}
|
||||
${this.hostInfo.deployment
|
||||
? html`
|
||||
<tr>
|
||||
<td>Deployment</td>
|
||||
<td>${this.hostInfo.deployment}</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
</tbody>
|
||||
</table>
|
||||
<mwc-button raised @click=${this._showHardware} class="info">
|
||||
Hardware
|
||||
</mwc-button>
|
||||
${this.hostInfo.features.includes("hostname")
|
||||
? html`
|
||||
? html`<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Hostname
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.hostInfo.hostname}
|
||||
</span>
|
||||
<mwc-button
|
||||
raised
|
||||
title="Change the hostname"
|
||||
label="Change"
|
||||
@click=${this._changeHostnameClicked}
|
||||
class="info"
|
||||
>
|
||||
Change hostname
|
||||
</mwc-button>
|
||||
`
|
||||
</ha-settings-row>`
|
||||
: ""}
|
||||
${this._errors
|
||||
? html` <div class="errors">Error: ${this._errors}</div> `
|
||||
${this.hostInfo.features.includes("network")
|
||||
? html` <ha-settings-row>
|
||||
<span slot="heading">
|
||||
IP address
|
||||
</span>
|
||||
<span slot="description">
|
||||
${primaryIpAddress}
|
||||
</span>
|
||||
<mwc-button
|
||||
title="Change the network"
|
||||
label="Change"
|
||||
@click=${this._changeNetworkClicked}
|
||||
>
|
||||
</mwc-button>
|
||||
</ha-settings-row>`
|
||||
: ""}
|
||||
|
||||
<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Operating system
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.hostInfo.operating_system}
|
||||
</span>
|
||||
${this.hostInfo.version !== this.hostInfo.version_latest &&
|
||||
this.hostInfo.features.includes("hassos")
|
||||
? html`
|
||||
<mwc-button
|
||||
title="Update the host OS"
|
||||
label="Update"
|
||||
@click=${this._osUpdate}
|
||||
>
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</ha-settings-row>
|
||||
${!this.hostInfo.features.includes("hassos")
|
||||
? html`<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Docker version
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.hassioInfo.docker}
|
||||
</span>
|
||||
</ha-settings-row>`
|
||||
: ""}
|
||||
${this.hostInfo.deployment
|
||||
? html`<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Deployment
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.hostInfo.deployment}
|
||||
</span>
|
||||
</ha-settings-row>`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
${this.hostInfo.features.includes("reboot")
|
||||
? html`
|
||||
<mwc-button class="warning" @click=${this._rebootHost}
|
||||
>Reboot</mwc-button
|
||||
<mwc-button
|
||||
title="Reboot the host OS"
|
||||
label="Reboot"
|
||||
class="warning"
|
||||
@click=${this._hostReboot}
|
||||
>
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
${this.hostInfo.features.includes("shutdown")
|
||||
? html`
|
||||
<mwc-button class="warning" @click=${this._shutdownHost}
|
||||
>Shutdown</mwc-button
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
${this.hostInfo.features.includes("hassos")
|
||||
? html`
|
||||
<ha-call-api-button
|
||||
<mwc-button
|
||||
title="Shutdown the host OS"
|
||||
label="Shutdown"
|
||||
class="warning"
|
||||
.hass=${this.hass}
|
||||
path="hassio/os/config/sync"
|
||||
title="Load HassOS configs or updates from USB"
|
||||
>Import from USB</ha-call-api-button
|
||||
@click=${this._hostShutdown}
|
||||
>
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
${this.hostInfo.version !== this.hostInfo.version_latest
|
||||
? html` <mwc-button @click=${this._updateOS}>Update</mwc-button> `
|
||||
: ""}
|
||||
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
@action=${this._handleMenuAction}
|
||||
>
|
||||
<mwc-icon-button slot="trigger">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item title="Show a list of hardware">
|
||||
Hardware
|
||||
</mwc-list-item>
|
||||
${this.hostInfo.features.includes("hassos")
|
||||
? html`<mwc-list-item
|
||||
title="Load HassOS configs or updates from USB"
|
||||
>
|
||||
Import from USB
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
@@ -133,72 +192,96 @@ class HassioHostInfo extends LitElement {
|
||||
css`
|
||||
ha-card {
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
.card-actions {
|
||||
height: 48px;
|
||||
border-top: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
ha-settings-row {
|
||||
padding: 0;
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
}
|
||||
.card-content {
|
||||
color: var(--primary-text-color);
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - 47px);
|
||||
ha-settings-row[three-line] {
|
||||
height: 74px;
|
||||
}
|
||||
.info {
|
||||
width: 100%;
|
||||
}
|
||||
.info td:nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
mwc-button.info {
|
||||
max-width: calc(50% - 12px);
|
||||
}
|
||||
table.info {
|
||||
margin-bottom: 10px;
|
||||
ha-settings-row > span[slot="description"] {
|
||||
white-space: normal;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.warning {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
|
||||
ha-button-menu {
|
||||
color: var(--secondary-text-color);
|
||||
--mdc-menu-min-width: 200px;
|
||||
}
|
||||
@media (min-width: 563px) {
|
||||
paper-listbox {
|
||||
max-height: 150px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
min-height: 35px;
|
||||
}
|
||||
mwc-list-item ha-svg-icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
private _apiCalled(ev): void {
|
||||
if (ev.detail.success) {
|
||||
this._errors = undefined;
|
||||
return;
|
||||
private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => {
|
||||
if (!network_info) {
|
||||
return "";
|
||||
}
|
||||
return Object.keys(network_info?.interfaces)
|
||||
.map((device) => network_info.interfaces[device])
|
||||
.find((device) => device.primary)?.ip_address;
|
||||
});
|
||||
|
||||
const response = ev.detail.response;
|
||||
|
||||
this._errors =
|
||||
typeof response.body === "object"
|
||||
? response.body.message || "Unknown error"
|
||||
: response.body;
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
await this._showHardware();
|
||||
break;
|
||||
case 1:
|
||||
await this._importFromUSB();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async _showHardware(): Promise<void> {
|
||||
try {
|
||||
const content = this._objectToMarkdown(
|
||||
await fetchHassioHardwareInfo(this.hass)
|
||||
);
|
||||
const content = await fetchHassioHardwareInfo(this.hass);
|
||||
showHassioMarkdownDialog(this, {
|
||||
title: "Hardware",
|
||||
content,
|
||||
content: `<pre>${safeDump(content, { indent: 2 })}</pre>`,
|
||||
});
|
||||
} catch (err) {
|
||||
showHassioMarkdownDialog(this, {
|
||||
title: "Hardware",
|
||||
content: "Error getting hardware info",
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to get Hardware list",
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _rebootHost(): Promise<void> {
|
||||
private async _hostReboot(): Promise<void> {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: "Reboot",
|
||||
text: "Are you sure you want to reboot the host?",
|
||||
@@ -215,12 +298,13 @@ class HassioHostInfo extends LitElement {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to reboot",
|
||||
text: err.body.message,
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _shutdownHost(): Promise<void> {
|
||||
private async _hostShutdown(): Promise<void> {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: "Shutdown",
|
||||
text: "Are you sure you want to shutdown the host?",
|
||||
@@ -237,12 +321,13 @@ class HassioHostInfo extends LitElement {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to shutdown",
|
||||
text: err.body.message,
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateOS(): Promise<void> {
|
||||
private async _osUpdate(): Promise<void> {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: "Update",
|
||||
text: "Are you sure you want to update the OS?",
|
||||
@@ -259,30 +344,17 @@ class HassioHostInfo extends LitElement {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to update",
|
||||
text: err.body.message,
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _objectToMarkdown(obj, indent = ""): string {
|
||||
let data = "";
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (typeof obj[key] !== "object") {
|
||||
data += `${indent}- ${key}: ${obj[key]}\n`;
|
||||
} else {
|
||||
data += `${indent}- ${key}:\n`;
|
||||
if (Array.isArray(obj[key])) {
|
||||
if (obj[key].length) {
|
||||
data +=
|
||||
`${indent} - ` + obj[key].join(`\n${indent} - `) + "\n";
|
||||
}
|
||||
} else {
|
||||
data += this._objectToMarkdown(obj[key], ` ${indent}`);
|
||||
}
|
||||
}
|
||||
private async _changeNetworkClicked(): Promise<void> {
|
||||
showNetworkDialog(this, {
|
||||
network: this._networkInfo!,
|
||||
loadData: () => this._loadData(),
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private async _changeHostnameClicked(): Promise<void> {
|
||||
@@ -301,11 +373,29 @@ class HassioHostInfo extends LitElement {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Setting hostname failed",
|
||||
text: err.body.message,
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _importFromUSB(): Promise<void> {
|
||||
try {
|
||||
await configSyncOS(this.hass);
|
||||
this.hostInfo = await fetchHassioHostInfo(this.hass);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to import from USB",
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadData(): Promise<void> {
|
||||
this._networkInfo = await fetchNetworkInfo(this.hass);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -6,27 +6,28 @@ import {
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import "../../../src/components/buttons/ha-call-api-button";
|
||||
import "../../../src/components/ha-card";
|
||||
|
||||
import { HassioHostInfo as HassioHostInfoType } from "../../../src/data/hassio/host";
|
||||
import {
|
||||
HassioSupervisorInfo as HassioSupervisorInfoType,
|
||||
setSupervisorOption,
|
||||
SupervisorOptions,
|
||||
} from "../../../src/data/hassio/supervisor";
|
||||
import "../../../src/components/ha-switch";
|
||||
import {
|
||||
showConfirmationDialog,
|
||||
showAlertDialog,
|
||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import "../../../src/components/ha-settings-row";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
import {
|
||||
HassioSupervisorInfo as HassioSupervisorInfoType,
|
||||
reloadSupervisor,
|
||||
setSupervisorOption,
|
||||
SupervisorOptions,
|
||||
updateSupervisor,
|
||||
} from "../../../src/data/hassio/supervisor";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-settings-row";
|
||||
import "../../../src/components/ha-switch";
|
||||
|
||||
@customElement("hassio-supervisor-info")
|
||||
class HassioSupervisorInfo extends LitElement {
|
||||
@@ -36,104 +37,108 @@ class HassioSupervisorInfo extends LitElement {
|
||||
|
||||
@property() public hostInfo!: HassioHostInfoType;
|
||||
|
||||
@internalProperty() private _errors?: string;
|
||||
|
||||
public render(): TemplateResult | void {
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-card header="Supervisor">
|
||||
<div class="card-content">
|
||||
<h2>Supervisor</h2>
|
||||
<table class="info">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>${this.supervisorInfo.version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Latest version</td>
|
||||
<td>${this.supervisorInfo.version_latest}</td>
|
||||
</tr>
|
||||
${this.supervisorInfo.channel !== "stable"
|
||||
? html`
|
||||
<tr>
|
||||
<td>Channel</td>
|
||||
<td>${this.supervisorInfo.channel}</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="options">
|
||||
${this.supervisorInfo?.supported
|
||||
? html` <ha-settings-row>
|
||||
<span slot="heading">
|
||||
Share Diagnostics
|
||||
</span>
|
||||
<div slot="description" class="diagnostics-description">
|
||||
Share crash reports and diagnostic information.
|
||||
<button
|
||||
class="link"
|
||||
@click=${this._diagnosticsInformationDialog}
|
||||
>
|
||||
Learn more
|
||||
</button>
|
||||
</div>
|
||||
<ha-switch
|
||||
.checked=${this.supervisorInfo.diagnostics}
|
||||
@change=${this._toggleDiagnostics}
|
||||
></ha-switch>
|
||||
</ha-settings-row>`
|
||||
: html`<div class="error">
|
||||
You are running an unsupported installation.
|
||||
<a
|
||||
href="https://github.com/home-assistant/architecture/blob/master/adr/${this.hostInfo.features.includes(
|
||||
"hassos"
|
||||
)
|
||||
? "0015-home-assistant-os.md"
|
||||
: "0014-home-assistant-supervised.md"}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Learn More</a
|
||||
<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Version
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.supervisorInfo.version}
|
||||
</span>
|
||||
</ha-settings-row>
|
||||
<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Newest version
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.supervisorInfo.version_latest}
|
||||
</span>
|
||||
${this.supervisorInfo.version !== this.supervisorInfo.version_latest
|
||||
? html`
|
||||
<mwc-button
|
||||
title="Update the supervisor"
|
||||
label="Update"
|
||||
@click=${this._supervisorUpdate}
|
||||
>
|
||||
</div>`}
|
||||
</div>
|
||||
${this._errors
|
||||
? html` <div class="error">Error: ${this._errors}</div> `
|
||||
: ""}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</ha-settings-row>
|
||||
<ha-settings-row>
|
||||
<span slot="heading">
|
||||
Channel
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this.supervisorInfo.channel}
|
||||
</span>
|
||||
${this.supervisorInfo.channel === "beta"
|
||||
? html`
|
||||
<mwc-button
|
||||
@click=${this._toggleBeta}
|
||||
label="Leave beta channel"
|
||||
title="Get stable updates for Home Assistant, supervisor and host"
|
||||
>
|
||||
</mwc-button>
|
||||
`
|
||||
: this.supervisorInfo.channel === "stable"
|
||||
? html`
|
||||
<mwc-button
|
||||
@click=${this._toggleBeta}
|
||||
label="Join beta channel"
|
||||
title="Get beta updates for Home Assistant (RCs), supervisor and host"
|
||||
>
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</ha-settings-row>
|
||||
|
||||
${this.supervisorInfo?.supported
|
||||
? html` <ha-settings-row three-line>
|
||||
<span slot="heading">
|
||||
Share diagnostics
|
||||
</span>
|
||||
<div slot="description" class="diagnostics-description">
|
||||
Share crash reports and diagnostic information.
|
||||
<button
|
||||
class="link"
|
||||
title="Show more information about this"
|
||||
@click=${this._diagnosticsInformationDialog}
|
||||
>
|
||||
Learn more
|
||||
</button>
|
||||
</div>
|
||||
<ha-switch
|
||||
haptic
|
||||
.checked=${this.supervisorInfo.diagnostics}
|
||||
@change=${this._toggleDiagnostics}
|
||||
></ha-switch>
|
||||
</ha-settings-row>`
|
||||
: html`<div class="error">
|
||||
You are running an unsupported installation.
|
||||
<a
|
||||
href="https://github.com/home-assistant/architecture/blob/master/adr/${this.hostInfo.features.includes(
|
||||
"hassos"
|
||||
)
|
||||
? "0015-home-assistant-os.md"
|
||||
: "0014-home-assistant-supervised.md"}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title="Learn more about how you can make your system compliant"
|
||||
>
|
||||
Learn More
|
||||
</a>
|
||||
</div>`}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-api-button .hass=${this.hass} path="hassio/supervisor/reload"
|
||||
>Reload</ha-call-api-button
|
||||
<mwc-button
|
||||
@click=${this._supervisorReload}
|
||||
title="Reload parts of the supervisor."
|
||||
label="Reload"
|
||||
>
|
||||
${this.supervisorInfo.version !== this.supervisorInfo.version_latest
|
||||
? html`
|
||||
<ha-call-api-button
|
||||
.hass=${this.hass}
|
||||
path="hassio/supervisor/update"
|
||||
>Update</ha-call-api-button
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
${this.supervisorInfo.channel === "beta"
|
||||
? html`
|
||||
<ha-call-api-button
|
||||
.hass=${this.hass}
|
||||
path="hassio/supervisor/options"
|
||||
.data=${{ channel: "stable" }}
|
||||
>Leave beta channel</ha-call-api-button
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
${this.supervisorInfo.channel === "stable"
|
||||
? html`
|
||||
<mwc-button
|
||||
@click=${this._joinBeta}
|
||||
class="warning"
|
||||
title="Get beta updates for Home Assistant (RCs), supervisor and host"
|
||||
>Join beta channel</mwc-button
|
||||
>
|
||||
`
|
||||
: ""}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
@@ -146,93 +151,103 @@ class HassioSupervisorInfo extends LitElement {
|
||||
css`
|
||||
ha-card {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
.card-content {
|
||||
color: var(--primary-text-color);
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - 47px);
|
||||
}
|
||||
.info,
|
||||
.options {
|
||||
width: 100%;
|
||||
}
|
||||
.info td:nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
ha-settings-row {
|
||||
padding: 0;
|
||||
.card-actions {
|
||||
height: 48px;
|
||||
border-top: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
button.link {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.diagnostics-description {
|
||||
white-space: normal;
|
||||
ha-settings-row {
|
||||
padding: 0;
|
||||
height: 54px;
|
||||
width: 100%;
|
||||
}
|
||||
ha-settings-row[three-line] {
|
||||
height: 74px;
|
||||
}
|
||||
ha-settings-row > span[slot="description"] {
|
||||
white-space: normal;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
||||
}
|
||||
private async _toggleBeta(): Promise<void> {
|
||||
if (this.supervisorInfo.channel === "stable") {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: "WARNING",
|
||||
text: html` Beta releases are for testers and early adopters and can
|
||||
contain unstable code changes.
|
||||
<br />
|
||||
<b>
|
||||
Make sure you have backups of your data before you activate this
|
||||
feature.
|
||||
</b>
|
||||
<br /><br />
|
||||
This includes beta releases for:
|
||||
<li>Home Assistant Core</li>
|
||||
<li>Home Assistant Supervisor</li>
|
||||
<li>Home Assistant Operating System</li>
|
||||
<br />
|
||||
Do you want to join the beta channel?`,
|
||||
confirmText: "join beta",
|
||||
dismissText: "no",
|
||||
});
|
||||
|
||||
private _apiCalled(ev): void {
|
||||
if (ev.detail.success) {
|
||||
this._errors = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const response = ev.detail.response;
|
||||
|
||||
this._errors =
|
||||
typeof response.body === "object"
|
||||
? response.body.message || "Unknown error"
|
||||
: response.body;
|
||||
}
|
||||
|
||||
private async _joinBeta() {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
title: "WARNING",
|
||||
text: html` Beta releases are for testers and early adopters and can
|
||||
contain unstable code changes.
|
||||
<br />
|
||||
<b>
|
||||
Make sure you have backups of your data before you activate this
|
||||
feature.
|
||||
</b>
|
||||
<br /><br />
|
||||
This includes beta releases for:
|
||||
<li>Home Assistant Core</li>
|
||||
<li>Home Assistant Supervisor</li>
|
||||
<li>Home Assistant Operating System</li>
|
||||
<br />
|
||||
Do you want to join the beta channel?`,
|
||||
confirmText: "join beta",
|
||||
dismissText: "no",
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const data: SupervisorOptions = { channel: "beta" };
|
||||
await setSupervisorOption(this.hass, data);
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "option",
|
||||
const data: Partial<SupervisorOptions> = {
|
||||
channel: this.supervisorInfo.channel !== "stable" ? "beta" : "stable",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
await setSupervisorOption(this.hass, data);
|
||||
await reloadSupervisor(this.hass);
|
||||
} catch (err) {
|
||||
this._errors = `Error joining beta channel, ${err.body?.message || err}`;
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to set supervisor option",
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _diagnosticsInformationDialog() {
|
||||
private async _supervisorReload(): Promise<void> {
|
||||
try {
|
||||
await reloadSupervisor(this.hass);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to reload the supervisor",
|
||||
text:
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _supervisorUpdate(): Promise<void> {
|
||||
try {
|
||||
await updateSupervisor(this.hass);
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to update the supervisor",
|
||||
text:
|
||||
typeof err === "object" ? err.body.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _diagnosticsInformationDialog(): Promise<void> {
|
||||
await showAlertDialog(this, {
|
||||
title: "Help Improve Home Assistant",
|
||||
text: html`Would you want to automatically share crash reports and
|
||||
@@ -247,22 +262,18 @@ class HassioSupervisorInfo extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private async _toggleDiagnostics() {
|
||||
private async _toggleDiagnostics(): Promise<void> {
|
||||
try {
|
||||
const data: SupervisorOptions = {
|
||||
diagnostics: !this.supervisorInfo?.diagnostics,
|
||||
};
|
||||
await setSupervisorOption(this.hass, data);
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
path: "option",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err) {
|
||||
this._errors = `Error changing supervisor setting, ${
|
||||
err.body?.message || err
|
||||
}`;
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to set supervisor option",
|
||||
text:
|
||||
typeof err === "object" ? err.body.message || "Unkown error" : err,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,20 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../src/components/ha-card";
|
||||
|
||||
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
|
||||
import "../../../src/layouts/hass-loading-screen";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/layouts/hass-loading-screen";
|
||||
import "../components/hassio-ansi-to-html";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
|
||||
interface LogProvider {
|
||||
key: string;
|
||||
@@ -102,7 +104,7 @@ class HassioSupervisorLog extends LitElement {
|
||||
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
||||
<mwc-button @click=${this._loadData}>Refresh</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
@@ -114,6 +116,7 @@ class HassioSupervisorLog extends LitElement {
|
||||
hassioStyle,
|
||||
css`
|
||||
ha-card {
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
pre {
|
||||
@@ -127,9 +130,6 @@ class HassioSupervisorLog extends LitElement {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-content {
|
||||
padding-top: 0px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@@ -142,7 +142,6 @@ class HassioSupervisorLog extends LitElement {
|
||||
|
||||
private async _loadData(): Promise<void> {
|
||||
this._error = undefined;
|
||||
this._content = undefined;
|
||||
|
||||
try {
|
||||
this._content = await fetchHassioLogs(
|
||||
@@ -151,14 +150,10 @@ class HassioSupervisorLog extends LitElement {
|
||||
);
|
||||
} catch (err) {
|
||||
this._error = `Failed to get supervisor logs, ${
|
||||
err.body?.message || err
|
||||
typeof err === "object" ? err.body?.message || "Unkown error" : err
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
private async _refresh(): Promise<void> {
|
||||
await this._loadData();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -52,7 +52,6 @@ class HassioSystem extends LitElement {
|
||||
>
|
||||
<span slot="header">System</span>
|
||||
<div class="content">
|
||||
<h1>Information</h1>
|
||||
<div class="card-group">
|
||||
<hassio-supervisor-info
|
||||
.hass=${this.hass}
|
||||
@@ -66,7 +65,6 @@ class HassioSystem extends LitElement {
|
||||
.hassOsInfo=${this.hassOsInfo}
|
||||
></hassio-host-info>
|
||||
</div>
|
||||
<h1>System log</h1>
|
||||
<hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log>
|
||||
</div>
|
||||
</hass-tabs-subpage>
|
||||
|
||||
+11
-8
@@ -23,8 +23,11 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-pluralrules": "^1.5.8",
|
||||
"@fullcalendar/core": "^5.0.0-beta.2",
|
||||
"@fullcalendar/daygrid": "^5.0.0-beta.2",
|
||||
"@fullcalendar/common": "5.1.0",
|
||||
"@fullcalendar/core": "5.1.0",
|
||||
"@fullcalendar/daygrid": "5.1.0",
|
||||
"@fullcalendar/interaction": "5.1.0",
|
||||
"@fullcalendar/list": "5.1.0",
|
||||
"@material/chips": "=8.0.0-canary.096a7a066.0",
|
||||
"@material/circular-progress": "=8.0.0-canary.a78ceb112.0",
|
||||
"@material/mwc-button": "^0.18.0",
|
||||
@@ -41,8 +44,8 @@
|
||||
"@material/mwc-tab": "^0.18.0",
|
||||
"@material/mwc-tab-bar": "^0.18.0",
|
||||
"@material/top-app-bar": "=8.0.0-canary.096a7a066.0",
|
||||
"@mdi/js": "5.4.55",
|
||||
"@mdi/svg": "5.4.55",
|
||||
"@mdi/js": "5.5.55",
|
||||
"@mdi/svg": "5.5.55",
|
||||
"@polymer/app-layout": "^3.0.2",
|
||||
"@polymer/app-route": "^3.0.2",
|
||||
"@polymer/app-storage": "^3.0.2",
|
||||
@@ -146,7 +149,7 @@
|
||||
"@types/leaflet-draw": "^1.0.1",
|
||||
"@types/marked": "^1.1.0",
|
||||
"@types/memoize-one": "4.1.0",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/resize-observer-browser": "^0.1.3",
|
||||
"@types/webspeechapi": "^0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "^2.28.0",
|
||||
@@ -157,7 +160,7 @@
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb-typescript": "^7.2.1",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-import-resolver-webpack": "^0.12.1",
|
||||
"eslint-import-resolver-webpack": "^0.12.2",
|
||||
"eslint-plugin-disable": "^2.0.1",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-lit": "^1.2.0",
|
||||
@@ -180,7 +183,7 @@
|
||||
"magic-string": "^0.25.7",
|
||||
"map-stream": "^0.0.7",
|
||||
"merge-stream": "^1.0.1",
|
||||
"mocha": "^6.0.2",
|
||||
"mocha": "^7.2.0",
|
||||
"object-hash": "^2.0.3",
|
||||
"open": "^7.0.4",
|
||||
"prettier": "^2.0.4",
|
||||
@@ -197,7 +200,7 @@
|
||||
"systemjs": "^6.3.2",
|
||||
"terser-webpack-plugin": "^3.0.6",
|
||||
"ts-lit-plugin": "^1.2.0",
|
||||
"ts-mocha": "^6.0.0",
|
||||
"ts-mocha": "^7.0.0",
|
||||
"typescript": "^3.8.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20200820.0",
|
||||
version="20200824.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
/** Return if a service is loaded. */
|
||||
export const isServiceLoaded = (
|
||||
hass: HomeAssistant,
|
||||
domain: string,
|
||||
service: string
|
||||
): boolean =>
|
||||
hass && domain in hass.services && service in hass.services[domain];
|
||||
@@ -21,6 +21,11 @@ export default function relativeTime(
|
||||
const tense = delta >= 0 ? "past" : "future";
|
||||
delta = Math.abs(delta);
|
||||
let roundedDelta = Math.round(delta);
|
||||
|
||||
if (roundedDelta === 0) {
|
||||
return localize("ui.components.relative_time.just_now");
|
||||
}
|
||||
|
||||
let unit = "week";
|
||||
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
|
||||
@@ -5,12 +5,16 @@ import { domainIcon } from "./domain_icon";
|
||||
import { batteryIcon } from "./battery_icon";
|
||||
|
||||
const fixedDeviceClassIcons = {
|
||||
current: "hass:current-ac",
|
||||
energy: "hass:flash",
|
||||
humidity: "hass:water-percent",
|
||||
illuminance: "hass:brightness-5",
|
||||
temperature: "hass:thermometer",
|
||||
pressure: "hass:gauge",
|
||||
power: "hass:flash",
|
||||
power_factor: "hass:angle-acute",
|
||||
signal_strength: "hass:wifi",
|
||||
voltage: "hass:sine-wave",
|
||||
};
|
||||
|
||||
export const sensorIcon = (state: HassEntity) => {
|
||||
|
||||
@@ -3,19 +3,21 @@ import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
eventOptions,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
eventOptions,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { styleMap } from "lit-html/directives/style-map";
|
||||
import { scroll } from "lit-virtualizer";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../common/search/search-input";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
@@ -24,8 +26,6 @@ import "../ha-checkbox";
|
||||
import type { HaCheckbox } from "../ha-checkbox";
|
||||
import "../ha-icon";
|
||||
import { filterData, sortData } from "./sort-filter";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@@ -70,6 +70,7 @@ export interface DataTableColumnData extends DataTableSortColumnData {
|
||||
maxWidth?: string;
|
||||
grows?: boolean;
|
||||
forceLTR?: boolean;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export interface DataTableRowData {
|
||||
@@ -214,13 +215,15 @@ export class HaDataTable extends LitElement {
|
||||
class="mdc-data-table__table ${classMap({
|
||||
"auto-height": this.autoHeight,
|
||||
})}"
|
||||
role="table"
|
||||
aria-rowcount=${this._filteredData.length}
|
||||
style=${styleMap({
|
||||
height: this.autoHeight
|
||||
? `${(this._filteredData.length || 1) * 53 + 57}px`
|
||||
: `calc(100% - ${this._header?.clientHeight}px)`,
|
||||
})}
|
||||
>
|
||||
<div class="mdc-data-table__header-row">
|
||||
<div class="mdc-data-table__header-row" role="row">
|
||||
${this.selectable
|
||||
? html`
|
||||
<div
|
||||
@@ -240,8 +243,10 @@ export class HaDataTable extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${Object.entries(this.columns).map((columnEntry) => {
|
||||
const [key, column] = columnEntry;
|
||||
${Object.entries(this.columns).map(([key, column]) => {
|
||||
if (column.hidden) {
|
||||
return "";
|
||||
}
|
||||
const sorted = key === this._sortColumn;
|
||||
const classes = {
|
||||
"mdc-data-table__header-cell--numeric": Boolean(
|
||||
@@ -288,8 +293,8 @@ export class HaDataTable extends LitElement {
|
||||
${!this._filteredData.length
|
||||
? html`
|
||||
<div class="mdc-data-table__content">
|
||||
<div class="mdc-data-table__row">
|
||||
<div class="mdc-data-table__cell grows center">
|
||||
<div class="mdc-data-table__row" role="row">
|
||||
<div class="mdc-data-table__cell grows center" role="cell">
|
||||
${this.noDataText || "No data"}
|
||||
</div>
|
||||
</div>
|
||||
@@ -304,12 +309,14 @@ export class HaDataTable extends LitElement {
|
||||
items: !this.hasFab
|
||||
? this._filteredData
|
||||
: [...this._filteredData, ...[{ empty: true }]],
|
||||
renderItem: (row: DataTableRowData) => {
|
||||
renderItem: (row: DataTableRowData, index) => {
|
||||
if (row.empty) {
|
||||
return html` <div class="mdc-data-table__row"></div> `;
|
||||
}
|
||||
return html`
|
||||
<div
|
||||
aria-rowindex=${index}
|
||||
role="row"
|
||||
.rowId="${row[this.id]}"
|
||||
@click=${this._handleRowClick}
|
||||
class="mdc-data-table__row ${classMap({
|
||||
@@ -328,6 +335,7 @@ export class HaDataTable extends LitElement {
|
||||
? html`
|
||||
<div
|
||||
class="mdc-data-table__cell mdc-data-table__cell--checkbox"
|
||||
role="cell"
|
||||
>
|
||||
<ha-checkbox
|
||||
class="mdc-data-table__row-checkbox"
|
||||
@@ -341,40 +349,45 @@ export class HaDataTable extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${Object.entries(this.columns).map((columnEntry) => {
|
||||
const [key, column] = columnEntry;
|
||||
return html`
|
||||
<div
|
||||
class="mdc-data-table__cell ${classMap({
|
||||
"mdc-data-table__cell--numeric": Boolean(
|
||||
column.type === "numeric"
|
||||
),
|
||||
"mdc-data-table__cell--icon": Boolean(
|
||||
column.type === "icon"
|
||||
),
|
||||
"mdc-data-table__cell--icon-button": Boolean(
|
||||
column.type === "icon-button"
|
||||
),
|
||||
grows: Boolean(column.grows),
|
||||
forceLTR: Boolean(column.forceLTR),
|
||||
})}"
|
||||
style=${column.width
|
||||
? styleMap({
|
||||
[column.grows
|
||||
? "minWidth"
|
||||
: "width"]: column.width,
|
||||
maxWidth: column.maxWidth
|
||||
? column.maxWidth
|
||||
: "",
|
||||
})
|
||||
: ""}
|
||||
>
|
||||
${column.template
|
||||
? column.template(row[key], row)
|
||||
: row[key]}
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
${Object.entries(this.columns).map(
|
||||
([key, column]) => {
|
||||
if (column.hidden) {
|
||||
return "";
|
||||
}
|
||||
return html`
|
||||
<div
|
||||
role="cell"
|
||||
class="mdc-data-table__cell ${classMap({
|
||||
"mdc-data-table__cell--numeric": Boolean(
|
||||
column.type === "numeric"
|
||||
),
|
||||
"mdc-data-table__cell--icon": Boolean(
|
||||
column.type === "icon"
|
||||
),
|
||||
"mdc-data-table__cell--icon-button": Boolean(
|
||||
column.type === "icon-button"
|
||||
),
|
||||
grows: Boolean(column.grows),
|
||||
forceLTR: Boolean(column.forceLTR),
|
||||
})}"
|
||||
style=${column.width
|
||||
? styleMap({
|
||||
[column.grows
|
||||
? "minWidth"
|
||||
: "width"]: column.width,
|
||||
maxWidth: column.maxWidth
|
||||
? column.maxWidth
|
||||
: "",
|
||||
})
|
||||
: ""}
|
||||
>
|
||||
${column.template
|
||||
? column.template(row[key], row)
|
||||
: row[key]}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// To use comlink under ES5
|
||||
import "proxy-polyfill";
|
||||
import { expose } from "comlink";
|
||||
import "proxy-polyfill";
|
||||
import type {
|
||||
DataTableSortColumnData,
|
||||
DataTableRowData,
|
||||
SortingDirection,
|
||||
DataTableSortColumnData,
|
||||
SortableColumnContainer,
|
||||
SortingDirection,
|
||||
} from "./ha-data-table";
|
||||
|
||||
const filterData = (
|
||||
@@ -19,7 +19,7 @@ const filterData = (
|
||||
const [key, column] = columnEntry;
|
||||
if (column.filterable) {
|
||||
if (
|
||||
(column.filterKey ? row[key][column.filterKey] : row[key])
|
||||
String(column.filterKey ? row[key][column.filterKey] : row[key])
|
||||
.toUpperCase()
|
||||
.includes(filter)
|
||||
) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-plugin-disable lit */
|
||||
import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
|
||||
import "../ha-icon-button";
|
||||
import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
|
||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
||||
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { formatTime } from "../../common/datetime/format_time";
|
||||
import "../ha-icon-button";
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
/* global Chart moment Color */
|
||||
@@ -355,7 +355,7 @@ class HaChartBase extends mixinBehaviors(
|
||||
return value;
|
||||
}
|
||||
const date = new Date(values[index].value);
|
||||
return formatTime(date);
|
||||
return formatTime(date, this.hass.language);
|
||||
}
|
||||
|
||||
drawChart() {
|
||||
@@ -420,7 +420,7 @@ class HaChartBase extends mixinBehaviors(
|
||||
},
|
||||
};
|
||||
options = Chart.helpers.merge(options, this.data.options);
|
||||
options.scales.xAxes[0].ticks.callback = this._formatTickValue;
|
||||
options.scales.xAxes[0].ticks.callback = this._formatTickValue.bind(this);
|
||||
if (this.data.type === "timeline") {
|
||||
this.set("isTimeline", true);
|
||||
if (this.data.colors !== undefined) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
LitElement,
|
||||
property,
|
||||
svg,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
|
||||
import {
|
||||
getValueInPercentage,
|
||||
normalize,
|
||||
roundWithOneDecimal,
|
||||
} from "../util/calculate";
|
||||
|
||||
@customElement("ha-bar")
|
||||
export class HaBar extends LitElement {
|
||||
@property({ type: Number }) public min = 0;
|
||||
|
||||
@property({ type: Number }) public max = 100;
|
||||
|
||||
@property({ type: Number }) public value!: number;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valuePrecentage = roundWithOneDecimal(
|
||||
getValueInPercentage(
|
||||
normalize(this.value, this.min, this.max),
|
||||
this.min,
|
||||
this.max
|
||||
)
|
||||
);
|
||||
|
||||
return svg`
|
||||
<svg>
|
||||
<g>
|
||||
<rect></rect>
|
||||
<rect width="${valuePrecentage}%"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
rect:first-child {
|
||||
width: 100%;
|
||||
fill: var(--ha-bar-background-color, var(--secondary-background-color));
|
||||
}
|
||||
rect:last-child {
|
||||
fill: var(--ha-bar-primary-color, var(--primary-color));
|
||||
rx: var(--ha-bar-border-radius, 4px);
|
||||
}
|
||||
svg {
|
||||
border-radius: var(--ha-bar-border-radius, 4px);
|
||||
height: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-bar": HaBar;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
TemplateResult,
|
||||
property,
|
||||
LitElement,
|
||||
CSSResult,
|
||||
css,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
|
||||
import "./ha-icon-button";
|
||||
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { ToggleButton } from "../types";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@customElement("ha-button-toggle-group")
|
||||
export class HaButtonToggleGroup extends LitElement {
|
||||
@property() public buttons!: ToggleButton[];
|
||||
@property({ attribute: false }) public buttons!: ToggleButton[];
|
||||
|
||||
@property() public active?: string;
|
||||
|
||||
@@ -23,21 +22,23 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
return html`
|
||||
<div>
|
||||
${this.buttons.map(
|
||||
(button) => html` <ha-icon-button
|
||||
.label=${button.label}
|
||||
.icon=${button.icon}
|
||||
.value=${button.value}
|
||||
?active=${this.active === button.value}
|
||||
@click=${this._handleClick}
|
||||
>
|
||||
</ha-icon-button>`
|
||||
(button) => html`
|
||||
<mwc-icon-button
|
||||
.label=${button.label}
|
||||
.value=${button.value}
|
||||
?active=${this.active === button.value}
|
||||
@click=${this._handleClick}
|
||||
>
|
||||
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleClick(ev): void {
|
||||
this.active = ev.target.value;
|
||||
this.active = ev.currentTarget.value;
|
||||
fireEvent(this, "value-changed", { value: this.active });
|
||||
}
|
||||
|
||||
@@ -48,12 +49,13 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
--mdc-icon-button-size: var(--button-toggle-size, 36px);
|
||||
--mdc-icon-size: var(--button-toggle-icon-size, 20px);
|
||||
}
|
||||
ha-icon-button {
|
||||
mwc-icon-button {
|
||||
border: 1px solid var(--primary-color);
|
||||
border-right-width: 0px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-icon-button::before {
|
||||
mwc-icon-button::before {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
@@ -65,22 +67,26 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
content: "";
|
||||
transition: opacity 15ms linear, background-color 15ms linear;
|
||||
}
|
||||
ha-icon-button[active]::before {
|
||||
mwc-icon-button[active]::before {
|
||||
opacity: var(--mdc-icon-button-ripple-opacity, 0.12);
|
||||
}
|
||||
ha-icon-button:first-child {
|
||||
mwc-icon-button:first-child {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
ha-icon-button:last-child {
|
||||
mwc-icon-button:last-child {
|
||||
border-radius: 0 4px 4px 0;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
mwc-icon-button:only-child {
|
||||
border-radius: 4px;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-button-toggle-button": HaButtonToggleGroup;
|
||||
"ha-button-toggle-group": HaButtonToggleGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
import { nextRender } from "../common/util/render-status";
|
||||
import { getExternalConfig } from "../external_app/external_config";
|
||||
import {
|
||||
CAMERA_SUPPORT_STREAM,
|
||||
computeMJPEGStreamUrl,
|
||||
@@ -37,6 +39,8 @@ class HaCameraStream extends LitElement {
|
||||
|
||||
private _hlsPolyfillInstance?: Hls;
|
||||
|
||||
private _useExoPlayer = false;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._attached = true;
|
||||
@@ -125,22 +129,33 @@ class HaCameraStream extends LitElement {
|
||||
return this.shadowRoot!.querySelector("video")!;
|
||||
}
|
||||
|
||||
private async _getUseExoPlayer(): Promise<boolean> {
|
||||
if (!this.hass!.auth.external) {
|
||||
return false;
|
||||
}
|
||||
const externalConfig = await getExternalConfig(this.hass!.auth.external);
|
||||
return externalConfig && externalConfig.hasExoPlayer;
|
||||
}
|
||||
|
||||
private async _startHls(): Promise<void> {
|
||||
// eslint-disable-next-line
|
||||
const Hls = ((await import(
|
||||
/* webpackChunkName: "hls.js" */ "hls.js"
|
||||
)) as any).default as HLSModule;
|
||||
let hlsSupported = Hls.isSupported();
|
||||
let hls;
|
||||
const videoEl = this._videoEl;
|
||||
this._useExoPlayer = await this._getUseExoPlayer();
|
||||
if (!this._useExoPlayer) {
|
||||
hls = ((await import(/* webpackChunkName: "hls.js" */ "hls.js")) as any)
|
||||
.default as HLSModule;
|
||||
let hlsSupported = hls.isSupported();
|
||||
|
||||
if (!hlsSupported) {
|
||||
hlsSupported =
|
||||
videoEl.canPlayType("application/vnd.apple.mpegurl") !== "";
|
||||
}
|
||||
if (!hlsSupported) {
|
||||
hlsSupported =
|
||||
videoEl.canPlayType("application/vnd.apple.mpegurl") !== "";
|
||||
}
|
||||
|
||||
if (!hlsSupported) {
|
||||
this._forceMJPEG = this.stateObj!.entity_id;
|
||||
return;
|
||||
if (!hlsSupported) {
|
||||
this._forceMJPEG = this.stateObj!.entity_id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -149,8 +164,10 @@ class HaCameraStream extends LitElement {
|
||||
this.stateObj!.entity_id
|
||||
);
|
||||
|
||||
if (Hls.isSupported()) {
|
||||
this._renderHLSPolyfill(videoEl, Hls, url);
|
||||
if (this._useExoPlayer) {
|
||||
this._renderHLSExoPlayer(url);
|
||||
} else if (hls.isSupported()) {
|
||||
this._renderHLSPolyfill(videoEl, hls, url);
|
||||
} else {
|
||||
this._renderHLSNative(videoEl, url);
|
||||
}
|
||||
@@ -163,6 +180,29 @@ class HaCameraStream extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _renderHLSExoPlayer(url: string) {
|
||||
window.addEventListener("resize", this._resizeExoPlayer);
|
||||
this.updateComplete.then(() => nextRender()).then(this._resizeExoPlayer);
|
||||
this._videoEl.style.visibility = "hidden";
|
||||
await this.hass!.auth.external!.sendMessage({
|
||||
type: "exoplayer/play_hls",
|
||||
payload: new URL(url, window.location.href).toString(),
|
||||
});
|
||||
}
|
||||
|
||||
private _resizeExoPlayer = () => {
|
||||
const rect = this._videoEl.getBoundingClientRect();
|
||||
this.hass!.auth.external!.fireMessage({
|
||||
type: "exoplayer/resize",
|
||||
payload: {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private async _renderHLSNative(videoEl: HTMLVideoElement, url: string) {
|
||||
videoEl.src = url;
|
||||
await new Promise((resolve) =>
|
||||
@@ -194,11 +234,15 @@ class HaCameraStream extends LitElement {
|
||||
fireEvent(this, "iron-resize");
|
||||
}
|
||||
|
||||
private _destroyPolyfill(): void {
|
||||
private _destroyPolyfill() {
|
||||
if (this._hlsPolyfillInstance) {
|
||||
this._hlsPolyfillInstance.destroy();
|
||||
this._hlsPolyfillInstance = undefined;
|
||||
}
|
||||
if (this._useExoPlayer) {
|
||||
window.removeEventListener("resize", this._resizeExoPlayer);
|
||||
this.hass!.auth.external!.fireMessage({ type: "exoplayer/stop" });
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Editor } from "codemirror";
|
||||
import {
|
||||
customElement,
|
||||
property,
|
||||
internalProperty,
|
||||
property,
|
||||
PropertyValues,
|
||||
UpdatingElement,
|
||||
} from "lit-element";
|
||||
@@ -123,7 +123,7 @@ export class HaCodeEditor extends UpdatingElement {
|
||||
transition: 0.2s ease border-right;
|
||||
}
|
||||
.cm-s-default.CodeMirror {
|
||||
background-color: var(--card-background-color);
|
||||
background-color: var(--code-editor-background-color, var(--card-background-color));
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.cm-s-default .CodeMirror-cursor {
|
||||
|
||||
@@ -11,23 +11,13 @@ import { styleMap } from "lit-html/directives/style-map";
|
||||
import { afterNextRender } from "../common/util/render-status";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
import { getValueInPercentage, normalize } from "../util/calculate";
|
||||
|
||||
const getAngle = (value: number, min: number, max: number) => {
|
||||
const percentage = getValueInPercentage(normalize(value, min, max), min, max);
|
||||
return (percentage * 180) / 100;
|
||||
};
|
||||
|
||||
const normalize = (value: number, min: number, max: number) => {
|
||||
if (value > max) return max;
|
||||
if (value < min) return min;
|
||||
return value;
|
||||
};
|
||||
|
||||
const getValueInPercentage = (value: number, min: number, max: number) => {
|
||||
const newMax = max - min;
|
||||
const newVal = value - min;
|
||||
return (100 * newVal) / newMax;
|
||||
};
|
||||
|
||||
// Workaround for https://github.com/home-assistant/frontend/issues/6467
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ const mdiRenameMapping = {
|
||||
pot: "pot-steam",
|
||||
ruby: "language-ruby",
|
||||
sailing: "sail-boat",
|
||||
scooter: "human-scooter",
|
||||
settings: "cog",
|
||||
"settings-box": "cog-box",
|
||||
"settings-outline": "cog-outline",
|
||||
|
||||
@@ -25,7 +25,7 @@ export class HaSettingsRow extends LitElement {
|
||||
</style>
|
||||
<paper-item-body
|
||||
?two-line=${!this.threeLine}
|
||||
?three-line=${!this.threeLine}
|
||||
?three-line=${this.threeLine}
|
||||
>
|
||||
<slot name="heading"></slot>
|
||||
<div secondary><slot name="description"></slot></div>
|
||||
|
||||
@@ -21,8 +21,8 @@ import {
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import {
|
||||
LeafletModuleType,
|
||||
setupLeafletMap,
|
||||
replaceTileLayer,
|
||||
setupLeafletMap,
|
||||
} from "../../common/dom/setup-leaflet-map";
|
||||
import { nextRender } from "../../common/util/render-status";
|
||||
import { defaultRadiusColor } from "../../data/zone";
|
||||
@@ -40,6 +40,8 @@ class LocationEditor extends LitElement {
|
||||
|
||||
@property() public icon?: string;
|
||||
|
||||
@property({ type: Boolean }) public darkMode?: boolean;
|
||||
|
||||
public fitZoom = 16;
|
||||
|
||||
private _iconEl?: DivIcon;
|
||||
@@ -129,7 +131,7 @@ class LocationEditor extends LitElement {
|
||||
private async _initMap(): Promise<void> {
|
||||
[this._leafletMap, this.Leaflet, this._tileLayer] = await setupLeafletMap(
|
||||
this._mapEl,
|
||||
this.hass.themes?.darkMode,
|
||||
this.darkMode ?? this.hass.themes?.darkMode,
|
||||
Boolean(this.radius)
|
||||
);
|
||||
this._leafletMap.addEventListener(
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { HASSDomEvent } from "../../common/dom/fire_event";
|
||||
import type {
|
||||
MediaPickedEvent,
|
||||
MediaPlayerBrowseAction,
|
||||
} from "../../data/media-player";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { createCloseHeading } from "../ha-dialog";
|
||||
import "./ha-media-player-browse";
|
||||
import { MediaPlayerBrowseDialogParams } from "./show-media-browser-dialog";
|
||||
|
||||
@customElement("dialog-media-player-browse")
|
||||
class DialogMediaPlayerBrowse extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _entityId!: string;
|
||||
|
||||
@internalProperty() private _mediaContentId?: string;
|
||||
|
||||
@internalProperty() private _mediaContentType?: string;
|
||||
|
||||
@internalProperty() private _action?: MediaPlayerBrowseAction;
|
||||
|
||||
@internalProperty() private _params?: MediaPlayerBrowseDialogParams;
|
||||
|
||||
public async showDialog(
|
||||
params: MediaPlayerBrowseDialogParams
|
||||
): Promise<void> {
|
||||
this._params = params;
|
||||
this._entityId = this._params.entityId;
|
||||
this._mediaContentId = this._params.mediaContentId;
|
||||
this._mediaContentType = this._params.mediaContentType;
|
||||
this._action = this._params.action || "play";
|
||||
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
hideActions
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.components.media-browser.media-player-browser")
|
||||
)}
|
||||
@closed=${this._closeDialog}
|
||||
>
|
||||
<ha-media-player-browse
|
||||
.hass=${this.hass}
|
||||
.entityId=${this._entityId}
|
||||
.action=${this._action!}
|
||||
.mediaContentId=${this._mediaContentId}
|
||||
.mediaContentType=${this._mediaContentType}
|
||||
@media-picked=${this._mediaPicked}
|
||||
></ha-media-player-browse>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _closeDialog() {
|
||||
this._params = undefined;
|
||||
}
|
||||
|
||||
private _mediaPicked(ev: HASSDomEvent<MediaPickedEvent>): void {
|
||||
this._params!.mediaPickedCallback(ev.detail);
|
||||
if (this._action !== "play") {
|
||||
this._closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--dialog-z-index: 8;
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-max-width: 800px;
|
||||
}
|
||||
ha-media-player-browse {
|
||||
width: 700px;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-media-player-browse": DialogMediaPlayerBrowse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-fab/mwc-fab";
|
||||
import "@material/mwc-list/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiArrowLeft, mdiFolder, mdiPlay, mdiPlus } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { browseMediaPlayer, MediaPickedEvent } from "../../data/media-player";
|
||||
import type { MediaPlayerItem } from "../../data/media-player";
|
||||
import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../entity/ha-entity-picker";
|
||||
import "../ha-button-menu";
|
||||
import "../ha-card";
|
||||
import "../ha-circular-progress";
|
||||
import "../ha-paper-dropdown-menu";
|
||||
import "../ha-svg-icon";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"media-picked": MediaPickedEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-media-player-browse")
|
||||
export class HaMediaPlayerBrowse extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public entityId!: string;
|
||||
|
||||
@property() public mediaContentId?: string;
|
||||
|
||||
@property() public mediaContentType?: string;
|
||||
|
||||
@property() public action: "pick" | "play" = "play";
|
||||
|
||||
@property({ type: Boolean, attribute: "narrow", reflect: true })
|
||||
private _narrow = false;
|
||||
|
||||
@internalProperty() private _loading = false;
|
||||
|
||||
@internalProperty() private _mediaPlayerItems: MediaPlayerItem[] = [];
|
||||
|
||||
private _resizeObserver?: ResizeObserver;
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.updateComplete.then(() => this._attachObserver());
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
if (this._resizeObserver) {
|
||||
this._resizeObserver.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._mediaPlayerItems.length) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
if (this._loading) {
|
||||
return html`<ha-circular-progress active></ha-circular-progress>`;
|
||||
}
|
||||
|
||||
const mostRecentItem = this._mediaPlayerItems[
|
||||
this._mediaPlayerItems.length - 1
|
||||
];
|
||||
const previousItem =
|
||||
this._mediaPlayerItems.length > 1
|
||||
? this._mediaPlayerItems[this._mediaPlayerItems.length - 2]
|
||||
: undefined;
|
||||
|
||||
const hasExpandableChildren:
|
||||
| MediaPlayerItem
|
||||
| undefined = this._hasExpandableChildren(mostRecentItem.children);
|
||||
|
||||
return html`
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
${mostRecentItem.thumbnail
|
||||
? html`
|
||||
<div
|
||||
class="img"
|
||||
style="background-image: url(${mostRecentItem.thumbnail})"
|
||||
>
|
||||
${this._narrow && mostRecentItem?.can_play
|
||||
? html`
|
||||
<mwc-fab
|
||||
mini
|
||||
.item=${mostRecentItem}
|
||||
@click=${this._actionClicked}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.label=${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}-media`
|
||||
)}
|
||||
.path=${this.action === "play" ? mdiPlay : mdiPlus}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}`
|
||||
)}
|
||||
</mwc-fab>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: html``}
|
||||
<div class="header-info">
|
||||
<div class="breadcrumb-overflow">
|
||||
<div class="breadcrumb">
|
||||
${previousItem
|
||||
? html`
|
||||
<div
|
||||
class="previous-title"
|
||||
.previous=${true}
|
||||
.item=${previousItem}
|
||||
@click=${this._navigate}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiArrowLeft}></ha-svg-icon>
|
||||
${previousItem.title}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<h1 class="title">${mostRecentItem.title}</h1>
|
||||
<h2 class="subtitle">
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.content-type.${mostRecentItem.media_content_type}`
|
||||
)}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
${mostRecentItem?.can_play &&
|
||||
(!this._narrow || (this._narrow && !mostRecentItem.thumbnail))
|
||||
? html`
|
||||
<div class="actions">
|
||||
<mwc-button
|
||||
raised
|
||||
.item=${mostRecentItem}
|
||||
@click=${this._actionClicked}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.label=${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}-media`
|
||||
)}
|
||||
.path=${this.action === "play" ? mdiPlay : mdiPlus}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}`
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
${mostRecentItem.children?.length
|
||||
? hasExpandableChildren
|
||||
? html`
|
||||
<div class="children">
|
||||
${mostRecentItem.children?.length
|
||||
? html`
|
||||
${mostRecentItem.children.map(
|
||||
(child) => html`
|
||||
<div
|
||||
class="child"
|
||||
.item=${child}
|
||||
@click=${this._navigate}
|
||||
>
|
||||
<div class="ha-card-parent">
|
||||
<ha-card
|
||||
outlined
|
||||
style="background-image: url(${child.thumbnail})"
|
||||
>
|
||||
${child.can_expand && !child.thumbnail
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
class="folder"
|
||||
.path=${mdiFolder}
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
${child.can_play
|
||||
? html`
|
||||
<mwc-icon-button
|
||||
class="play"
|
||||
.item=${child}
|
||||
.label=${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}-media`
|
||||
)}
|
||||
@click=${this._actionClicked}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${this.action === "play"
|
||||
? mdiPlay
|
||||
: mdiPlus}
|
||||
></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="title">${child.title}</div>
|
||||
<div class="type">
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.content-type.${child.media_content_type}`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<mwc-list>
|
||||
${mostRecentItem.children.map(
|
||||
(child) => html`<mwc-list-item
|
||||
@click=${this._actionClicked}
|
||||
.item=${child}
|
||||
graphic="icon"
|
||||
>
|
||||
<span>${child.title}</span>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.label=${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}-media`
|
||||
)}
|
||||
.path=${this.action === "play" ? mdiPlay : mdiPlus}
|
||||
></ha-svg-icon
|
||||
></mwc-list-item>
|
||||
<li divider role="separator"></li>`
|
||||
)}
|
||||
</mwc-list>
|
||||
`
|
||||
: this.hass.localize("ui.components.media-browser.no_items")}
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
this._measureCard();
|
||||
this._attachObserver();
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (
|
||||
!changedProps.has("entityId") &&
|
||||
!changedProps.has("mediaContentId") &&
|
||||
!changedProps.has("mediaContentType") &&
|
||||
!changedProps.has("action")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fetchData(this.mediaContentId, this.mediaContentType).then(
|
||||
(itemData) => {
|
||||
this._mediaPlayerItems = [itemData];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _actionClicked(ev: MouseEvent): void {
|
||||
ev.stopPropagation();
|
||||
const item = (ev.currentTarget as any).item;
|
||||
|
||||
this._runAction(item);
|
||||
}
|
||||
|
||||
private _runAction(item: MediaPlayerItem): void {
|
||||
fireEvent(this, "media-picked", {
|
||||
media_content_id: item.media_content_id,
|
||||
media_content_type: item.media_content_type,
|
||||
});
|
||||
}
|
||||
|
||||
private async _navigate(ev: MouseEvent): Promise<void> {
|
||||
const target = ev.currentTarget as any;
|
||||
let item: MediaPlayerItem | undefined;
|
||||
|
||||
if (target.previous) {
|
||||
this._mediaPlayerItems!.pop();
|
||||
item = this._mediaPlayerItems!.pop();
|
||||
}
|
||||
|
||||
item = target.item;
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemData = await this._fetchData(
|
||||
item.media_content_id,
|
||||
item.media_content_type
|
||||
);
|
||||
this._mediaPlayerItems = [...this._mediaPlayerItems, itemData];
|
||||
}
|
||||
|
||||
private async _fetchData(
|
||||
mediaContentId?: string,
|
||||
mediaContentType?: string
|
||||
): Promise<MediaPlayerItem> {
|
||||
const itemData = await browseMediaPlayer(
|
||||
this.hass,
|
||||
this.entityId,
|
||||
!mediaContentId ? undefined : mediaContentId,
|
||||
mediaContentType
|
||||
);
|
||||
|
||||
return itemData;
|
||||
}
|
||||
|
||||
private _measureCard(): void {
|
||||
this._narrow = this.offsetWidth < 500;
|
||||
}
|
||||
|
||||
private async _attachObserver(): Promise<void> {
|
||||
if (!this._resizeObserver) {
|
||||
await installResizeObserver();
|
||||
this._resizeObserver = new ResizeObserver(
|
||||
debounce(() => this._measureCard(), 250, false)
|
||||
);
|
||||
}
|
||||
|
||||
this._resizeObserver.observe(this);
|
||||
}
|
||||
|
||||
private _hasExpandableChildren = memoizeOne((children) =>
|
||||
children.find((item: MediaPlayerItem) => item.can_expand)
|
||||
);
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.breadcrumb-overflow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-grow: 1;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.header-content .img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
margin-right: 16px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-self: stretch;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-info .actions {
|
||||
padding-top: 24px;
|
||||
--mdc-theme-primary: var(--primary-color);
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.breadcrumb .title {
|
||||
font-size: 48px;
|
||||
line-height: 1.2;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.breadcrumb .previous-title {
|
||||
font-size: 14px;
|
||||
padding-bottom: 8px;
|
||||
color: var(--secondary-text-color);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
|
||||
.breadcrumb .subtitle {
|
||||
font-size: 16px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.divider {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.divider::before {
|
||||
height: 1px;
|
||||
display: block;
|
||||
background-color: var(--divider-color);
|
||||
content: " ";
|
||||
}
|
||||
|
||||
/* ============= CHILDREN ============= */
|
||||
|
||||
mwc-list {
|
||||
--mdc-list-vertical-padding: 0;
|
||||
--mdc-theme-text-icon-on-background: var(--secondary-text-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
mwc-list li:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
mwc-list li[divider] {
|
||||
border-bottom-color: var(--divider-color);
|
||||
}
|
||||
|
||||
.children {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(
|
||||
auto-fit,
|
||||
minmax(var(--media-browse-item-size, 175px), 0.33fr)
|
||||
);
|
||||
grid-gap: 16px;
|
||||
margin: 8px 0px;
|
||||
}
|
||||
|
||||
.child {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ha-card-parent {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
width: 100%;
|
||||
padding-bottom: 100%;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.child .folder,
|
||||
.child .play {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.child .folder {
|
||||
color: var(--secondary-text-color);
|
||||
top: calc(50% - (var(--mdc-icon-size) / 2));
|
||||
left: calc(50% - (var(--mdc-icon-size) / 2));
|
||||
--mdc-icon-size: calc(var(--media-browse-item-size, 175px) * 0.4);
|
||||
}
|
||||
|
||||
.child .play {
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
transition: all 0.5s;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.child .play:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-card:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.child .title {
|
||||
font-size: 16px;
|
||||
padding-top: 8px;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.child .type {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
/* ============= Narrow ============= */
|
||||
|
||||
:host([narrow]) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:host([narrow]) mwc-list {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:host([narrow]) .breadcrumb .title {
|
||||
font-size: 38px;
|
||||
}
|
||||
|
||||
:host([narrow]) .breadcrumb-overflow {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
:host([narrow]) .header-content {
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
:host([narrow]) .header-content .img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
padding-bottom: 100%;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:host([narrow]) .header-content .img mwc-fab {
|
||||
position: absolute;
|
||||
--mdc-theme-secondary: var(--primary-color);
|
||||
bottom: -20px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
:host([narrow]) .header-info,
|
||||
:host([narrow]) .media-source,
|
||||
:host([narrow]) .children {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
:host([narrow]) .children {
|
||||
grid-template-columns: 1fr 1fr !important;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-media-player-browse": HaMediaPlayerBrowse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import {
|
||||
MediaPickedEvent,
|
||||
MediaPlayerBrowseAction,
|
||||
} from "../../data/media-player";
|
||||
|
||||
export interface MediaPlayerBrowseDialogParams {
|
||||
action: MediaPlayerBrowseAction;
|
||||
entityId: string;
|
||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) => void;
|
||||
mediaContentId?: string;
|
||||
mediaContentType?: string;
|
||||
}
|
||||
|
||||
export const showMediaBrowserDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: MediaPlayerBrowseDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-media-player-browse",
|
||||
dialogImport: () =>
|
||||
import(
|
||||
/* webpackChunkName: "dialog-media-player-browse" */ "./dialog-media-player-browse"
|
||||
),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
||||
@@ -19,6 +19,7 @@ class StateHistoryChartLine extends LocalizeMixin(PolymerElement) {
|
||||
</style>
|
||||
<ha-chart-base
|
||||
id="chart"
|
||||
hass="[[hass]]"
|
||||
data="[[chartData]]"
|
||||
identifier="[[identifier]]"
|
||||
rendered="{{rendered}}"
|
||||
@@ -28,6 +29,9 @@ class StateHistoryChartLine extends LocalizeMixin(PolymerElement) {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
chartData: Object,
|
||||
data: Object,
|
||||
names: Object,
|
||||
|
||||
@@ -25,6 +25,7 @@ class StateHistoryChartTimeline extends LocalizeMixin(PolymerElement) {
|
||||
}
|
||||
</style>
|
||||
<ha-chart-base
|
||||
hass="[[hass]]"
|
||||
data="[[chartData]]"
|
||||
rendered="{{rendered}}"
|
||||
rtl="{{rtl}}"
|
||||
|
||||
@@ -44,3 +44,14 @@ export const createAuthForUser = async (
|
||||
username,
|
||||
password,
|
||||
});
|
||||
|
||||
export const adminChangePassword = async (
|
||||
hass: HomeAssistant,
|
||||
userId: string,
|
||||
password: string
|
||||
) =>
|
||||
hass.callWS<void>({
|
||||
type: "config/auth_provider/homeassistant/admin_change_password",
|
||||
user_id: userId,
|
||||
password,
|
||||
});
|
||||
|
||||
+29
-1
@@ -3,7 +3,7 @@ import {
|
||||
HassEntityBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { navigate } from "../common/navigate";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { HomeAssistant, Context } from "../types";
|
||||
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
||||
import { Action } from "./script";
|
||||
|
||||
@@ -206,3 +206,31 @@ export const getAutomationEditorInitData = () => {
|
||||
inititialAutomationEditorData = undefined;
|
||||
return data;
|
||||
};
|
||||
|
||||
export const subscribeTrigger = (
|
||||
hass: HomeAssistant,
|
||||
onChange: (result: {
|
||||
variables: {
|
||||
trigger: {};
|
||||
};
|
||||
context: Context;
|
||||
}) => void,
|
||||
trigger: Trigger | Trigger[],
|
||||
variables?: {}
|
||||
) =>
|
||||
hass.connection.subscribeMessage(onChange, {
|
||||
type: "subscribe_trigger",
|
||||
trigger,
|
||||
variables,
|
||||
});
|
||||
|
||||
export const testCondition = (
|
||||
hass: HomeAssistant,
|
||||
condition: Condition | Condition[],
|
||||
variables?: {}
|
||||
) =>
|
||||
hass.callWS<{ result: boolean }>({
|
||||
type: "test_condition",
|
||||
condition,
|
||||
variables,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface ConfigEntry {
|
||||
state: string;
|
||||
connection_class: string;
|
||||
supports_options: boolean;
|
||||
supports_unload: boolean;
|
||||
}
|
||||
|
||||
export interface ConfigEntryMutableParams {
|
||||
@@ -37,6 +38,11 @@ export const deleteConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
|
||||
require_restart: boolean;
|
||||
}>("DELETE", `config/config_entries/entry/${configEntryId}`);
|
||||
|
||||
export const reloadConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
|
||||
hass.callApi<{
|
||||
require_restart: boolean;
|
||||
}>("POST", `config/config_entries/entry/${configEntryId}/reload`);
|
||||
|
||||
export const getConfigEntrySystemOptions = (
|
||||
hass: HomeAssistant,
|
||||
configEntryId: string
|
||||
|
||||
@@ -72,6 +72,7 @@ export interface HassioAddonDetails extends HassioAddonInfo {
|
||||
ingress_panel: boolean;
|
||||
ingress_entry: null | string;
|
||||
ingress_url: null | string;
|
||||
watchdog: null | boolean;
|
||||
}
|
||||
|
||||
export interface HassioAddonsInfo {
|
||||
@@ -99,6 +100,7 @@ export interface HassioAddonSetOptionParams {
|
||||
auto_update?: boolean;
|
||||
ingress_panel?: boolean;
|
||||
network?: object | null;
|
||||
watchdog?: boolean;
|
||||
}
|
||||
|
||||
export const reloadHassioAddons = async (hass: HomeAssistant) => {
|
||||
|
||||
@@ -40,6 +40,10 @@ export const updateOS = async (hass: HomeAssistant) => {
|
||||
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/update");
|
||||
};
|
||||
|
||||
export const configSyncOS = async (hass: HomeAssistant) => {
|
||||
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/config/sync");
|
||||
};
|
||||
|
||||
export const changeHostOptions = async (hass: HomeAssistant, options: any) => {
|
||||
return hass.callApi<HassioResponse<void>>(
|
||||
"POST",
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { hassioApiResultExtractor, HassioResponse } from "./common";
|
||||
|
||||
export interface NetworkInterface {
|
||||
gateway: string;
|
||||
id: string;
|
||||
ip_address: string;
|
||||
address?: string;
|
||||
method: "static" | "dhcp";
|
||||
nameservers: string[] | string;
|
||||
dns?: string[];
|
||||
primary: boolean;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface NetworkInterfaces {
|
||||
[key: string]: NetworkInterface;
|
||||
}
|
||||
|
||||
export interface NetworkInfo {
|
||||
interfaces: NetworkInterfaces;
|
||||
}
|
||||
|
||||
export const fetchNetworkInfo = async (hass: HomeAssistant) => {
|
||||
return hassioApiResultExtractor(
|
||||
await hass.callApi<HassioResponse<NetworkInfo>>(
|
||||
"GET",
|
||||
"hassio/network/info"
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const updateNetworkInterface = async (
|
||||
hass: HomeAssistant,
|
||||
network_interface: string,
|
||||
options: Partial<NetworkInterface>
|
||||
) => {
|
||||
await hass.callApi<HassioResponse<NetworkInfo>>(
|
||||
"POST",
|
||||
`hassio/network/interface/${network_interface}/update`,
|
||||
options
|
||||
);
|
||||
};
|
||||
@@ -35,6 +35,14 @@ export interface SupervisorOptions {
|
||||
addons_repositories?: string[];
|
||||
}
|
||||
|
||||
export const reloadSupervisor = async (hass: HomeAssistant) => {
|
||||
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/reload`);
|
||||
};
|
||||
|
||||
export const updateSupervisor = async (hass: HomeAssistant) => {
|
||||
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/update`);
|
||||
};
|
||||
|
||||
export const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => {
|
||||
return hassioApiResultExtractor(
|
||||
await hass.callApi<HassioResponse<HassioHomeAssistantInfo>>(
|
||||
@@ -71,7 +79,11 @@ export const createHassioSession = async (hass: HomeAssistant) => {
|
||||
"POST",
|
||||
"hassio/ingress/session"
|
||||
);
|
||||
document.cookie = `ingress_session=${response.data.session};path=/api/hassio_ingress/;SameSite=Strict`;
|
||||
document.cookie = `ingress_session=${
|
||||
response.data.session
|
||||
};path=/api/hassio_ingress/;SameSite=Strict${
|
||||
location.protocol === "https:" ? ";Secure" : ""
|
||||
}`;
|
||||
};
|
||||
|
||||
export const setSupervisorOption = async (
|
||||
|
||||
@@ -7,6 +7,12 @@ export interface LogbookEntry {
|
||||
entity_id?: string;
|
||||
domain: string;
|
||||
context_user_id?: string;
|
||||
context_event_type?: string;
|
||||
context_domain?: string;
|
||||
context_service?: string;
|
||||
context_entity_id?: string;
|
||||
context_entity_id_name?: string;
|
||||
context_name?: string;
|
||||
}
|
||||
|
||||
const DATA_CACHE: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
export const SUPPORT_PAUSE = 1;
|
||||
export const SUPPORT_SEEK = 2;
|
||||
@@ -14,8 +15,16 @@ export const SUPPORT_SELECT_SOURCE = 2048;
|
||||
export const SUPPORT_STOP = 4096;
|
||||
export const SUPPORTS_PLAY = 16384;
|
||||
export const SUPPORT_SELECT_SOUND_MODE = 65536;
|
||||
export const SUPPORT_BROWSE_MEDIA = 131072;
|
||||
export const CONTRAST_RATIO = 4.5;
|
||||
|
||||
export type MediaPlayerBrowseAction = "pick" | "play";
|
||||
|
||||
export interface MediaPickedEvent {
|
||||
media_content_id: string;
|
||||
media_content_type: string;
|
||||
}
|
||||
|
||||
export interface MediaPlayerThumbnail {
|
||||
content_type: string;
|
||||
content: string;
|
||||
@@ -26,6 +35,29 @@ export interface ControlButton {
|
||||
action: string;
|
||||
}
|
||||
|
||||
export interface MediaPlayerItem {
|
||||
title: string;
|
||||
media_content_type: string;
|
||||
media_content_id: string;
|
||||
can_play: boolean;
|
||||
can_expand: boolean;
|
||||
thumbnail?: string;
|
||||
children?: MediaPlayerItem[];
|
||||
}
|
||||
|
||||
export const browseMediaPlayer = (
|
||||
hass: HomeAssistant,
|
||||
entityId: string,
|
||||
mediaContentId?: string,
|
||||
mediaContentType?: string
|
||||
): Promise<MediaPlayerItem> =>
|
||||
hass.callWS<MediaPlayerItem>({
|
||||
type: "media_player/browse_media",
|
||||
entity_id: entityId,
|
||||
media_content_id: mediaContentId,
|
||||
media_content_type: mediaContentType,
|
||||
});
|
||||
|
||||
export const getCurrentProgress = (stateObj: HassEntity): number => {
|
||||
let progress = stateObj.attributes.media_position;
|
||||
|
||||
|
||||
@@ -39,6 +39,28 @@ export interface OZWDeviceMetaData {
|
||||
ProductPicBase64: string;
|
||||
}
|
||||
|
||||
export interface OZWInstance {
|
||||
ozw_instance: number;
|
||||
OZWDaemon_Version: string;
|
||||
OpenZWave_Version: string;
|
||||
QTOpenZWave_Version: string;
|
||||
Status: string;
|
||||
getControllerPath: string;
|
||||
homeID: string;
|
||||
}
|
||||
|
||||
export interface OZWNetworkStatistics {
|
||||
ozw_instance: number;
|
||||
node_count: number;
|
||||
readCnt: number;
|
||||
writeCnt: number;
|
||||
ACKCnt: number;
|
||||
CANCnt: number;
|
||||
NAKCnt: number;
|
||||
dropped: number;
|
||||
retries: number;
|
||||
}
|
||||
|
||||
export const nodeQueryStages = [
|
||||
"ProtocolInfo",
|
||||
"Probe",
|
||||
@@ -59,6 +81,26 @@ export const nodeQueryStages = [
|
||||
"Complete",
|
||||
];
|
||||
|
||||
export const networkOnlineStatuses = [
|
||||
"driverAllNodesQueried",
|
||||
"driverAllNodesQueriedSomeDead",
|
||||
"driverAwakeNodesQueried",
|
||||
];
|
||||
export const networkStartingStatuses = [
|
||||
"starting",
|
||||
"started",
|
||||
"Ready",
|
||||
"driverReady",
|
||||
];
|
||||
export const networkOfflineStatuses = [
|
||||
"Offline",
|
||||
"stopped",
|
||||
"driverFailed",
|
||||
"driverReset",
|
||||
"driverRemoved",
|
||||
"driverAllNodesOnFire",
|
||||
];
|
||||
|
||||
export const getIdentifiersFromDevice = function (
|
||||
device: DeviceRegistryEntry
|
||||
): OZWNodeIdentifiers | undefined {
|
||||
@@ -80,6 +122,31 @@ export const getIdentifiersFromDevice = function (
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchOZWInstances = (
|
||||
hass: HomeAssistant
|
||||
): Promise<OZWInstance[]> =>
|
||||
hass.callWS({
|
||||
type: "ozw/get_instances",
|
||||
});
|
||||
|
||||
export const fetchOZWNetworkStatus = (
|
||||
hass: HomeAssistant,
|
||||
ozw_instance: number
|
||||
): Promise<OZWInstance> =>
|
||||
hass.callWS({
|
||||
type: "ozw/network_status",
|
||||
ozw_instance: ozw_instance,
|
||||
});
|
||||
|
||||
export const fetchOZWNetworkStatistics = (
|
||||
hass: HomeAssistant,
|
||||
ozw_instance: number
|
||||
): Promise<OZWNetworkStatistics> =>
|
||||
hass.callWS({
|
||||
type: "ozw/network_statistics",
|
||||
ozw_instance: ozw_instance,
|
||||
});
|
||||
|
||||
export const fetchOZWNodeStatus = (
|
||||
hass: HomeAssistant,
|
||||
ozw_instance: number,
|
||||
|
||||
+3
-1
@@ -17,7 +17,9 @@ export const setDefaultPanel = (
|
||||
};
|
||||
|
||||
export const getDefaultPanel = (hass: HomeAssistant): PanelInfo =>
|
||||
hass.panels[hass.defaultPanel];
|
||||
hass.panels[hass.defaultPanel]
|
||||
? hass.panels[hass.defaultPanel]
|
||||
: hass.panels[DEFAULT_PANEL];
|
||||
|
||||
export const getPanelTitle = (hass: HomeAssistant): string | undefined => {
|
||||
if (!hass.panels) {
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { SVGTemplateResult, svg, html, TemplateResult, css } from "lit-element";
|
||||
import { styleMap } from "lit-html/directives/style-map";
|
||||
|
||||
import type { HomeAssistant, WeatherEntity } from "../types";
|
||||
import { roundWithOneDecimal } from "../util/calculate";
|
||||
|
||||
export const weatherSVGs = new Set<string>([
|
||||
"clear-night",
|
||||
@@ -135,7 +136,7 @@ export const getSecondaryWeatherAttribute = (
|
||||
return `
|
||||
${hass!.localize(
|
||||
`ui.card.weather.attributes.${attribute}`
|
||||
)} ${value} ${getWeatherUnit(hass!, attribute)}
|
||||
)} ${roundWithOneDecimal(value)} ${getWeatherUnit(hass!, attribute)}
|
||||
`;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,29 +5,27 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
|
||||
import {
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_EFFECT,
|
||||
} from "../../../data/light";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import type { HomeAssistant, LightEntity } from "../../../types";
|
||||
|
||||
import "../../../components/ha-attributes";
|
||||
import "../../../components/ha-color-picker";
|
||||
import "../../../components/ha-labeled-slider";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-labeled-slider";
|
||||
import "../../../components/ha-paper-dropdown-menu";
|
||||
import {
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_EFFECT,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
} from "../../../data/light";
|
||||
import type { HomeAssistant, LightEntity } from "../../../types";
|
||||
|
||||
interface HueSatColor {
|
||||
h: number;
|
||||
@@ -149,7 +147,7 @@ class MoreInfoLight extends LitElement {
|
||||
: ""}
|
||||
<ha-attributes
|
||||
.stateObj=${this.stateObj}
|
||||
extraFilters="brightness,color_temp,white_value,effect_list,effect,hs_color,rgb_color,xy_color,min_mireds,max_mireds,entity_id"
|
||||
extra-filters="brightness,color_temp,white_value,effect_list,effect,hs_color,rgb_color,xy_color,min_mireds,max_mireds,entity_id"
|
||||
></ha-attributes>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,43 +1,45 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
customElement,
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
import { HomeAssistant, MediaEntity } from "../../../types";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { UNAVAILABLE_STATES, UNAVAILABLE, UNKNOWN } from "../../../data/entity";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-paper-dropdown-menu";
|
||||
import "../../../components/ha-slider";
|
||||
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
|
||||
import { UNAVAILABLE, UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
||||
import {
|
||||
SUPPORT_TURN_ON,
|
||||
SUPPORT_TURN_OFF,
|
||||
ControlButton,
|
||||
MediaPickedEvent,
|
||||
SUPPORTS_PLAY,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_BROWSE_MEDIA,
|
||||
SUPPORT_NEXT_TRACK,
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
SUPPORT_SELECT_SOUND_MODE,
|
||||
SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_TURN_OFF,
|
||||
SUPPORT_TURN_ON,
|
||||
SUPPORT_VOLUME_BUTTONS,
|
||||
SUPPORT_VOLUME_MUTE,
|
||||
SUPPORT_VOLUME_SET,
|
||||
SUPPORT_VOLUME_BUTTONS,
|
||||
SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_SELECT_SOUND_MODE,
|
||||
SUPPORT_PLAY_MEDIA,
|
||||
ControlButton,
|
||||
} from "../../../data/media-player";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
|
||||
import "../../../components/ha-paper-dropdown-menu";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-slider";
|
||||
import "../../../components/ha-icon";
|
||||
import { HomeAssistant, MediaEntity } from "../../../types";
|
||||
|
||||
@customElement("more-info-media_player")
|
||||
class MoreInfoMediaPlayer extends LitElement {
|
||||
@@ -60,15 +62,29 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
? ""
|
||||
: html`
|
||||
<div class="controls">
|
||||
${controls!.map(
|
||||
(control) => html`
|
||||
<ha-icon-button
|
||||
action=${control.action}
|
||||
.icon=${control.icon}
|
||||
@click=${this._handleClick}
|
||||
></ha-icon-button>
|
||||
`
|
||||
)}
|
||||
<div class="basic-controls">
|
||||
${controls!.map(
|
||||
(control) => html`
|
||||
<ha-icon-button
|
||||
action=${control.action}
|
||||
.icon=${control.icon}
|
||||
@click=${this._handleClick}
|
||||
></ha-icon-button>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
|
||||
? html`
|
||||
<ha-icon-button
|
||||
icon="hass:folder-multiple"
|
||||
.title=${this.hass.localize(
|
||||
"ui.card.media_player.browse_media"
|
||||
)}
|
||||
@click=${this._showBrowseMedia}
|
||||
>
|
||||
</ha-icon-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`}
|
||||
${(supportsFeature(stateObj, SUPPORT_VOLUME_SET) ||
|
||||
@@ -158,7 +174,7 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
>
|
||||
${stateObj.attributes.sound_mode_list.map(
|
||||
(mode) => html`
|
||||
<paper-item itemName=${mode}>${mode}</paper-item>
|
||||
<paper-item .itemName=${mode}>${mode}</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
@@ -196,8 +212,16 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.basic-controls {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.volume,
|
||||
.controls,
|
||||
.source-input,
|
||||
.sound-input,
|
||||
.tts {
|
||||
@@ -328,21 +352,27 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
}
|
||||
|
||||
private _handleSourceChanged(e: CustomEvent) {
|
||||
const newVal = e.detail.value;
|
||||
const newVal = e.detail.item.itemName;
|
||||
|
||||
if (!newVal || this.stateObj!.attributes.source === newVal) return;
|
||||
if (!newVal || this.stateObj!.attributes.source === newVal) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass.callService("media_player", "select_source", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
source: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleSoundModeChanged(e: CustomEvent) {
|
||||
const newVal = e.detail.value;
|
||||
const newVal = e.detail.item.itemName;
|
||||
|
||||
if (!newVal || this.stateObj?.attributes.sound_mode === newVal) return;
|
||||
if (!newVal || this.stateObj?.attributes.sound_mode === newVal) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass.callService("media_player", "select_sound_mode", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
sound_mode: newVal,
|
||||
});
|
||||
}
|
||||
@@ -372,6 +402,26 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
});
|
||||
ttsInput.value = "";
|
||||
}
|
||||
|
||||
private _showBrowseMedia(): void {
|
||||
showMediaBrowserDialog(this, {
|
||||
action: "play",
|
||||
entityId: this.stateObj!.entity_id,
|
||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) =>
|
||||
this._playMedia(
|
||||
pickedMedia.media_content_id,
|
||||
pickedMedia.media_content_type
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
private _playMedia(media_content_id: string, media_content_type: string) {
|
||||
this.hass!.callService("media_player", "play_media", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
media_content_id,
|
||||
media_content_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ExternalMessaging } from "./external_messaging";
|
||||
export interface ExternalConfig {
|
||||
hasSettingsScreen: boolean;
|
||||
canWriteTag: boolean;
|
||||
hasExoPlayer: boolean;
|
||||
}
|
||||
|
||||
export const getExternalConfig = (
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
.header img {
|
||||
margin-right: 16px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #111111;
|
||||
color: #e1e1e1;
|
||||
--primary-text-color: #e1e1e1;
|
||||
--secondary-text-color: #9b9b9b;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -23,6 +23,14 @@
|
||||
.header img {
|
||||
margin-right: 16px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #111111;
|
||||
color: #e1e1e1;
|
||||
--primary-text-color: #e1e1e1;
|
||||
--secondary-text-color: #9b9b9b;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -5,15 +5,15 @@ import type { AppDrawerElement } from "@polymer/app-layout/app-drawer/app-drawer
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
customElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { listenMediaQuery } from "../common/dom/media_query";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { listenMediaQuery } from "../common/dom/media_query";
|
||||
import { toggleAttribute } from "../common/dom/toggle_attribute";
|
||||
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
||||
import type { HomeAssistant, Route } from "../types";
|
||||
@@ -49,7 +49,15 @@ class HomeAssistantMain extends LitElement {
|
||||
const disableSwipe =
|
||||
!sidebarNarrow || NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
|
||||
|
||||
// Style block in render because of the mixin that is not supported
|
||||
return html`
|
||||
<style>
|
||||
app-drawer {
|
||||
--app-drawer-content-container: {
|
||||
background-color: var(--primary-background-color, #fff);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<app-drawer-layout
|
||||
fullbleed
|
||||
.forceNarrow=${sidebarNarrow}
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
@@ -27,6 +27,7 @@ import type { PolymerChangedEvent } from "../polymer-types";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
const amsterdam = [52.3731339, 4.8903147];
|
||||
const mql = matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
@customElement("onboarding-core-config")
|
||||
class OnboardingCoreConfig extends LitElement {
|
||||
@@ -93,6 +94,7 @@ class OnboardingCoreConfig extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.location=${this._locationValue}
|
||||
.fitZoom=${14}
|
||||
.darkMode=${mql.matches}
|
||||
@change=${this._locationChanged}
|
||||
></ha-location-editor>
|
||||
</div>
|
||||
|
||||
@@ -1,67 +1,85 @@
|
||||
import {
|
||||
property,
|
||||
internalProperty,
|
||||
PropertyValues,
|
||||
LitElement,
|
||||
CSSResult,
|
||||
html,
|
||||
css,
|
||||
unsafeCSS,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
// @ts-ignore
|
||||
import fullcalendarStyle from "@fullcalendar/common/main.css";
|
||||
import { Calendar } from "@fullcalendar/core";
|
||||
import type { CalendarOptions } from "@fullcalendar/core";
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
// @ts-ignore
|
||||
import fullcalendarStyle from "@fullcalendar/core/main.css";
|
||||
// @ts-ignore
|
||||
import daygridStyle from "@fullcalendar/daygrid/main.css";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
import listPlugin from "@fullcalendar/list";
|
||||
// @ts-ignore
|
||||
import listStyle from "@fullcalendar/list/main.css";
|
||||
import "@material/mwc-button";
|
||||
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-button-toggle-group";
|
||||
|
||||
import type {
|
||||
CalendarViewChanged,
|
||||
CalendarEvent,
|
||||
ToggleButton,
|
||||
HomeAssistant,
|
||||
} from "../../types";
|
||||
import { mdiViewAgenda, mdiViewDay, mdiViewModule, mdiViewWeek } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
unsafeCSS,
|
||||
} from "lit-element";
|
||||
import memoize from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-button-toggle-group";
|
||||
import "../../components/ha-icon-button";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type {
|
||||
CalendarEvent,
|
||||
CalendarViewChanged,
|
||||
FullCalendarView,
|
||||
HomeAssistant,
|
||||
ToggleButton,
|
||||
} from "../../types";
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-full-calendar": HAFullCalendar;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"view-changed": CalendarViewChanged;
|
||||
}
|
||||
}
|
||||
|
||||
const fullCalendarConfig = {
|
||||
const defaultFullCalendarConfig: CalendarOptions = {
|
||||
headerToolbar: false,
|
||||
plugins: [dayGridPlugin],
|
||||
plugins: [dayGridPlugin, listPlugin, interactionPlugin],
|
||||
initialView: "dayGridMonth",
|
||||
dayMaxEventRows: true,
|
||||
height: "parent",
|
||||
eventDisplay: "list-item",
|
||||
};
|
||||
|
||||
const viewButtons: ToggleButton[] = [
|
||||
{ label: "Month View", value: "dayGridMonth", icon: "hass:view-module" },
|
||||
{ label: "Week View", value: "dayGridWeek", icon: "hass:view-week" },
|
||||
{ label: "Day View", value: "dayGridDay", icon: "hass:view-day" },
|
||||
{ label: "Month View", value: "dayGridMonth", iconPath: mdiViewModule },
|
||||
{ label: "Week View", value: "dayGridWeek", iconPath: mdiViewWeek },
|
||||
{ label: "Day View", value: "dayGridDay", iconPath: mdiViewDay },
|
||||
{ label: "List View", value: "listWeek", iconPath: mdiViewAgenda },
|
||||
];
|
||||
|
||||
class HAFullCalendar extends LitElement {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@property() public events: CalendarEvent[] = [];
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public narrow!: boolean;
|
||||
@property({ attribute: false }) public events: CalendarEvent[] = [];
|
||||
|
||||
@property({ attribute: false }) public views: FullCalendarView[] = [
|
||||
"dayGridMonth",
|
||||
"dayGridWeek",
|
||||
"dayGridDay",
|
||||
];
|
||||
|
||||
@internalProperty() private calendar?: Calendar;
|
||||
|
||||
@internalProperty() private _activeView = "dayGridMonth";
|
||||
@internalProperty() private _activeView: FullCalendarView = "dayGridMonth";
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const viewToggleButtons = this._viewToggleButtons(this.views);
|
||||
|
||||
return html`
|
||||
${this.calendar
|
||||
? html`
|
||||
@@ -96,27 +114,12 @@ class HAFullCalendar extends LitElement {
|
||||
${this.calendar.view.title}
|
||||
</h1>
|
||||
<ha-button-toggle-group
|
||||
.buttons=${viewButtons}
|
||||
.buttons=${viewToggleButtons}
|
||||
.active=${this._activeView}
|
||||
@value-changed=${this._handleView}
|
||||
></ha-button-toggle-group>
|
||||
`
|
||||
: html`
|
||||
<div class="controls">
|
||||
<mwc-button
|
||||
outlined
|
||||
class="today"
|
||||
@click=${this._handleToday}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.calendar.today"
|
||||
)}</mwc-button
|
||||
>
|
||||
<ha-button-toggle-group
|
||||
.buttons=${viewButtons}
|
||||
.active=${this._activeView}
|
||||
@value-changed=${this._handleView}
|
||||
></ha-button-toggle-group>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<h1>
|
||||
${this.calendar.view.title}
|
||||
@@ -138,6 +141,21 @@ class HAFullCalendar extends LitElement {
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<mwc-button
|
||||
outlined
|
||||
class="today"
|
||||
@click=${this._handleToday}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.calendar.today"
|
||||
)}</mwc-button
|
||||
>
|
||||
<ha-button-toggle-group
|
||||
.buttons=${viewToggleButtons}
|
||||
.active=${this._activeView}
|
||||
@value-changed=${this._handleView}
|
||||
></ha-button-toggle-group>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
`
|
||||
@@ -157,14 +175,25 @@ class HAFullCalendar extends LitElement {
|
||||
this.calendar.removeAllEventSources();
|
||||
this.calendar.addEventSource(this.events);
|
||||
}
|
||||
|
||||
if (changedProps.has("views") && !this.views.includes(this._activeView)) {
|
||||
this._activeView = this.views[0];
|
||||
this.calendar!.changeView(this._activeView);
|
||||
this._fireViewChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
const config = { ...fullCalendarConfig, locale: this.hass.language };
|
||||
const config: CalendarOptions = {
|
||||
...defaultFullCalendarConfig,
|
||||
locale: this.hass.language,
|
||||
};
|
||||
|
||||
config.dateClick = (info) => this._handleDateClick(info);
|
||||
config.eventClick = (info) => this._handleEventClick(info);
|
||||
|
||||
this.calendar = new Calendar(
|
||||
this.shadowRoot!.getElementById("calendar")!,
|
||||
// @ts-ignore
|
||||
config
|
||||
);
|
||||
|
||||
@@ -172,6 +201,25 @@ class HAFullCalendar extends LitElement {
|
||||
this._fireViewChanged();
|
||||
}
|
||||
|
||||
private _handleEventClick(info): void {
|
||||
if (info.view.type !== "dayGridMonth") {
|
||||
return;
|
||||
}
|
||||
|
||||
this._activeView = "dayGridDay";
|
||||
this.calendar!.changeView("dayGridDay");
|
||||
this.calendar!.gotoDate(info.event.startStr);
|
||||
}
|
||||
|
||||
private _handleDateClick(info): void {
|
||||
if (info.view.type !== "dayGridMonth") {
|
||||
return;
|
||||
}
|
||||
this._activeView = "dayGridDay";
|
||||
this.calendar!.changeView("dayGridDay");
|
||||
this.calendar!.gotoDate(info.dateStr);
|
||||
}
|
||||
|
||||
private _handleNext(): void {
|
||||
this.calendar!.next();
|
||||
this._fireViewChanged();
|
||||
@@ -201,14 +249,21 @@ class HAFullCalendar extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _viewToggleButtons = memoize((views) =>
|
||||
viewButtons.filter((button) =>
|
||||
views.includes(button.value as FullCalendarView)
|
||||
)
|
||||
);
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
${unsafeCSS(fullcalendarStyle)}
|
||||
${unsafeCSS(daygridStyle)}
|
||||
|
||||
:host {
|
||||
${unsafeCSS(listStyle)}
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
--fc-theme-standard-border-color: var(--divider-color);
|
||||
@@ -262,6 +317,15 @@ class HAFullCalendar extends LitElement {
|
||||
#calendar {
|
||||
flex-grow: 1;
|
||||
background-color: var(--card-background-color);
|
||||
min-height: 400px;
|
||||
--fc-neutral-bg-color: var(--card-background-color);
|
||||
--fc-list-event-hover-bg-color: var(--card-background-color);
|
||||
--fc-theme-standard-border-color: var(--divider-color);
|
||||
--fc-border-color: var(--divider-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.fc-theme-standard .fc-scrollgrid {
|
||||
@@ -273,15 +337,20 @@ class HAFullCalendar extends LitElement {
|
||||
}
|
||||
|
||||
th.fc-col-header-cell.fc-day {
|
||||
color: #70757a;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.fc-daygrid-dot-event:hover {
|
||||
background-color: inherit
|
||||
}
|
||||
|
||||
.fc-daygrid-day-top {
|
||||
text-align: center;
|
||||
padding-top: 8px;
|
||||
padding-top: 5px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
table.fc-scrollgrid-sync-table
|
||||
@@ -296,13 +365,21 @@ class HAFullCalendar extends LitElement {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
td.fc-day-today {
|
||||
.fc .fc-daygrid-day-number {
|
||||
padding: 3px !important;
|
||||
}
|
||||
|
||||
.fc .fc-daygrid-day.fc-day-today {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
td.fc-day-today .fc-daygrid-day-top {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
td.fc-day-today .fc-daygrid-day-number {
|
||||
height: 24px;
|
||||
color: var(--text-primary-color);
|
||||
color: var(--text-primary-color) !important;
|
||||
background-color: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
@@ -342,6 +419,66 @@ class HAFullCalendar extends LitElement {
|
||||
.fc-popover-header {
|
||||
background-color: var(--secondary-background-color) !important;
|
||||
}
|
||||
|
||||
.fc-theme-standard .fc-list-day-frame {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.fc-list.fc-view,
|
||||
.fc-list-event.fc-event td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.fc-list-day.fc-day th {
|
||||
border-bottom: none;
|
||||
border-top: 1px solid var(--fc-theme-standard-border-color, #ddd) !important;
|
||||
}
|
||||
|
||||
.fc-list-day-text {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.fc-list-day-side-text {
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.fc-list-table td,
|
||||
.fc-list-day-frame {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
:host([narrow]) .fc-dayGridMonth-view
|
||||
.fc-daygrid-dot-event
|
||||
.fc-event-time,
|
||||
:host([narrow]) .fc-dayGridMonth-view
|
||||
.fc-daygrid-dot-event
|
||||
.fc-event-title,
|
||||
:host([narrow]) .fc-dayGridMonth-view .fc-daygrid-day-bottom {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host([narrow]) .fc .fc-dayGridMonth-view .fc-daygrid-event-harness-abs {
|
||||
visibility: visible !important;
|
||||
position: static;
|
||||
}
|
||||
|
||||
:host([narrow]) .fc-dayGridMonth-view .fc-daygrid-day-events {
|
||||
display: flex;
|
||||
min-height: 2em !important;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
max-height: 2em;
|
||||
height: 2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host([narrow]) .fc-dayGridMonth-view .fc-scrollgrid-sync-table {
|
||||
overflow: hidden;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -53,12 +53,6 @@ class PanelCalendar extends LitElement {
|
||||
selected: true,
|
||||
calendar,
|
||||
}));
|
||||
|
||||
if (!this._start || !this._end) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fetchEvents(this._start, this._end, this._selectedCalendars);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -88,8 +82,8 @@ class PanelCalendar extends LitElement {
|
||||
<mwc-formfield .label=${selCal.calendar.name}>
|
||||
<mwc-checkbox
|
||||
style=${styleMap({
|
||||
"--mdc-theme-secondary":
|
||||
selCal.calendar.backgroundColor,
|
||||
"--mdc-theme-secondary": selCal.calendar
|
||||
.backgroundColor!,
|
||||
})}
|
||||
.value=${selCal.calendar.entity_id}
|
||||
.checked=${selCal.selected}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import "@material/mwc-fab";
|
||||
import { mdiPlus } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import {
|
||||
@@ -16,7 +18,8 @@ import {
|
||||
DataTableColumnContainer,
|
||||
RowClickedEvent,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "@material/mwc-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
AreaRegistryEntry,
|
||||
createAreaRegistryEntry,
|
||||
@@ -26,8 +29,6 @@ import {
|
||||
devicesInArea,
|
||||
} from "../../../data/device_registry";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../layouts/hass-loading-screen";
|
||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
@@ -37,7 +38,6 @@ import {
|
||||
loadAreaRegistryDetailDialog,
|
||||
showAreaRegistryDetailDialog,
|
||||
} from "./show-dialog-area-registry-detail";
|
||||
import { mdiPlus } from "@mdi/js";
|
||||
|
||||
@customElement("ha-config-areas-dashboard")
|
||||
export class HaConfigAreasDashboard extends LitElement {
|
||||
|
||||
@@ -44,6 +44,7 @@ import "./device-detail/ha-device-entities-card";
|
||||
import "./device-detail/ha-device-info-card";
|
||||
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
|
||||
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
||||
stateName?: string | null;
|
||||
@@ -307,11 +308,15 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.devices.automation.no_automations"
|
||||
)}</paper-item
|
||||
>
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.add_prompt",
|
||||
"name",
|
||||
this.hass.localize(
|
||||
"ui.panel.config.devices.automation.automations"
|
||||
)
|
||||
)}
|
||||
</paper-item>
|
||||
`}
|
||||
</ha-card>
|
||||
`
|
||||
@@ -375,11 +380,15 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<paper-item class="no-link"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.devices.scene.no_scenes"
|
||||
)}</paper-item
|
||||
>
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.add_prompt",
|
||||
"name",
|
||||
this.hass.localize(
|
||||
"ui.panel.config.devices.scene.scenes"
|
||||
)
|
||||
)}
|
||||
</paper-item>
|
||||
`
|
||||
}
|
||||
</ha-card>
|
||||
@@ -428,9 +437,13 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: html`
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.script.no_scripts"
|
||||
)}</paper-item
|
||||
>
|
||||
"ui.panel.config.devices.add_prompt",
|
||||
"name",
|
||||
this.hass.localize(
|
||||
"ui.panel.config.devices.script.scripts"
|
||||
)
|
||||
)}
|
||||
</paper-item>
|
||||
`}
|
||||
</ha-card>
|
||||
`
|
||||
@@ -549,11 +562,14 @@ export class HaConfigDevicePage extends LitElement {
|
||||
|
||||
const renameEntityid =
|
||||
this.showAdvanced &&
|
||||
confirm(
|
||||
this.hass.localize(
|
||||
(await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.confirm_rename_entity_ids"
|
||||
)
|
||||
);
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.devices.confirm_rename_entity_ids_warning"
|
||||
),
|
||||
}));
|
||||
|
||||
const updateProms = entities.map((entity) => {
|
||||
const name = entity.name || entity.stateName;
|
||||
@@ -702,6 +718,10 @@ export class HaConfigDevicePage extends LitElement {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-card {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
ha-card a {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
} from "../../../data/device_registry";
|
||||
import {
|
||||
EntityRegistryEntry,
|
||||
findBatteryEntity,
|
||||
findBatteryChargingEntity,
|
||||
findBatteryEntity,
|
||||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
@@ -181,8 +181,8 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
);
|
||||
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean): DataTableColumnContainer =>
|
||||
narrow
|
||||
(narrow: boolean): DataTableColumnContainer => {
|
||||
const columns: DataTableColumnContainer = narrow
|
||||
? {
|
||||
name: {
|
||||
title: "Device",
|
||||
@@ -199,36 +199,6 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
`;
|
||||
},
|
||||
},
|
||||
battery_entity: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.battery"
|
||||
),
|
||||
sortable: true,
|
||||
type: "numeric",
|
||||
width: "90px",
|
||||
template: (
|
||||
batteryEntityPair: DeviceRowData["battery_entity"]
|
||||
) => {
|
||||
const battery =
|
||||
batteryEntityPair && batteryEntityPair[0]
|
||||
? this.hass.states[batteryEntityPair[0]]
|
||||
: undefined;
|
||||
const batteryCharging =
|
||||
batteryEntityPair && batteryEntityPair[1]
|
||||
? this.hass.states[batteryEntityPair[1]]
|
||||
: undefined;
|
||||
return battery
|
||||
? html`
|
||||
${isNaN(battery.state as any) ? "-" : battery.state}%
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass!}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryCharging}
|
||||
></ha-battery-icon>
|
||||
`
|
||||
: html` - `;
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
name: {
|
||||
@@ -240,70 +210,69 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
grows: true,
|
||||
direction: "asc",
|
||||
},
|
||||
manufacturer: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.manufacturer"
|
||||
),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
},
|
||||
model: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.model"
|
||||
),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
},
|
||||
area: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.area"
|
||||
),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
},
|
||||
integration: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.integration"
|
||||
),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
},
|
||||
battery_entity: {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.battery"
|
||||
),
|
||||
sortable: true,
|
||||
type: "numeric",
|
||||
width: "15%",
|
||||
maxWidth: "90px",
|
||||
template: (
|
||||
batteryEntityPair: DeviceRowData["battery_entity"]
|
||||
) => {
|
||||
const battery =
|
||||
batteryEntityPair && batteryEntityPair[0]
|
||||
? this.hass.states[batteryEntityPair[0]]
|
||||
: undefined;
|
||||
const batteryCharging =
|
||||
batteryEntityPair && batteryEntityPair[1]
|
||||
? this.hass.states[batteryEntityPair[1]]
|
||||
: undefined;
|
||||
return battery && !isNaN(battery.state as any)
|
||||
? html`
|
||||
${battery.state}%
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass!}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryCharging}
|
||||
></ha-battery-icon>
|
||||
`
|
||||
: html` - `;
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
columns.manufacturer = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.manufacturer"
|
||||
),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.model = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.model"),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.area = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.area"),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.integration = {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.integration"
|
||||
),
|
||||
sortable: true,
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
width: "15%",
|
||||
};
|
||||
columns.battery_entity = {
|
||||
title: this.hass.localize("ui.panel.config.devices.data_table.battery"),
|
||||
sortable: true,
|
||||
type: "numeric",
|
||||
width: narrow ? "90px" : "15%",
|
||||
maxWidth: "90px",
|
||||
template: (batteryEntityPair: DeviceRowData["battery_entity"]) => {
|
||||
const battery =
|
||||
batteryEntityPair && batteryEntityPair[0]
|
||||
? this.hass.states[batteryEntityPair[0]]
|
||||
: undefined;
|
||||
const batteryCharging =
|
||||
batteryEntityPair && batteryEntityPair[1]
|
||||
? this.hass.states[batteryEntityPair[1]]
|
||||
: undefined;
|
||||
return battery && !isNaN(battery.state as any)
|
||||
? html`
|
||||
${battery.state}%
|
||||
<ha-battery-icon
|
||||
.hass=${this.hass!}
|
||||
.batteryStateObj=${battery}
|
||||
.batteryChargingStateObj=${batteryCharging}
|
||||
></ha-battery-icon>
|
||||
`
|
||||
: html` - `;
|
||||
},
|
||||
};
|
||||
return columns;
|
||||
}
|
||||
);
|
||||
|
||||
public constructor() {
|
||||
|
||||
@@ -352,6 +352,13 @@ class HaPanelConfig extends HassRouterPage {
|
||||
/* webpackChunkName: "panel-config-mqtt" */ "./integrations/integration-panels/mqtt/mqtt-config-panel"
|
||||
),
|
||||
},
|
||||
ozw: {
|
||||
tag: "ozw-config-router",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "panel-config-ozw" */ "./integrations/integration-panels/ozw/ozw-config-router"
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
ConfigEntry,
|
||||
updateConfigEntry,
|
||||
deleteConfigEntry,
|
||||
reloadConfigEntry,
|
||||
} from "../../../data/config_entries";
|
||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||
@@ -28,7 +29,8 @@ import { haStyle } from "../../../resources/styles";
|
||||
import "../../../components/ha-icon-next";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { mdiDotsVertical, mdiOpenInNew } from "@mdi/js";
|
||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||
|
||||
export interface ConfigEntryUpdatedEvent {
|
||||
entry: ConfigEntry;
|
||||
@@ -55,6 +57,10 @@ const integrationsWithPanel = {
|
||||
buttonLocalizeKey: "ui.panel.config.zha.button",
|
||||
path: "/config/zha/dashboard",
|
||||
},
|
||||
ozw: {
|
||||
buttonLocalizeKey: "ui.panel.config.ozw.button",
|
||||
path: "/config/ozw/dashboard",
|
||||
},
|
||||
zwave: {
|
||||
buttonLocalizeKey: "ui.panel.config.zwave.button",
|
||||
path: "/config/zwave",
|
||||
@@ -224,7 +230,7 @@ export class HaIntegrationCard extends LitElement {
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<mwc-icon-button
|
||||
.title=${this.hass.localize("ui.common.menu")}
|
||||
.label=${this.hass.localize("ui.common.overflow_menu")}
|
||||
@@ -232,7 +238,7 @@ export class HaIntegrationCard extends LitElement {
|
||||
>
|
||||
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item>
|
||||
<mwc-list-item @request-selected="${this._handleSystemOptions}">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.system_options"
|
||||
)}
|
||||
@@ -255,7 +261,17 @@ export class HaIntegrationCard extends LitElement {
|
||||
</mwc-list-item>
|
||||
</a>
|
||||
`}
|
||||
<mwc-list-item class="warning">
|
||||
${item.state === "loaded" && item.supports_unload
|
||||
? html`<mwc-list-item @request-selected="${this._handleReload}">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.reload"
|
||||
)}
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
<mwc-list-item
|
||||
class="warning"
|
||||
@request-selected="${this._handleDelete}"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete"
|
||||
)}
|
||||
@@ -305,17 +321,31 @@ export class HaIntegrationCard extends LitElement {
|
||||
showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry);
|
||||
}
|
||||
|
||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||
const configEntry = ((ev.target as HTMLElement).closest("ha-card") as any)
|
||||
.configEntry;
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._showSystemOptions(configEntry);
|
||||
break;
|
||||
case 1:
|
||||
this._removeIntegration(configEntry);
|
||||
break;
|
||||
private _handleReload(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||
return;
|
||||
}
|
||||
this._reloadIntegration(
|
||||
((ev.target as HTMLElement).closest("ha-card") as any).configEntry
|
||||
);
|
||||
}
|
||||
|
||||
private _handleDelete(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||
return;
|
||||
}
|
||||
this._removeIntegration(
|
||||
((ev.target as HTMLElement).closest("ha-card") as any).configEntry
|
||||
);
|
||||
}
|
||||
|
||||
private _handleSystemOptions(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||
return;
|
||||
}
|
||||
this._showSystemOptions(
|
||||
((ev.target as HTMLElement).closest("ha-card") as any).configEntry
|
||||
);
|
||||
}
|
||||
|
||||
private _showSystemOptions(configEntry: ConfigEntry) {
|
||||
@@ -349,6 +379,21 @@ export class HaIntegrationCard extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private async _reloadIntegration(configEntry: ConfigEntry) {
|
||||
const entryId = configEntry.entry_id;
|
||||
|
||||
reloadConfigEntry(this.hass, entryId).then((result) => {
|
||||
const locale_key = result.require_restart
|
||||
? "reload_restart_confirm"
|
||||
: "reload_confirm";
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
`ui.panel.config.integrations.config_entry.${locale_key}`
|
||||
),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async _editEntryName(ev) {
|
||||
const configEntry = ev.target.closest("ha-card").configEntry;
|
||||
const newName = await showPromptDialog(this, {
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@material/mwc-fab";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-next";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { mdiCircle, mdiCheckCircle, mdiCloseCircle, mdiZWave } from "@mdi/js";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
OZWInstance,
|
||||
fetchOZWInstances,
|
||||
networkOnlineStatuses,
|
||||
networkOfflineStatuses,
|
||||
networkStartingStatuses,
|
||||
} from "../../../../../data/ozw";
|
||||
|
||||
export const ozwTabs: PageNavigation[] = [];
|
||||
|
||||
@customElement("ozw-config-dashboard")
|
||||
class OZWConfigDashboard extends LitElement {
|
||||
@property({ type: Object }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Object }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property() public configEntryId?: string;
|
||||
|
||||
@internalProperty() private _instances: OZWInstance[] = [];
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.hass) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
private async _fetchData() {
|
||||
this._instances = await fetchOZWInstances(this.hass!);
|
||||
if (this._instances.length === 1) {
|
||||
navigate(
|
||||
this,
|
||||
`/config/ozw/network/${this._instances[0].ozw_instance}`,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${ozwTabs}
|
||||
back-path="/config/integrations"
|
||||
>
|
||||
<ha-config-section .narrow=${this.narrow} .isWide=${this.isWide}>
|
||||
<div slot="header">
|
||||
${this.hass.localize("ui.panel.config.ozw.select_instance.header")}
|
||||
</div>
|
||||
|
||||
<div slot="introduction">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.select_instance.introduction"
|
||||
)}
|
||||
</div>
|
||||
${this._instances.length > 0
|
||||
? html`
|
||||
${this._instances.map((instance) => {
|
||||
let status = "unknown";
|
||||
let icon = mdiCircle;
|
||||
if (networkOnlineStatuses.includes(instance.Status)) {
|
||||
status = "online";
|
||||
icon = mdiCheckCircle;
|
||||
}
|
||||
if (networkStartingStatuses.includes(instance.Status)) {
|
||||
status = "starting";
|
||||
}
|
||||
if (networkOfflineStatuses.includes(instance.Status)) {
|
||||
status = "offline";
|
||||
icon = mdiCloseCircle;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<a
|
||||
href="/config/ozw/network/${instance.ozw_instance}"
|
||||
aria-role="option"
|
||||
tabindex="-1"
|
||||
>
|
||||
<paper-icon-item>
|
||||
<ha-svg-icon .path=${mdiZWave} slot="item-icon">
|
||||
</ha-svg-icon>
|
||||
<paper-item-body>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.common.instance"
|
||||
)}
|
||||
${instance.ozw_instance}
|
||||
<div secondary>
|
||||
<ha-svg-icon
|
||||
.path=${icon}
|
||||
class="network-status-icon ${status}"
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.network_status." + status
|
||||
)}
|
||||
-
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.network_status.details." +
|
||||
instance.Status.toLowerCase()
|
||||
)}<br />
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.common.controller"
|
||||
)}
|
||||
: ${instance.getControllerPath}<br />
|
||||
OZWDaemon ${instance.OZWDaemon_Version} (OpenZWave
|
||||
${instance.OpenZWave_Version})
|
||||
</div>
|
||||
</paper-item-body>
|
||||
<ha-icon-next></ha-icon-next>
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
</ha-card>
|
||||
`;
|
||||
})}
|
||||
`
|
||||
: ``}
|
||||
</ha-config-section>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-card:last-child {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
ha-config-section {
|
||||
margin-top: -12px;
|
||||
}
|
||||
:host([narrow]) ha-config-section {
|
||||
margin-top: -20px;
|
||||
}
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
}
|
||||
ha-card a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
paper-item-body {
|
||||
margin: 16px 0;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
position: relative;
|
||||
display: block;
|
||||
outline: 0;
|
||||
}
|
||||
ha-svg-icon.network-status-icon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
.online {
|
||||
color: green;
|
||||
}
|
||||
.starting {
|
||||
color: orange;
|
||||
}
|
||||
.offline {
|
||||
color: red;
|
||||
}
|
||||
ha-svg-icon,
|
||||
ha-icon-next {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.iron-selected paper-item::before,
|
||||
a:not(.iron-selected):focus::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
transition: opacity 15ms linear;
|
||||
will-change: opacity;
|
||||
}
|
||||
a:not(.iron-selected):focus::before {
|
||||
background-color: currentColor;
|
||||
opacity: var(--dark-divider-opacity);
|
||||
}
|
||||
.iron-selected paper-item:focus::before,
|
||||
.iron-selected:focus paper-item::before {
|
||||
opacity: 0.2;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ozw-config-dashboard": OZWConfigDashboard;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
import "@material/mwc-fab";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-next";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { mdiCircle, mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
OZWInstance,
|
||||
fetchOZWNetworkStatus,
|
||||
fetchOZWNetworkStatistics,
|
||||
networkOnlineStatuses,
|
||||
networkOfflineStatuses,
|
||||
networkStartingStatuses,
|
||||
OZWNetworkStatistics,
|
||||
} from "../../../../../data/ozw";
|
||||
|
||||
export const ozwTabs: PageNavigation[] = [];
|
||||
|
||||
@customElement("ozw-config-network")
|
||||
class OZWConfigNetwork extends LitElement {
|
||||
@property({ type: Object }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Object }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property() public configEntryId?: string;
|
||||
|
||||
@property() public ozw_instance = 0;
|
||||
|
||||
@internalProperty() private _network?: OZWInstance;
|
||||
|
||||
@internalProperty() private _statistics?: OZWNetworkStatistics;
|
||||
|
||||
@internalProperty() private _status = "unknown";
|
||||
|
||||
@internalProperty() private _icon = mdiCircle;
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.ozw_instance <= 0) {
|
||||
navigate(this, "/config/ozw/dashboard", true);
|
||||
}
|
||||
if (this.hass) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
private async _fetchData() {
|
||||
this._network = await fetchOZWNetworkStatus(this.hass!, this.ozw_instance);
|
||||
this._statistics = await fetchOZWNetworkStatistics(
|
||||
this.hass!,
|
||||
this.ozw_instance
|
||||
);
|
||||
if (networkOnlineStatuses.includes(this._network.Status)) {
|
||||
this._status = "online";
|
||||
this._icon = mdiCheckCircle;
|
||||
}
|
||||
if (networkStartingStatuses.includes(this._network.Status)) {
|
||||
this._status = "starting";
|
||||
}
|
||||
if (networkOfflineStatuses.includes(this._network.Status)) {
|
||||
this._status = "offline";
|
||||
this._icon = mdiCloseCircle;
|
||||
}
|
||||
}
|
||||
|
||||
private _generateServiceButton(service: string) {
|
||||
return html`
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="ozw"
|
||||
service="${service}"
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.ozw.services." + service)}
|
||||
</ha-call-service-button>
|
||||
`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${ozwTabs}
|
||||
>
|
||||
<ha-config-section .narrow=${this.narrow} .isWide=${this.isWide}>
|
||||
<div slot="header">
|
||||
${this.hass.localize("ui.panel.config.ozw.network.header")}
|
||||
</div>
|
||||
|
||||
<div slot="introduction">
|
||||
${this.hass.localize("ui.panel.config.ozw.network.introduction")}
|
||||
</div>
|
||||
${this._network
|
||||
? html`
|
||||
<ha-card class="content network-status">
|
||||
<div class="card-content">
|
||||
<div class="details">
|
||||
<ha-svg-icon
|
||||
.path=${this._icon}
|
||||
class="network-status-icon ${this._status}"
|
||||
slot="item-icon"
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.common.network"
|
||||
)}
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.network_status." + this._status
|
||||
)}
|
||||
<br />
|
||||
<small>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.network_status.details." +
|
||||
this._network.Status.toLowerCase()
|
||||
)}
|
||||
</small>
|
||||
</div>
|
||||
<div class="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.common.ozw_instance"
|
||||
)}
|
||||
${this._network.ozw_instance}
|
||||
${this._statistics
|
||||
? html`
|
||||
•
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.network.node_count",
|
||||
"count",
|
||||
this._statistics.node_count
|
||||
)}
|
||||
`
|
||||
: ``}
|
||||
<br />
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.ozw.common.controller"
|
||||
)}:
|
||||
${this._network.getControllerPath}<br />
|
||||
OZWDaemon ${this._network.OZWDaemon_Version} (OpenZWave
|
||||
${this._network.OpenZWave_Version})
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
${this._generateServiceButton("add_node")}
|
||||
${this._generateServiceButton("remove_node")}
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: ``}
|
||||
</ha-config-section>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.secondary {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.online {
|
||||
color: green;
|
||||
}
|
||||
.starting {
|
||||
color: orange;
|
||||
}
|
||||
.offline {
|
||||
color: red;
|
||||
}
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.sectionHeader {
|
||||
position: relative;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.network-status {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.network-status div.details {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.network-status ha-svg-icon {
|
||||
display: block;
|
||||
margin: 0px auto 16px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.network-status small {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
margin: 0 auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
padding: 0 8px 12px;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ozw-config-network": OZWConfigNetwork;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { customElement, property } from "lit-element";
|
||||
import {
|
||||
HassRouterPage,
|
||||
RouterOptions,
|
||||
} from "../../../../../layouts/hass-router-page";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
|
||||
@customElement("ozw-config-router")
|
||||
class OZWConfigRouter extends HassRouterPage {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public isWide!: boolean;
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
private _configEntry = new URLSearchParams(window.location.search).get(
|
||||
"config_entry"
|
||||
);
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "dashboard",
|
||||
showLoading: true,
|
||||
routes: {
|
||||
dashboard: {
|
||||
tag: "ozw-config-dashboard",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "ozw-config-dashboard" */ "./ozw-config-dashboard"
|
||||
),
|
||||
},
|
||||
network: {
|
||||
tag: "ozw-config-network",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "ozw-config-network" */ "./ozw-config-network"
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
protected updatePageEl(el): void {
|
||||
el.route = this.routeTail;
|
||||
el.hass = this.hass;
|
||||
el.isWide = this.isWide;
|
||||
el.narrow = this.narrow;
|
||||
el.configEntryId = this._configEntry;
|
||||
if (this._currentPage === "network") {
|
||||
el.ozw_instance = this.routeTail.path.substr(1);
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if (this._configEntry && !searchParams.has("config_entry")) {
|
||||
searchParams.append("config_entry", this._configEntry);
|
||||
navigate(
|
||||
this,
|
||||
`${this.routeTail.prefix}${
|
||||
this.routeTail.path
|
||||
}?${searchParams.toString()}`,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ozw-config-router": OZWConfigRouter;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { HomeAssistant, Route } from "../../../types";
|
||||
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { isServiceLoaded } from "../../../common/config/is_service_loaded";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import "../../../components/ha-card";
|
||||
@@ -35,6 +36,20 @@ const reloadableDomains = [
|
||||
"input_number",
|
||||
"input_datetime",
|
||||
"input_select",
|
||||
"template",
|
||||
"universal",
|
||||
"rest",
|
||||
"command_line",
|
||||
"filter",
|
||||
"statistics",
|
||||
"generic",
|
||||
"generic_thermostat",
|
||||
"homekit",
|
||||
"min_max",
|
||||
"history_stats",
|
||||
"trend",
|
||||
"ping",
|
||||
"filesize",
|
||||
];
|
||||
|
||||
@customElement("ha-config-server-control")
|
||||
@@ -164,18 +179,20 @@ export class HaConfigServerControl extends LitElement {
|
||||
"ui.panel.config.server_control.section.server_management.restart"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
class="warning"
|
||||
.hass=${this.hass}
|
||||
domain="homeassistant"
|
||||
service="stop"
|
||||
confirmation=${this.hass.localize(
|
||||
"ui.panel.config.server_control.section.server_management.confirm_stop"
|
||||
)}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.server_control.section.server_management.stop"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
${!isComponentLoaded(this.hass, "hassio")
|
||||
? html` <ha-call-service-button
|
||||
class="warning"
|
||||
.hass=${this.hass}
|
||||
domain="homeassistant"
|
||||
service="stop"
|
||||
confirmation=${this.hass.localize(
|
||||
"ui.panel.config.server_control.section.server_management.confirm_stop"
|
||||
)}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.server_control.section.server_management.stop"
|
||||
)}
|
||||
</ha-call-service-button>`
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
@@ -202,7 +219,7 @@ export class HaConfigServerControl extends LitElement {
|
||||
</ha-call-service-button>
|
||||
</div>
|
||||
${reloadableDomains.map((domain) =>
|
||||
isComponentLoaded(this.hass, domain)
|
||||
isServiceLoaded(this.hass, domain, "reload")
|
||||
? html`<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
|
||||
@@ -3,23 +3,23 @@ import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/ha-formfield";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/map/ha-location-editor";
|
||||
import { Tag, UpdateTagParams } from "../../../data/tag";
|
||||
import { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||
import { TagDetailDialogParams } from "./show-dialog-tag-detail";
|
||||
import { UpdateTagParams, Tag } from "../../../data/tag";
|
||||
|
||||
@customElement("dialog-tag-detail")
|
||||
class DialogTagDetail extends LitElement implements HassDialog {
|
||||
@@ -162,7 +162,7 @@ class DialogTagDetail extends LitElement implements HassDialog {
|
||||
} else {
|
||||
newValue = await this._params!.createEntry(values, this._id);
|
||||
}
|
||||
this._params = undefined;
|
||||
this.closeDialog();
|
||||
} catch (err) {
|
||||
this._error = err ? err.message : "Unknown error";
|
||||
} finally {
|
||||
@@ -172,11 +172,12 @@ class DialogTagDetail extends LitElement implements HassDialog {
|
||||
}
|
||||
|
||||
private async _updateWriteEntry() {
|
||||
const openWrite = this._params?.openWrite;
|
||||
const tag = await this._updateEntry();
|
||||
if (!tag) {
|
||||
if (!tag || !openWrite) {
|
||||
return;
|
||||
}
|
||||
this._params?.openWrite!(tag);
|
||||
openWrite(tag);
|
||||
}
|
||||
|
||||
private async _deleteEntry() {
|
||||
|
||||
@@ -12,6 +12,7 @@ import memoizeOne from "memoize-one";
|
||||
import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-relative-time";
|
||||
import { showAutomationEditor, TagTrigger } from "../../../data/automation";
|
||||
import {
|
||||
createTag,
|
||||
deleteTag,
|
||||
@@ -23,14 +24,13 @@ import {
|
||||
UpdateTagParams,
|
||||
} from "../../../data/tag";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { getExternalConfig } from "../../../external_app/external_config";
|
||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showTagDetailDialog } from "./show-dialog-tag-detail";
|
||||
import "./tag-image";
|
||||
import { getExternalConfig } from "../../../external_app/external_config";
|
||||
import { showAutomationEditor, TagTrigger } from "../../../data/automation";
|
||||
|
||||
export interface TagRowData extends Tag {
|
||||
last_scanned_datetime: Date | null;
|
||||
@@ -70,12 +70,12 @@ export class HaConfigTags extends SubscribeMixin(LitElement) {
|
||||
template: (name, tag: any) => html`${name}
|
||||
${narrow
|
||||
? html`<div class="secondary">
|
||||
${tag.last_scanned
|
||||
${tag.last_scanned_datetime
|
||||
? html`<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetimeObj=${tag.last_scanned_datetime}
|
||||
></ha-relative-time>`
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
: this.hass.localize("ui.panel.config.tags.never_scanned")}
|
||||
</div>`
|
||||
: ""}`,
|
||||
},
|
||||
@@ -94,7 +94,7 @@ export class HaConfigTags extends SubscribeMixin(LitElement) {
|
||||
.hass=${this.hass}
|
||||
.datetimeObj=${last_scanned_datetime}
|
||||
></ha-relative-time>`
|
||||
: this.hass.localize("ui.components.relative_time.never")}
|
||||
: this.hass.localize("ui.panel.config.tags.never_scanned")}
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import "../../../components/ha-dialog";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/ha-formfield";
|
||||
import "../../../components/ha-switch";
|
||||
import { createAuthForUser } from "../../../data/auth";
|
||||
import {
|
||||
createUser,
|
||||
@@ -27,7 +28,6 @@ import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { AddUserDialogParams } from "./show-dialog-add-user";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
|
||||
@customElement("dialog-add-user")
|
||||
export class DialogAddUser extends LitElement {
|
||||
@@ -46,6 +46,8 @@ export class DialogAddUser extends LitElement {
|
||||
|
||||
@internalProperty() private _password?: string;
|
||||
|
||||
@internalProperty() private _passwordConfirm?: string;
|
||||
|
||||
@internalProperty() private _isAdmin?: boolean;
|
||||
|
||||
public showDialog(params: AddUserDialogParams) {
|
||||
@@ -53,6 +55,7 @@ export class DialogAddUser extends LitElement {
|
||||
this._name = "";
|
||||
this._username = "";
|
||||
this._password = "";
|
||||
this._passwordConfirm = "";
|
||||
this._isAdmin = false;
|
||||
this._error = undefined;
|
||||
this._loading = false;
|
||||
@@ -83,17 +86,20 @@ export class DialogAddUser extends LitElement {
|
||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||
<paper-input
|
||||
class="name"
|
||||
name="name"
|
||||
.label=${this.hass.localize("ui.panel.config.users.add_user.name")}
|
||||
.value=${this._name}
|
||||
required
|
||||
auto-validate
|
||||
autocapitalize="on"
|
||||
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
||||
@value-changed=${this._nameChanged}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
@blur=${this._maybePopulateUsername}
|
||||
></paper-input>
|
||||
|
||||
<paper-input
|
||||
class="username"
|
||||
name="username"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.users.add_user.username"
|
||||
)}
|
||||
@@ -101,20 +107,40 @@ export class DialogAddUser extends LitElement {
|
||||
required
|
||||
auto-validate
|
||||
autocapitalize="none"
|
||||
@value-changed=${this._usernameChanged}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
||||
></paper-input>
|
||||
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.users.add_user.password"
|
||||
)}
|
||||
type="password"
|
||||
name="password"
|
||||
.value=${this._password}
|
||||
required
|
||||
auto-validate
|
||||
@value-changed=${this._passwordChanged}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
.errorMessage=${this.hass.localize("ui.common.error_required")}
|
||||
></paper-input>
|
||||
|
||||
<paper-input
|
||||
label="${this.hass.localize(
|
||||
"ui.panel.config.users.add_user.password_confirm"
|
||||
)}"
|
||||
name="passwordConfirm"
|
||||
.value=${this._passwordConfirm}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
required
|
||||
type="password"
|
||||
.invalid=${this._password !== "" &&
|
||||
this._passwordConfirm !== "" &&
|
||||
this._passwordConfirm !== this._password}
|
||||
.errorMessage="${this.hass.localize(
|
||||
"ui.panel.config.users.add_user.password_not_match"
|
||||
)}"
|
||||
></paper-input>
|
||||
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize("ui.panel.config.users.editor.admin")}
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
@@ -147,7 +173,10 @@ export class DialogAddUser extends LitElement {
|
||||
: html`
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
.disabled=${!this._name || !this._username || !this._password}
|
||||
.disabled=${!this._name ||
|
||||
!this._username ||
|
||||
!this._password ||
|
||||
this._password !== this._passwordConfirm}
|
||||
@click=${this._createUser}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.users.add_user.create")}
|
||||
@@ -173,19 +202,10 @@ export class DialogAddUser extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _nameChanged(ev: PolymerChangedEvent<string>) {
|
||||
private _handleValueChanged(ev: PolymerChangedEvent<string>): void {
|
||||
this._error = undefined;
|
||||
this._name = ev.detail.value;
|
||||
}
|
||||
|
||||
private _usernameChanged(ev: PolymerChangedEvent<string>) {
|
||||
this._error = undefined;
|
||||
this._username = ev.detail.value;
|
||||
}
|
||||
|
||||
private _passwordChanged(ev: PolymerChangedEvent<string>) {
|
||||
this._error = undefined;
|
||||
this._password = ev.detail.value;
|
||||
const name = (ev.target as any).name;
|
||||
this[`_${name}`] = ev.detail.value;
|
||||
}
|
||||
|
||||
private async _adminChanged(ev): Promise<void> {
|
||||
|
||||
@@ -6,23 +6,28 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/ha-formfield";
|
||||
import "../../../components/ha-switch";
|
||||
import { adminChangePassword } from "../../../data/auth";
|
||||
import {
|
||||
SYSTEM_GROUP_ID_ADMIN,
|
||||
SYSTEM_GROUP_ID_USER,
|
||||
} from "../../../data/user";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../dialogs/generic/show-dialog-box";
|
||||
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { UserDetailDialogParams } from "./show-dialog-user-detail";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
|
||||
@customElement("dialog-user-detail")
|
||||
class DialogUserDetail extends LitElement {
|
||||
@@ -141,7 +146,15 @@ class DialogUserDetail extends LitElement {
|
||||
</paper-tooltip>
|
||||
`
|
||||
: ""}
|
||||
${!user.system_generated && this.hass.user?.is_owner
|
||||
? html`<mwc-button @click=${this._changePassword}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.users.editor.change_password"
|
||||
)}
|
||||
</mwc-button>`
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
<div slot="primaryAction">
|
||||
<mwc-button
|
||||
@click=${this._updateEntry}
|
||||
@@ -202,6 +215,52 @@ class DialogUserDetail extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _changePassword() {
|
||||
const credential = this._params?.entry.credentials.find(
|
||||
(cred) => cred.type === "homeassistant"
|
||||
);
|
||||
if (!credential) {
|
||||
showAlertDialog(this, {
|
||||
title: "No Home Assistant credentials found.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const newPassword = await showPromptDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.users.editor.change_password"),
|
||||
inputType: "password",
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.users.editor.new_password"
|
||||
),
|
||||
});
|
||||
if (!newPassword) {
|
||||
return;
|
||||
}
|
||||
const confirmPassword = await showPromptDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.users.editor.change_password"),
|
||||
inputType: "password",
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.users.add_user.password_confirm"
|
||||
),
|
||||
});
|
||||
if (!confirmPassword) {
|
||||
return;
|
||||
}
|
||||
if (newPassword !== confirmPassword) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.users.add_user.password_not_match"
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
await adminChangePassword(this.hass, this._params!.entry.id, newPassword);
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.users.add_user.password_changed"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._params = undefined;
|
||||
}
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
import "../../../components/ha-circular-progress";
|
||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
||||
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "../../../components/ha-code-editor";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
import "../../../styles/polymer-ha-style";
|
||||
|
||||
class HaPanelDevTemplate extends LocalizeMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="ha-style iron-flex iron-positioning"></style>
|
||||
<style>
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 16px;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.edit-pane {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.edit-pane a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.horizontal .edit-pane {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.render-pane {
|
||||
position: relative;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.render-spinner {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.rendered {
|
||||
@apply --paper-font-code1;
|
||||
clear: both;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.rendered.error {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class$="[[computeFormClasses(narrow)]]">
|
||||
<div class="edit-pane">
|
||||
<p>
|
||||
[[localize('ui.panel.developer-tools.tabs.templates.description')]]
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="http://jinja.pocoo.org/docs/dev/templates/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.developer-tools.tabs.templates.jinja_documentation')]]</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://home-assistant.io/docs/configuration/templating/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>[[localize('ui.panel.developer-tools.tabs.templates.template_extensions')]]</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<p>[[localize('ui.panel.developer-tools.tabs.templates.editor')]]</p>
|
||||
<ha-code-editor
|
||||
mode="jinja2"
|
||||
value="[[template]]"
|
||||
error="[[error]]"
|
||||
autofocus
|
||||
on-value-changed="templateChanged"
|
||||
></ha-code-editor>
|
||||
</div>
|
||||
|
||||
<div class="render-pane">
|
||||
<ha-circular-progress
|
||||
class="render-spinner"
|
||||
active="[[rendering]]"
|
||||
size="small"
|
||||
></ha-circular-progress>
|
||||
<pre class$="[[computeRenderedClasses(error)]]">[[processed]]</pre>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
error: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
rendering: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
template: {
|
||||
type: String,
|
||||
/* eslint-disable max-len */
|
||||
value: `Imitate available variables:
|
||||
{% set my_test_json = {
|
||||
"temperature": 25,
|
||||
"unit": "°C"
|
||||
} %}
|
||||
|
||||
The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}.
|
||||
|
||||
{% if is_state("device_tracker.paulus", "home") and
|
||||
is_state("device_tracker.anne_therese", "home") -%}
|
||||
You are both home, you silly
|
||||
{%- else -%}
|
||||
Anne Therese is at {{ states("device_tracker.anne_therese") }}
|
||||
Paulus is at {{ states("device_tracker.paulus") }}
|
||||
{%- endif %}
|
||||
|
||||
For loop example:
|
||||
{% for state in states.sensor -%}
|
||||
{%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%}
|
||||
{{ state.name | lower }} is {{state.state_with_unit}}
|
||||
{%- endfor %}.`,
|
||||
/* eslint-enable max-len */
|
||||
},
|
||||
|
||||
processed: {
|
||||
type: String,
|
||||
value: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ready() {
|
||||
super.ready();
|
||||
this.renderTemplate();
|
||||
}
|
||||
|
||||
computeFormClasses(narrow) {
|
||||
return narrow ? "content" : "content layout horizontal";
|
||||
}
|
||||
|
||||
computeRenderedClasses(error) {
|
||||
return error ? "error rendered" : "rendered";
|
||||
}
|
||||
|
||||
templateChanged(ev) {
|
||||
this.template = ev.detail.value;
|
||||
if (this.error) {
|
||||
this.error = false;
|
||||
}
|
||||
this._debouncer = Debouncer.debounce(
|
||||
this._debouncer,
|
||||
timeOut.after(500),
|
||||
() => {
|
||||
this.renderTemplate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
renderTemplate() {
|
||||
this.rendering = true;
|
||||
|
||||
this.hass.callApi("POST", "template", { template: this.template }).then(
|
||||
function (processed) {
|
||||
this.processed = processed;
|
||||
this.rendering = false;
|
||||
}.bind(this),
|
||||
function (error) {
|
||||
this.processed =
|
||||
(error && error.body && error.body.message) ||
|
||||
this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.templates.unknown_error_template"
|
||||
);
|
||||
this.error = true;
|
||||
this.rendering = false;
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("developer-tools-template", HaPanelDevTemplate);
|
||||
@@ -0,0 +1,280 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import "../../../components/ha-code-editor";
|
||||
import { subscribeRenderTemplate } from "../../../data/ws-templates";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
const DEMO_TEMPLATE = `{## Imitate available variables: ##}
|
||||
{% set my_test_json = {
|
||||
"temperature": 25,
|
||||
"unit": "°C"
|
||||
} %}
|
||||
|
||||
The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}.
|
||||
|
||||
{% if is_state("sun.sun", "above_horizon") -%}
|
||||
The sun rose {{ relative_time(states.sun.sun.last_changed) }} ago.
|
||||
{%- else -%}
|
||||
The sun will rise at {{ as_timestamp(strptime(state_attr("sun.sun", "next_rising"), "")) | timestamp_local }}.
|
||||
{%- endif %}
|
||||
|
||||
For loop example getting 3 entity values:
|
||||
|
||||
{% for states in states | slice(3) -%}
|
||||
{% set state = states | first %}
|
||||
{%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%}
|
||||
{{ state.name | lower }} is {{state.state_with_unit}}
|
||||
{%- endfor %}.`;
|
||||
|
||||
@customElement("developer-tools-template")
|
||||
class HaPanelDevTemplate extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
@internalProperty() private _error = false;
|
||||
|
||||
@internalProperty() private _rendering = false;
|
||||
|
||||
@internalProperty() private _processed = "";
|
||||
|
||||
@internalProperty() private _unsubRenderTemplate?: Promise<UnsubscribeFunc>;
|
||||
|
||||
private _template = "";
|
||||
|
||||
private _inited = false;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this._template && !this._unsubRenderTemplate) {
|
||||
this._subscribeTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
this._unsubscribeTemplate();
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
if (localStorage && localStorage["panel-dev-template-template"]) {
|
||||
this._template = localStorage["panel-dev-template-template"];
|
||||
} else {
|
||||
this._template = DEMO_TEMPLATE;
|
||||
}
|
||||
this._subscribeTemplate();
|
||||
this._inited = true;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<div
|
||||
class="content ${classMap({
|
||||
layout: !this.narrow,
|
||||
horizontal: !this.narrow,
|
||||
})}"
|
||||
>
|
||||
<div class="edit-pane">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.templates.description"
|
||||
)}
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="http://jinja.pocoo.org/docs/dev/templates/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.templates.jinja_documentation"
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://home-assistant.io/docs/configuration/templating/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.templates.template_extensions"
|
||||
)}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.templates.editor"
|
||||
)}
|
||||
</p>
|
||||
<ha-code-editor
|
||||
mode="jinja2"
|
||||
.value=${this._template}
|
||||
.error=${this._error}
|
||||
autofocus
|
||||
@value-changed=${this._templateChanged}
|
||||
></ha-code-editor>
|
||||
<mwc-button @click=${this._restoreDemo}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.templates.reset"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
|
||||
<div class="render-pane">
|
||||
<ha-circular-progress
|
||||
class="render-spinner"
|
||||
.active=${this._rendering}
|
||||
size="small"
|
||||
></ha-circular-progress>
|
||||
<pre class="rendered ${classMap({ error: this._error })}">
|
||||
${this._processed}</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 16px;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.edit-pane {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.edit-pane a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.horizontal .edit-pane {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.render-pane {
|
||||
position: relative;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.render-spinner {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.rendered {
|
||||
@apply --paper-font-code1;
|
||||
clear: both;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.rendered.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _debounceRender = debounce(
|
||||
() => {
|
||||
this._subscribeTemplate();
|
||||
this._storeTemplate();
|
||||
},
|
||||
500,
|
||||
false
|
||||
);
|
||||
|
||||
private _templateChanged(ev) {
|
||||
this._template = ev.detail.value;
|
||||
if (this._error) {
|
||||
this._error = false;
|
||||
}
|
||||
this._debounceRender();
|
||||
}
|
||||
|
||||
private async _subscribeTemplate() {
|
||||
this._rendering = true;
|
||||
await this._unsubscribeTemplate();
|
||||
try {
|
||||
this._unsubRenderTemplate = subscribeRenderTemplate(
|
||||
this.hass.connection,
|
||||
(result) => {
|
||||
this._processed = result;
|
||||
},
|
||||
{
|
||||
template: this._template,
|
||||
}
|
||||
);
|
||||
await this._unsubRenderTemplate;
|
||||
} catch (err) {
|
||||
this._error = true;
|
||||
if (err.message) {
|
||||
this._processed = err.message;
|
||||
}
|
||||
this._unsubRenderTemplate = undefined;
|
||||
} finally {
|
||||
this._rendering = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async _unsubscribeTemplate(): Promise<void> {
|
||||
if (!this._unsubRenderTemplate) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const unsub = await this._unsubRenderTemplate;
|
||||
unsub();
|
||||
this._unsubRenderTemplate = undefined;
|
||||
} catch (e) {
|
||||
if (e.code === "not_found") {
|
||||
// If we get here, the connection was probably already closed. Ignore.
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _storeTemplate() {
|
||||
if (!this._inited) {
|
||||
return;
|
||||
}
|
||||
localStorage["panel-dev-template-template"] = this._template;
|
||||
}
|
||||
|
||||
private _restoreDemo() {
|
||||
this._template = DEMO_TEMPLATE;
|
||||
this._subscribeTemplate();
|
||||
delete localStorage["panel-dev-template-template"];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"developer-tools-template": HaPanelDevTemplate;
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,26 @@ class HaLogbook extends LitElement {
|
||||
? ` (${item_username})`
|
||||
: ``}</span
|
||||
>
|
||||
${!item.context_event_type
|
||||
? ""
|
||||
: item.context_event_type === "call_service"
|
||||
? // Service Call
|
||||
html` by service ${item.context_domain}.${item.context_service}`
|
||||
: item.context_entity_id === item.entity_id
|
||||
? // HomeKit or something that self references
|
||||
html` by
|
||||
${item.context_name
|
||||
? item.context_name
|
||||
: item.context_event_type}`
|
||||
: // Another entity such as an automation or script
|
||||
html` by
|
||||
<a
|
||||
href="#"
|
||||
@click=${this._entityClicked}
|
||||
.entityId=${item.context_entity_id}
|
||||
class="name"
|
||||
>${item.context_entity_id_name}</a
|
||||
>`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
} from "lit-element";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../calendar/ha-full-calendar";
|
||||
|
||||
import type {
|
||||
HomeAssistant,
|
||||
CalendarEvent,
|
||||
Calendar,
|
||||
CalendarViewChanged,
|
||||
FullCalendarView,
|
||||
} from "../../../types";
|
||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import type { CalendarCardConfig } from "./types";
|
||||
import { findEntities } from "../common/find-entites";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import "../components/hui-warning";
|
||||
import { fetchCalendarEvents } from "../../../data/calendar";
|
||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { HA_COLOR_PALETTE } from "../../../common/const";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import { installResizeObserver } from "../common/install-resize-observer";
|
||||
|
||||
@customElement("hui-calendar-card")
|
||||
export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import(
|
||||
/* webpackChunkName: "hui-calendar-card-editor" */ "../editor/config-elements/hui-calendar-card-editor"
|
||||
);
|
||||
return document.createElement("hui-calendar-card-editor");
|
||||
}
|
||||
|
||||
public static getStubConfig(
|
||||
hass: HomeAssistant,
|
||||
entities: string[],
|
||||
entitiesFill: string[]
|
||||
) {
|
||||
const includeDomains = ["calendar"];
|
||||
const maxEntities = 2;
|
||||
const foundEntities = findEntities(
|
||||
hass,
|
||||
maxEntities,
|
||||
entities,
|
||||
entitiesFill,
|
||||
includeDomains
|
||||
);
|
||||
|
||||
return {
|
||||
entities: foundEntities,
|
||||
};
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public _events: CalendarEvent[] = [];
|
||||
|
||||
@internalProperty() private _config?: CalendarCardConfig;
|
||||
|
||||
@internalProperty() private _calendars: Calendar[] = [];
|
||||
|
||||
@internalProperty() private _narrow = false;
|
||||
|
||||
@internalProperty() private _veryNarrow = false;
|
||||
|
||||
private _resizeObserver?: ResizeObserver;
|
||||
|
||||
public setConfig(config: CalendarCardConfig): void {
|
||||
if (!config.entities) {
|
||||
throw new Error("Entities must be defined");
|
||||
}
|
||||
|
||||
if (config.entities && !Array.isArray(config.entities)) {
|
||||
throw new Error("Entities need to be an array");
|
||||
}
|
||||
|
||||
this._calendars = config!.entities.map((entity, idx) => {
|
||||
return {
|
||||
entity_id: entity,
|
||||
backgroundColor: `#${HA_COLOR_PALETTE[idx % HA_COLOR_PALETTE.length]}`,
|
||||
};
|
||||
});
|
||||
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.updateComplete.then(() => this._attachObserver());
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
if (this._resizeObserver) {
|
||||
this._resizeObserver.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._config || !this.hass || !this._calendars.length) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const views: FullCalendarView[] = this._veryNarrow
|
||||
? ["listWeek"]
|
||||
: ["listWeek", "dayGridMonth", "dayGridDay"];
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="header">${this._config.title}</div>
|
||||
<ha-full-calendar
|
||||
.narrow=${this._narrow}
|
||||
.events=${this._events}
|
||||
.hass=${this.hass}
|
||||
.views=${views}
|
||||
@view-changed=${this._handleViewChanged}
|
||||
></ha-full-calendar>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
const oldConfig = changedProps.get("_config") as
|
||||
| CalendarCardConfig
|
||||
| undefined;
|
||||
|
||||
if (
|
||||
!oldHass ||
|
||||
!oldConfig ||
|
||||
(changedProps.has("hass") && oldHass.themes !== this.hass.themes) ||
|
||||
(changedProps.has("_config") && oldConfig.theme !== this._config.theme)
|
||||
) {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config!.theme);
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleViewChanged(
|
||||
ev: HASSDomEvent<CalendarViewChanged>
|
||||
): Promise<void> {
|
||||
this._events = await fetchCalendarEvents(
|
||||
this.hass!,
|
||||
ev.detail.start,
|
||||
ev.detail.end,
|
||||
this._calendars
|
||||
);
|
||||
}
|
||||
|
||||
private _measureCard() {
|
||||
const card = this.shadowRoot!.querySelector("ha-card");
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
this._narrow = card.offsetWidth < 870;
|
||||
this._veryNarrow = card.offsetWidth < 350;
|
||||
}
|
||||
|
||||
private async _attachObserver(): Promise<void> {
|
||||
if (!this._resizeObserver) {
|
||||
await installResizeObserver();
|
||||
this._resizeObserver = new ResizeObserver(
|
||||
debounce(() => this._measureCard(), 250, false)
|
||||
);
|
||||
}
|
||||
const card = this.shadowRoot!.querySelector("ha-card");
|
||||
// If we show an error or warning there is no ha-card
|
||||
if (!card) {
|
||||
return;
|
||||
}
|
||||
this._resizeObserver.observe(card);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
ha-card {
|
||||
position: relative;
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
.header {
|
||||
color: var(--ha-card-header-color, --primary-text-color);
|
||||
font-size: var(--ha-card-header-font-size, 24px);
|
||||
line-height: 1.2;
|
||||
padding-top: 16px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-calendar-card": HuiCalendarCard;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import { processConfigEntities } from "../common/process-config-entities";
|
||||
import { EntityConfig } from "../entity-rows/types";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { HistoryGraphCardConfig } from "./types";
|
||||
import { HistoryResult } from "../../../data/history";
|
||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
||||
|
||||
@customElement("hui-history-graph-card")
|
||||
export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
@@ -49,7 +51,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@internalProperty() private _stateHistory?: any;
|
||||
@internalProperty() private _stateHistory?: HistoryResult;
|
||||
|
||||
@internalProperty() private _config?: HistoryGraphCardConfig;
|
||||
|
||||
@@ -59,10 +61,10 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
|
||||
private _cacheConfig?: CacheConfig;
|
||||
|
||||
private _interval?: number;
|
||||
|
||||
private _fetching = false;
|
||||
|
||||
private _date?: Date;
|
||||
|
||||
public getCardSize(): number {
|
||||
return 4;
|
||||
}
|
||||
@@ -97,9 +99,8 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
};
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._clearInterval();
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
return hasConfigOrEntitiesChanged(this, changedProps);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
@@ -108,21 +109,19 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!changedProps.has("_config")) {
|
||||
if (!changedProps.has("_config") && !changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldConfig = changedProps.get("_config") as HistoryGraphCardConfig;
|
||||
|
||||
if (oldConfig !== this._config) {
|
||||
if (changedProps.has("_config") && oldConfig !== this._config) {
|
||||
this._getStateHistory();
|
||||
} else if (
|
||||
this._cacheConfig.refresh &&
|
||||
Date.now() - this._date!.getTime() >= this._cacheConfig.refresh * 100
|
||||
) {
|
||||
this._getStateHistory();
|
||||
this._clearInterval();
|
||||
|
||||
if (!this._interval && this._cacheConfig.refresh) {
|
||||
this._interval = window.setInterval(() => {
|
||||
this._getStateHistory();
|
||||
}, this._cacheConfig.refresh * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,27 +154,23 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
if (this._fetching) {
|
||||
return;
|
||||
}
|
||||
this._date = new Date();
|
||||
this._fetching = true;
|
||||
try {
|
||||
this._stateHistory = await getRecentWithCache(
|
||||
this.hass!,
|
||||
this._cacheConfig!.cacheKey,
|
||||
this._cacheConfig!,
|
||||
this.hass!.localize,
|
||||
this.hass!.language
|
||||
);
|
||||
this._stateHistory = {
|
||||
...(await getRecentWithCache(
|
||||
this.hass!,
|
||||
this._cacheConfig!.cacheKey,
|
||||
this._cacheConfig!,
|
||||
this.hass!.localize,
|
||||
this.hass!.language
|
||||
)),
|
||||
};
|
||||
} finally {
|
||||
this._fetching = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _clearInterval(): void {
|
||||
if (this._interval) {
|
||||
window.clearInterval(this._interval);
|
||||
this._interval = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
.content {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "../../../components/ha-icon-button";
|
||||
import "@polymer/paper-progress/paper-progress";
|
||||
import type { PaperProgressElement } from "@polymer/paper-progress/paper-progress";
|
||||
import {
|
||||
@@ -6,9 +5,9 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
property,
|
||||
internalProperty,
|
||||
PropertyValues,
|
||||
query,
|
||||
TemplateResult,
|
||||
@@ -25,12 +24,17 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
|
||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
||||
import {
|
||||
computeMediaDescription,
|
||||
CONTRAST_RATIO,
|
||||
ControlButton,
|
||||
getCurrentProgress,
|
||||
MediaPickedEvent,
|
||||
SUPPORTS_PLAY,
|
||||
SUPPORT_BROWSE_MEDIA,
|
||||
SUPPORT_NEXT_TRACK,
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
@@ -38,17 +42,16 @@ import {
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_TURN_OFF,
|
||||
SUPPORT_TURN_ON,
|
||||
ControlButton,
|
||||
} from "../../../data/media-player";
|
||||
import type { HomeAssistant, MediaEntity } from "../../../types";
|
||||
import { contrast } from "../common/color/contrast";
|
||||
import { findEntities } from "../common/find-entites";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
import "../components/hui-marquee";
|
||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import { MediaControlCardConfig } from "./types";
|
||||
import { installResizeObserver } from "../common/install-resize-observer";
|
||||
import "../components/hui-marquee";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { MediaControlCardConfig } from "./types";
|
||||
|
||||
function getContrastRatio(
|
||||
rgb1: [number, number, number],
|
||||
@@ -185,7 +188,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
return { type: "media-control", entity: foundEntities[0] || "" };
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@internalProperty() private _config?: MediaControlCardConfig;
|
||||
|
||||
@@ -389,12 +392,27 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
${controls!.map(
|
||||
(control) => html`
|
||||
<ha-icon-button
|
||||
.title=${this.hass.localize(
|
||||
`ui.card.media_player.${control.action}`
|
||||
)}
|
||||
.icon=${control.icon}
|
||||
action=${control.action}
|
||||
@click=${this._handleClick}
|
||||
></ha-icon-button>
|
||||
`
|
||||
)}
|
||||
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
|
||||
? html`
|
||||
<ha-icon-button
|
||||
class="browse-media"
|
||||
icon="hass:folder-multiple"
|
||||
.title=${this.hass.localize(
|
||||
"ui.card.media_player.browse_media"
|
||||
)}
|
||||
@click=${this._handleBrowseMedia}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
@@ -643,14 +661,31 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleBrowseMedia(): void {
|
||||
showMediaBrowserDialog(this, {
|
||||
action: "play",
|
||||
entityId: this._config!.entity,
|
||||
mediaPickedCallback: (pickedMedia: MediaPickedEvent) =>
|
||||
this._playMedia(
|
||||
pickedMedia.media_content_id,
|
||||
pickedMedia.media_content_type
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
private _playMedia(media_content_id: string, media_content_type: string) {
|
||||
this.hass!.callService("media_player", "play_media", {
|
||||
entity_id: this._config!.entity,
|
||||
media_content_id,
|
||||
media_content_type,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleClick(e: MouseEvent): void {
|
||||
this.hass!.callService(
|
||||
"media_player",
|
||||
(e.currentTarget! as HTMLElement).getAttribute("action")!,
|
||||
{
|
||||
entity_id: this._config!.entity,
|
||||
}
|
||||
);
|
||||
const action = (e.currentTarget! as HTMLElement).getAttribute("action")!;
|
||||
this.hass!.callService("media_player", action, {
|
||||
entity_id: this._config!.entity,
|
||||
});
|
||||
}
|
||||
|
||||
private _updateProgressBar(): void {
|
||||
@@ -831,6 +866,12 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
--mdc-icon-size: 40px;
|
||||
}
|
||||
|
||||
ha-icon-button.browse-media {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
--mdc-icon-size: 24px;
|
||||
}
|
||||
|
||||
.top-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -900,6 +941,10 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
--mdc-icon-size: 36px;
|
||||
}
|
||||
|
||||
.narrow ha-icon-button.browse-media {
|
||||
--mdc-icon-size: 24px;
|
||||
}
|
||||
|
||||
.no-progress.player:not(.no-controls) {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ export interface AlarmPanelCardConfig extends LovelaceCardConfig {
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export interface CalendarCardConfig extends LovelaceCardConfig {
|
||||
entities: string[];
|
||||
title?: string;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export interface ConditionalCardConfig extends LovelaceCardConfig {
|
||||
card: LovelaceCardConfig;
|
||||
conditions: Condition[];
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { PropertyValues } from "lit-element";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { processConfigEntities } from "./process-config-entities";
|
||||
|
||||
// Check if config or Entity changed
|
||||
export function hasConfigOrEntityChanged(
|
||||
element: any,
|
||||
changedProps: PropertyValues
|
||||
): boolean {
|
||||
function hasConfigChanged(element: any, changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has("_config")) {
|
||||
return true;
|
||||
}
|
||||
@@ -23,9 +20,41 @@ export function hasConfigOrEntityChanged(
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if config or Entity changed
|
||||
export function hasConfigOrEntityChanged(
|
||||
element: any,
|
||||
changedProps: PropertyValues
|
||||
): boolean {
|
||||
if (hasConfigChanged(element, changedProps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant;
|
||||
|
||||
return (
|
||||
oldHass.states[element._config!.entity] !==
|
||||
element.hass!.states[element._config!.entity]
|
||||
);
|
||||
}
|
||||
|
||||
// Check if config or Entities changed
|
||||
export function hasConfigOrEntitiesChanged(
|
||||
element: any,
|
||||
changedProps: PropertyValues
|
||||
): boolean {
|
||||
if (hasConfigChanged(element, changedProps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant;
|
||||
|
||||
const entities = processConfigEntities(element._config!.entities);
|
||||
|
||||
return entities.some(
|
||||
(entity) =>
|
||||
oldHass.states[entity.entity] !== element.hass!.states[entity.entity]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import "../cards/hui-button-card";
|
||||
import "../cards/hui-calendar-card";
|
||||
import "../cards/hui-entities-card";
|
||||
import "../cards/hui-entity-button-card";
|
||||
import "../cards/hui-entity-card";
|
||||
@@ -52,6 +53,7 @@ const LAZY_LOAD_TYPES = {
|
||||
map: () => import("../cards/hui-map-card"),
|
||||
markdown: () => import("../cards/hui-markdown-card"),
|
||||
picture: () => import("../cards/hui-picture-card"),
|
||||
calendar: () => import("../cards/hui-calendar-card"),
|
||||
};
|
||||
|
||||
// This will not return an error card but will throw the error
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
internalProperty,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { CalendarCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-entity-editor";
|
||||
import "../../../../components/entity/ha-entities-picker";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import type { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import {
|
||||
string,
|
||||
optional,
|
||||
object,
|
||||
boolean,
|
||||
array,
|
||||
union,
|
||||
assert,
|
||||
} from "superstruct";
|
||||
|
||||
const cardConfigStruct = object({
|
||||
type: string(),
|
||||
title: optional(union([string(), boolean()])),
|
||||
theme: optional(string()),
|
||||
entities: array(string()),
|
||||
});
|
||||
|
||||
@customElement("hui-calendar-card-editor")
|
||||
export class HuiCalendarCardEditor extends LitElement
|
||||
implements LovelaceCardEditor {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) private _config?: CalendarCardConfig;
|
||||
|
||||
@internalProperty() private _configEntities?: string[];
|
||||
|
||||
public setConfig(config: CalendarCardConfig): void {
|
||||
assert(config, cardConfigStruct);
|
||||
this._config = config;
|
||||
this._configEntities = config.entities;
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
${configElementStyle}
|
||||
<div class="card-config">
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
</div>
|
||||
<h3>
|
||||
${"Calendar Entities" +
|
||||
" (" +
|
||||
this.hass!.localize("ui.panel.lovelace.editor.card.config.required") +
|
||||
")"}
|
||||
</h3>
|
||||
<ha-entities-picker
|
||||
.hass=${this.hass!}
|
||||
.value=${this._configEntities}
|
||||
.includeDomains=${["calendar"]}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</ha-entities-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: EntitiesEditorEvent | CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.detail && ev.detail.value && Array.isArray(ev.detail.value)) {
|
||||
this._config = { ...this._config, entities: ev.detail.value };
|
||||
} else if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue]: target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-calendar-card-editor": HuiCalendarCardEditor;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,10 @@ export const coreCards: Card[] = [
|
||||
type: "button",
|
||||
showElement: true,
|
||||
},
|
||||
{
|
||||
type: "calendar",
|
||||
showElement: true,
|
||||
},
|
||||
{
|
||||
type: "entities",
|
||||
showElement: true,
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface EntityFilterEntityConfig extends EntityConfig {
|
||||
}
|
||||
export interface DividerConfig {
|
||||
type: "divider";
|
||||
style: string;
|
||||
style: { [key: string]: string };
|
||||
}
|
||||
export interface SectionConfig {
|
||||
type: "section";
|
||||
|
||||
@@ -40,7 +40,7 @@ export const buttonsHeaderFooterConfigStruct = object({
|
||||
export const graphHeaderFooterConfigStruct = object({
|
||||
type: string(),
|
||||
entity: string(),
|
||||
detail: optional(string()),
|
||||
detail: optional(number()),
|
||||
hours_to_show: optional(number()),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { styleMap } from "lit-html/directives/style-map";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { DividerConfig, LovelaceRow } from "../entity-rows/types";
|
||||
|
||||
@@ -19,13 +22,7 @@ class HuiDividerRow extends LitElement implements LovelaceRow {
|
||||
throw new Error("Error in card configuration.");
|
||||
}
|
||||
|
||||
this._config = {
|
||||
style: {
|
||||
height: "1px",
|
||||
"background-color": "var(--divider-color)",
|
||||
},
|
||||
...config,
|
||||
};
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -33,13 +30,16 @@ class HuiDividerRow extends LitElement implements LovelaceRow {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const el = document.createElement("div");
|
||||
return html`<div style=${styleMap(this._config.style)}></div>`;
|
||||
}
|
||||
|
||||
Object.keys(this._config.style).forEach((prop) => {
|
||||
el.style.setProperty(prop, this._config!.style[prop]);
|
||||
});
|
||||
|
||||
return html` ${el} `;
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
div {
|
||||
height: 1px;
|
||||
background-color: var(--entities-divider-color, var(--divider-color));
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
internalProperty,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../components/ha-icon";
|
||||
@@ -48,7 +48,7 @@ class HuiSectionRow extends LitElement implements LovelaceRow {
|
||||
}
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: var(--divider-color);
|
||||
background-color: var(--entities-divider-color, var(--divider-color));
|
||||
margin-left: -16px;
|
||||
margin-right: -16px;
|
||||
margin-top: 8px;
|
||||
|
||||
@@ -394,18 +394,6 @@ export class HUIView extends LitElement {
|
||||
left: calc(16px + env(safe-area-inset-left));
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
:host {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.column > * {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 599px) {
|
||||
.column {
|
||||
max-width: 600px;
|
||||
|
||||
@@ -6,6 +6,7 @@ export const darkStyles = {
|
||||
"secondary-background-color": "#1e1e1e",
|
||||
"primary-text-color": "#e1e1e1",
|
||||
"secondary-text-color": "#9b9b9b",
|
||||
"disabled-text-color": "#6f6f6f",
|
||||
"app-header-text-color": "#e1e1e1",
|
||||
"app-header-background-color": "#1c1c1c",
|
||||
"switch-unchecked-button-color": "#999999",
|
||||
|
||||
@@ -179,6 +179,13 @@
|
||||
"media_player": {
|
||||
"source": "Source",
|
||||
"sound_mode": "Sound mode",
|
||||
"browse_media": "Browse media",
|
||||
"turn_on": "Turn on",
|
||||
"turn_off": "Turn off",
|
||||
"media_play": "Play",
|
||||
"media_play_pause": "Play/pause",
|
||||
"media_next_track": "Next",
|
||||
"media_previous_track": "Previous",
|
||||
"text_to_speak": "Text to speak"
|
||||
},
|
||||
"persistent_notification": {
|
||||
@@ -312,6 +319,7 @@
|
||||
"past": "{time} ago",
|
||||
"future": "In {time}",
|
||||
"never": "Never",
|
||||
"just_now": "Just now",
|
||||
"duration": {
|
||||
"second": "{count} {count, plural,\n one {second}\n other {seconds}\n}",
|
||||
"minute": "{count} {count, plural,\n one {minute}\n other {minutes}\n}",
|
||||
@@ -341,6 +349,22 @@
|
||||
"data-table": {
|
||||
"search": "Search",
|
||||
"no-data": "No data"
|
||||
},
|
||||
"media-browser": {
|
||||
"pick": "Pick",
|
||||
"play": "Play",
|
||||
"play-media": "Play Media",
|
||||
"pick-media": "Pick Media",
|
||||
"no_items": "No items",
|
||||
"choose-source": "Choose Source",
|
||||
"media-player-browser": "Media Player Browser",
|
||||
"content-type": {
|
||||
"server": "Server",
|
||||
"library": "Library",
|
||||
"artist": "Artist",
|
||||
"album": "Album",
|
||||
"playlist": "Playlist"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -608,6 +632,7 @@
|
||||
"add_tag": "Add tag",
|
||||
"write": "Write",
|
||||
"edit": "Edit",
|
||||
"never_scanned": "Never scanned",
|
||||
"create_automation": "Create automation with tag",
|
||||
"automation_title": "Tag {name} is scanned",
|
||||
"headers": {
|
||||
@@ -812,7 +837,21 @@
|
||||
"input_text": "Reload input texts",
|
||||
"input_number": "Reload input numbers",
|
||||
"input_datetime": "Reload input date times",
|
||||
"input_select": "Reload input selects"
|
||||
"input_select": "Reload input selects",
|
||||
"template": "Reload template entities",
|
||||
"universal": "Reload universal media player entities",
|
||||
"rest": "Reload rest entities",
|
||||
"command_line": "Reload command line entities",
|
||||
"filter": "Reload filter entities",
|
||||
"statistics": "Reload statistics entities",
|
||||
"generic": "Reload generic IP camera entities",
|
||||
"generic_thermostat": "Reload generic thermostat entities",
|
||||
"homekit": "Reload HomeKit",
|
||||
"min_max": "Reload min/max entities",
|
||||
"history_stats": "Reload history stats entities",
|
||||
"trend": "Reload trend entities",
|
||||
"ping": "Reload ping binary sensor entities",
|
||||
"filesize": "Reload file size entities"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Server management",
|
||||
@@ -1376,6 +1415,7 @@
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"add_prompt": "No {name} have been added using this device yet. You can add one by clicking the + button above.",
|
||||
"caption": "Devices",
|
||||
"description": "Manage connected devices",
|
||||
"device_info": "Device info",
|
||||
@@ -1421,6 +1461,7 @@
|
||||
"scripts": "Scripts",
|
||||
"scenes": "Scenes",
|
||||
"confirm_rename_entity_ids": "Do you also want to rename the entity id's of your entities?",
|
||||
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, Lovelace) that is currently using these entities, you will have to update them yourself.",
|
||||
"data_table": {
|
||||
"device": "Device",
|
||||
"manufacturer": "Manufacturer",
|
||||
@@ -1577,7 +1618,10 @@
|
||||
"documentation": "Documentation",
|
||||
"delete": "Delete",
|
||||
"delete_confirm": "Are you sure you want to delete this integration?",
|
||||
"reload": "Reload",
|
||||
"restart_confirm": "Restart Home Assistant to finish removing this integration",
|
||||
"reload_confirm": "The integration was reloaded",
|
||||
"reload_restart_confirm": "Restart Home Assistant to finish reloading this integration",
|
||||
"manuf": "by {manufacturer}",
|
||||
"hub": "Connected via",
|
||||
"firmware": "Firmware: {version}",
|
||||
@@ -1618,6 +1662,8 @@
|
||||
"caption": "View user",
|
||||
"name": "Name",
|
||||
"change_password": "Change password",
|
||||
"new_password": "New Password",
|
||||
"password_changed": "The password is changed!",
|
||||
"activate_user": "Activate user",
|
||||
"deactivate_user": "Deactivate user",
|
||||
"delete_user": "Delete user",
|
||||
@@ -1638,6 +1684,8 @@
|
||||
"name": "Name",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"password_confirm": "Confirm Password",
|
||||
"password_not_match": "Passwords don't match",
|
||||
"create": "Create"
|
||||
}
|
||||
},
|
||||
@@ -1656,10 +1704,14 @@
|
||||
"message_received": "Message {id} received on {topic} at {time}:"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Configure",
|
||||
"common": {
|
||||
"zwave": "Z-Wave",
|
||||
"node_id": "Node ID",
|
||||
"ozw_instance": "OpenZWave Instance"
|
||||
"ozw_instance": "OpenZWave Instance",
|
||||
"instance": "Instance",
|
||||
"controller": "Controller",
|
||||
"network": "Network"
|
||||
},
|
||||
"device_info": {
|
||||
"zwave_info": "Z-Wave Info",
|
||||
@@ -1696,6 +1748,44 @@
|
||||
"refreshing_description": "Refreshing node information...",
|
||||
"node_status": "Node Status",
|
||||
"step": "Step"
|
||||
},
|
||||
"network_status": {
|
||||
"online": "Online",
|
||||
"offline": "Offline",
|
||||
"starting": "Starting",
|
||||
"unknown": "Unknown",
|
||||
"details": {
|
||||
"driverallnodesqueried": "All nodes have been queried",
|
||||
"driverallnodesqueriedsomedead": "All nodes have been queried. Some nodes were found dead",
|
||||
"driverawakenodesqueries": "All awake nodes have been queried",
|
||||
"driverremoved": "The driver has been removed",
|
||||
"driverreset": "The driver has been reset",
|
||||
"driverfailed": "Failed to connect to Z-Wave controller",
|
||||
"driverready": "Initializing the Z-Wave controller",
|
||||
"ready": "Ready to connect",
|
||||
"stopped": "OpenZWave stopped",
|
||||
"started": "Connected to MQTT",
|
||||
"starting": "Connecting to MQTT",
|
||||
"offline": "OZWDaemon offline"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"select_instance": "Select Instance",
|
||||
"network": "Network",
|
||||
"nodes": "Nodes"
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Select an OpenZWave Instance",
|
||||
"introduction": "You have more than one OpenZWave instance running. Which instance would you like to manage?"
|
||||
},
|
||||
"network": {
|
||||
"header": "Network Management",
|
||||
"introduction": "Manage network-wide functions.",
|
||||
"node_count": "{count} nodes"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Add Node",
|
||||
"remove_node": "Remove Node"
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
@@ -2064,6 +2154,10 @@
|
||||
"available_states": "Available States",
|
||||
"description": "The Alarm Panel card allows you to Arm and Disarm your alarm control panel integrations."
|
||||
},
|
||||
"calendar": {
|
||||
"name": "Calendar",
|
||||
"description": "The Calendar card displays a calendar including day, week and list views"
|
||||
},
|
||||
"conditional": {
|
||||
"name": "Conditional",
|
||||
"description": "The Conditional card displays another card based on entity states.",
|
||||
@@ -2564,6 +2658,7 @@
|
||||
"title": "Template",
|
||||
"description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.",
|
||||
"editor": "Template editor",
|
||||
"reset": "Reset to demo template",
|
||||
"jinja_documentation": "Jinja2 template documentation",
|
||||
"template_extensions": "Home Assistant template extensions",
|
||||
"unknown_error_template": "Unknown error rendering template"
|
||||
@@ -2591,6 +2686,7 @@
|
||||
"intro": "Hello {name}, welcome to Home Assistant. How would you like to name your home?",
|
||||
"intro_location": "We would like to know where you live. This information will help with displaying information and setting up sun-based automations. This data is never shared outside of your network.",
|
||||
"intro_location_detect": "We can help you fill in this information by making a one-time request to an external service.",
|
||||
"location_name": "Name of your Home Assistant installation",
|
||||
"location_name_default": "Home",
|
||||
"button_detect": "Detect",
|
||||
"finish": "Next"
|
||||
|
||||
+11
-5
@@ -118,8 +118,8 @@ export interface Panels {
|
||||
|
||||
export interface Calendar {
|
||||
entity_id: string;
|
||||
name: string;
|
||||
backgroundColor: string;
|
||||
name?: string;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export interface SelectedCalendar {
|
||||
@@ -144,9 +144,15 @@ export interface CalendarViewChanged {
|
||||
view: string;
|
||||
}
|
||||
|
||||
export type FullCalendarView =
|
||||
| "dayGridMonth"
|
||||
| "dayGridWeek"
|
||||
| "dayGridDay"
|
||||
| "listWeek";
|
||||
|
||||
export interface ToggleButton {
|
||||
label?: string;
|
||||
icon: string;
|
||||
label: string;
|
||||
iconPath: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -187,7 +193,7 @@ export interface Resources {
|
||||
|
||||
export interface Context {
|
||||
id: string;
|
||||
parrent_id?: string;
|
||||
parent_id?: string;
|
||||
user_id?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
export const normalize = (value: number, min: number, max: number): number => {
|
||||
if (isNaN(value) || isNaN(min) || isNaN(max)) {
|
||||
// Not a number, return 0
|
||||
return 0;
|
||||
}
|
||||
if (value > max) return max;
|
||||
if (value < min) return min;
|
||||
return value;
|
||||
};
|
||||
|
||||
export const getValueInPercentage = (
|
||||
value: number,
|
||||
min: number,
|
||||
max: number
|
||||
): number => {
|
||||
const newMax = max - min;
|
||||
const newVal = value - min;
|
||||
return (100 * newVal) / newMax;
|
||||
};
|
||||
|
||||
export const roundWithOneDecimal = (value: number): number => {
|
||||
return Math.round(value * 10) / 10;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
import * as assert from "assert";
|
||||
import { createHassioSession } from "../../src/data/hassio/supervisor";
|
||||
|
||||
const sessionID = "fhdsu73rh3io4h8f3irhjel8ousafehf8f3yh";
|
||||
|
||||
describe("Create hassio session", function () {
|
||||
it("Test create session without HTTPS", async function () {
|
||||
// @ts-ignore
|
||||
global.document = {};
|
||||
// @ts-ignore
|
||||
global.location = {};
|
||||
await createHassioSession({
|
||||
// @ts-ignore
|
||||
callApi: async function () {
|
||||
return { data: { session: sessionID } };
|
||||
},
|
||||
});
|
||||
assert.equal(
|
||||
// @ts-ignore
|
||||
global.document.cookie,
|
||||
"ingress_session=fhdsu73rh3io4h8f3irhjel8ousafehf8f3yh;path=/api/hassio_ingress/;SameSite=Strict"
|
||||
);
|
||||
});
|
||||
it("Test create session with HTTPS", async function () {
|
||||
// @ts-ignore
|
||||
global.document = {};
|
||||
// @ts-ignore
|
||||
global.location = { protocol: "https:" };
|
||||
await createHassioSession({
|
||||
// @ts-ignore
|
||||
callApi: async function () {
|
||||
return { data: { session: sessionID } };
|
||||
},
|
||||
});
|
||||
assert.equal(
|
||||
// @ts-ignore
|
||||
global.document.cookie,
|
||||
"ingress_session=fhdsu73rh3io4h8f3irhjel8ousafehf8f3yh;path=/api/hassio_ingress/;SameSite=Strict;Secure"
|
||||
);
|
||||
|
||||
// Clean up in case they will be used in other tests
|
||||
// @ts-ignore
|
||||
global.document = {};
|
||||
// @ts-ignore
|
||||
global.location = {};
|
||||
});
|
||||
it("Test fail to create", async function () {
|
||||
const createSessionPromise = createHassioSession({
|
||||
// @ts-ignore
|
||||
callApi: async function () {},
|
||||
}).then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
assert.equal(await createSessionPromise, false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as assert from "assert";
|
||||
import {
|
||||
getValueInPercentage,
|
||||
normalize,
|
||||
roundWithOneDecimal,
|
||||
} from "../../src/util/calculate";
|
||||
|
||||
describe("Calculate tests", function () {
|
||||
it("Test getValueInPercentage", function () {
|
||||
assert.equal(getValueInPercentage(10, 0, 100), 10);
|
||||
assert.equal(getValueInPercentage(120, 0, 100), 120);
|
||||
assert.equal(getValueInPercentage(-10, 0, 100), -10);
|
||||
assert.equal(getValueInPercentage(10.33333, 0, 100), 10.33333);
|
||||
});
|
||||
it("Test normalize", function () {
|
||||
assert.equal(normalize(10, 0, 100), 10);
|
||||
assert.equal(normalize(1, 10, 100), 10);
|
||||
assert.equal(normalize(100, 0, 10), 10);
|
||||
});
|
||||
it("Test roundWithOneDecimal", function () {
|
||||
assert.equal(roundWithOneDecimal(10), 10);
|
||||
assert.equal(roundWithOneDecimal(10.3), 10.3);
|
||||
assert.equal(roundWithOneDecimal(10.3333), 10.3);
|
||||
});
|
||||
});
|
||||
@@ -46,15 +46,15 @@
|
||||
"state_badge": {
|
||||
"alarm_control_panel": {
|
||||
"armed": "Gewapen",
|
||||
"armed_away": "Gewapen",
|
||||
"armed_away": "Aktief",
|
||||
"armed_custom_bypass": "Gewapen",
|
||||
"armed_home": "Gewapen",
|
||||
"armed_night": "Gewapen",
|
||||
"armed_night": "Aktief",
|
||||
"arming": "Bewapen Tans",
|
||||
"disarmed": "Ontwapen",
|
||||
"disarming": "Ontwapen",
|
||||
"pending": "Hangend",
|
||||
"triggered": "Aktief"
|
||||
"triggered": "Lui"
|
||||
},
|
||||
"default": {
|
||||
"entity_not_found": "Entiteit nie gevind nie",
|
||||
|
||||
+156
-10
@@ -419,9 +419,16 @@
|
||||
"unlock": "Desbloquejar"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Navega pels mitjans",
|
||||
"media_next_track": "Següent",
|
||||
"media_play": "Reprodueix",
|
||||
"media_play_pause": "Reprodueix/pausa",
|
||||
"media_previous_track": "Anterior",
|
||||
"sound_mode": "Mode de so",
|
||||
"source": "Entrada",
|
||||
"text_to_speak": "Text a veu"
|
||||
"text_to_speak": "Text a veu",
|
||||
"turn_off": "Apaga",
|
||||
"turn_on": "Engega"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Desestimar"
|
||||
@@ -554,6 +561,26 @@
|
||||
"loading_history": "Carregant historial d'estats...",
|
||||
"no_history_found": "No s'ha trobat cap historial d'estats."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Tria la font",
|
||||
"content-type": {
|
||||
"album": "Àlbum",
|
||||
"artist": "Artista",
|
||||
"library": "Biblioteca",
|
||||
"playlist": "Llista de reproducció",
|
||||
"server": "Servidor"
|
||||
},
|
||||
"media-player-browser": "Navegador del reproductor multimèdia",
|
||||
"no_items": "Sense elements",
|
||||
"pick": "Escull",
|
||||
"pick-media": "Tria mitjans",
|
||||
"play": "Reprodueix",
|
||||
"play-media": "Reprodueix mitjans"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "Imatge",
|
||||
"unsupported_format": "Format no compatible, tria una imatge JPEG, PNG o GIF."
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Àrea",
|
||||
"automation": "Part de les següents automatitzacions",
|
||||
@@ -574,6 +601,7 @@
|
||||
"week": "{count} {count, plural,\n one {setmana}\n other {setmanes}\n}"
|
||||
},
|
||||
"future": "D'aquí a {time}",
|
||||
"just_now": "Ara mateix",
|
||||
"never": "Mai",
|
||||
"past": "Fa {time}"
|
||||
},
|
||||
@@ -656,6 +684,9 @@
|
||||
"required_error_msg": "Aquest camp és obligatori",
|
||||
"yaml_not_editable": "La configuració d'aquesta entitat no es pot editar des de la interfície d'usuari. Només es poden editar des de la interfície aquelles entitats que s'han configurat des d'ella."
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop": "Retalla"
|
||||
},
|
||||
"more_info_control": {
|
||||
"dismiss": "Desestimar el diàleg",
|
||||
"edit": "Edita entitat",
|
||||
@@ -892,7 +923,7 @@
|
||||
"wait_template": "Plantilla d'espera"
|
||||
}
|
||||
},
|
||||
"unsupported_action": "Acció {action} no suportada."
|
||||
"unsupported_action": "Acció no suportada per la UI: {action}"
|
||||
},
|
||||
"alias": "Nom",
|
||||
"conditions": {
|
||||
@@ -958,7 +989,7 @@
|
||||
"zone": "Zona"
|
||||
}
|
||||
},
|
||||
"unsupported_condition": "Condició {condition} no suportada."
|
||||
"unsupported_condition": "Condició no suportada per la UI: {condition}"
|
||||
},
|
||||
"default_name": "Nova automatització",
|
||||
"description": {
|
||||
@@ -993,7 +1024,7 @@
|
||||
"delete_confirm": "Segur que vols eliminar-ho?",
|
||||
"duplicate": "Duplica",
|
||||
"header": "Disparadors",
|
||||
"introduction": "Els activadors són les regles que fan que es dispari una automatització. Pots definir més d'un activador per a cada automatització. Una vegada s'iniciï un activador, el Home Assistant validarà les condicions (si n'hi ha) i finalment cridarà l'acció.",
|
||||
"introduction": "Els activadors són les regles que fan que es dispari una automatització. Pots definir més d'un activador per a cada automatització. Una vegada s'iniciï un activador, Home Assistant validarà les condicions (si n'hi ha) i finalment cridarà l'acció.",
|
||||
"learn_more": "Més informació sobre els activadors",
|
||||
"name": "Disparador",
|
||||
"type_select": "Tipus de disparador",
|
||||
@@ -1050,6 +1081,9 @@
|
||||
"sunrise": "a l'Alba",
|
||||
"sunset": "al Capvespre"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Etiqueta"
|
||||
},
|
||||
"template": {
|
||||
"label": "Plantilla",
|
||||
"value_template": "Plantilla de valor"
|
||||
@@ -1077,7 +1111,7 @@
|
||||
"zone": "Zona"
|
||||
}
|
||||
},
|
||||
"unsupported_platform": "Plataforma {platform} no suportada."
|
||||
"unsupported_platform": "Plataforma no suportada per la UI: {platform}"
|
||||
},
|
||||
"unsaved_confirm": "Hi ha canvis no desats. Segur que vols sortir?"
|
||||
},
|
||||
@@ -1090,7 +1124,7 @@
|
||||
"headers": {
|
||||
"name": "Nom"
|
||||
},
|
||||
"introduction": "L'editor d'automatitzacions et permet crear i editar automatitzacions. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat el Home Assistant correctament.",
|
||||
"introduction": "L'editor d'automatitzacions et permet crear i editar automatitzacions. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat Home Assistant correctament.",
|
||||
"learn_more": "Més informació sobre les automatitzacions",
|
||||
"no_automations": "No s'ha pogut trobar cap automatització editable",
|
||||
"only_editable": "Només es poden editar les automatitzacions definides dins l'arxiu automations.yaml.",
|
||||
@@ -1308,6 +1342,7 @@
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"add_prompt": "Encara no s'han afegit {name} mitjançant aquest dispositiu. Pots afegir-ne fent clic al botó + a sobre.",
|
||||
"automation": {
|
||||
"actions": {
|
||||
"caption": "Quan es dispara alguna cosa..."
|
||||
@@ -1327,6 +1362,7 @@
|
||||
"caption": "Dispositius",
|
||||
"confirm_delete": "Estàs segur que vols eliminar aquest dispositiu?",
|
||||
"confirm_rename_entity_ids": "Vols, també, canviar el nom dels ID's d'entitat de les entitats?",
|
||||
"confirm_rename_entity_ids_warning": "Això no canviarà cap configuració (com automatitzacions, scripts, escenes, Lovelace) que estigui utilitzant aquestes entitats, les hauràs d'actualitzar tu mateix.",
|
||||
"data_table": {
|
||||
"area": "Àrea",
|
||||
"battery": "Bateria",
|
||||
@@ -1489,8 +1525,11 @@
|
||||
"no_device": "Entitats sense dispositius",
|
||||
"no_devices": "Aquesta integració no té dispositius.",
|
||||
"options": "Opcions",
|
||||
"reload": "Torna a carregar",
|
||||
"reload_confirm": "La integració s'ha tornar a carregar",
|
||||
"reload_restart_confirm": "Reinicia Home Assistant per acabar de carregar aquesta integració",
|
||||
"rename": "Canvia el nom",
|
||||
"restart_confirm": "Reinicia el Home Assistant per acabar d'eliminar aquesta integració",
|
||||
"restart_confirm": "Reinicia Home Assistant per acabar d'eliminar aquesta integració",
|
||||
"settings_button": "Edita la configuració de {integration}",
|
||||
"system_options": "Opcions de sistema",
|
||||
"system_options_button": "Opcions de sistema de {integration}",
|
||||
@@ -1647,7 +1686,11 @@
|
||||
"topic": "tòpic"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Configura",
|
||||
"common": {
|
||||
"controller": "Controlador",
|
||||
"instance": "Instància",
|
||||
"network": "Xarxa",
|
||||
"node_id": "ID del node",
|
||||
"ozw_instance": "Instància OpenZWave",
|
||||
"zwave": "Z-Wave"
|
||||
@@ -1657,9 +1700,74 @@
|
||||
"stage": "Etapa",
|
||||
"zwave_info": "Informació Z-Wave"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Xarxa",
|
||||
"nodes": "Nodes",
|
||||
"select_instance": "Selecciona instància"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "S'han cosultat tots els nodes",
|
||||
"driverallnodesqueriedsomedead": "S'han consultat tots els nodes. Se n'han trobat alguns morts",
|
||||
"driverawakenodesqueries": "S'han cosultat tots els nodes desperts",
|
||||
"driverfailed": "No s'ha pogut connectar amb el controlador Z-Wave",
|
||||
"driverready": "Iniciant el controlador Z-Wave",
|
||||
"driverremoved": "El controlador s'ha eliminat",
|
||||
"driverreset": "El controlador s'ha restablert",
|
||||
"offline": "OZWDaemon fora de línia",
|
||||
"ready": "A punt per connectar-se",
|
||||
"started": "Connectat a MQTT",
|
||||
"starting": "Connectant a MQTT",
|
||||
"stopped": "OpenZWave aturat"
|
||||
},
|
||||
"offline": "Fora de línia",
|
||||
"online": "En línia",
|
||||
"starting": "Iniciant",
|
||||
"unknown": "Desconegut"
|
||||
},
|
||||
"network": {
|
||||
"header": "Gestió de la xarxa",
|
||||
"introduction": "Gestiona les funcions de tota la xarxa.",
|
||||
"node_count": "{count} nodes"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "Actualitzant de grups d'associacions i membres",
|
||||
"cacheload": "Carregant informació del fitxer de memòria cau d'OpenZWave. Els nodes amb bateria romandran en aquesta fase fins que es despertin.",
|
||||
"complete": "Procés de consulta completat",
|
||||
"configuration": "Obtenint valors de configuració del node",
|
||||
"dynamic": "Obtenint valors usualment canviants del node",
|
||||
"instances": "Obtenint detalls sobre instàncies i canals compatibles amb el dispositiu",
|
||||
"manufacturerspecific1": "Obtenint fabricant i codis ID de producte del node",
|
||||
"manufacturerspecific2": "Obtenint fabricant i codis ID de producte addicionals del node",
|
||||
"neighbors": "Obtenint llista de nodes veïns",
|
||||
"nodeinfo": "Obtenint classes de comandes compatibles del node",
|
||||
"nodeplusinfo": "Obtenint informació Z-Wave+ del node",
|
||||
"probe": "Comprovant si el node està despert",
|
||||
"protocolinfo": "Obtenint funcions Z-Wave bàsiques del controlador d'aquest node",
|
||||
"session": "Obtenint valors inusualment canviants del node",
|
||||
"static": "Obtenint valors estàtics del dispositiu",
|
||||
"versions": "Obtenint informació de programari i versions de classes de comandes",
|
||||
"wakeup": "Configurant el suport per a cues i missatges"
|
||||
},
|
||||
"refresh_node": {
|
||||
"battery_note": "Si el node funciona amb bateria, assegura't de que estigui actiu abans de continuar",
|
||||
"complete": "Actualització del node completa",
|
||||
"description": "Això farà que OpenZWave torni a consultar el node i n'actualitzi les classes de comandes, funcions i valors.",
|
||||
"node_status": "Estat del node",
|
||||
"step": "Pas"
|
||||
"refreshing_description": "Actualitzant la informació del node...",
|
||||
"start_refresh_button": "Inicia l'actualització",
|
||||
"step": "Pas",
|
||||
"title": "Informació d'actualització del node",
|
||||
"wakeup_header": "Instruccions en despertar de",
|
||||
"wakeup_instructions_source": "Les instruccions en despertar provenen de la base de dades de dispositius de la comunitat OpenZWave."
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Selecciona una instància d'OpenZWave",
|
||||
"introduction": "Tens més d'una instància d'OpenZWave en funcionament. Quina instància vols gestionar?"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Afegeix node",
|
||||
"remove_node": "Elimina node"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1725,7 +1833,7 @@
|
||||
"headers": {
|
||||
"name": "Nom"
|
||||
},
|
||||
"introduction": "L'editor d'escenes et permet crear i editar escenes. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat el Home Assistant correctament.",
|
||||
"introduction": "L'editor d'escenes et permet crear i editar escenes. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat Home Assistant correctament.",
|
||||
"learn_more": "Més informació sobre les escenes",
|
||||
"no_scenes": "No s'ha pogut trobar cap escena editable",
|
||||
"only_editable": "Només es poden editar les escenes definides dins l'arxiu scenes.yaml.",
|
||||
@@ -1772,7 +1880,7 @@
|
||||
"headers": {
|
||||
"name": "Nom"
|
||||
},
|
||||
"introduction": "L'editor de scripts et permet crear i editar scripts. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat el Home Assistant correctament.",
|
||||
"introduction": "L'editor de scripts et permet crear i editar scripts. Vés a l'enllaç de sota per veure'n les instruccions i assegurar-te que has configurat Home Assistant correctament.",
|
||||
"learn_more": "Més informació sobre els scripts",
|
||||
"no_scripts": "No hem trobat cap script editable",
|
||||
"show_info": "Mostra informació sobre l'script",
|
||||
@@ -1785,7 +1893,9 @@
|
||||
"section": {
|
||||
"reloading": {
|
||||
"automation": "Actualitza automatitzacions",
|
||||
"command_line": "Torna a carregar entitats de comandes",
|
||||
"core": "Actualitza ubicació i personalitzacions",
|
||||
"filter": "Torna a carregar entitats de filtre",
|
||||
"group": "Actualitza grups",
|
||||
"heading": "Tornant a carregar la configuració",
|
||||
"input_boolean": "Actualitza entrades booleanes",
|
||||
@@ -1795,8 +1905,12 @@
|
||||
"input_text": "Actualitza entrades de text",
|
||||
"introduction": "Algunes parts de Home Assistant es poden actualitzar sense necessitat reiniciar-lo. Si prems actualitza s'esborrarà la configuració YAML actual i se'n carregarà la nova.",
|
||||
"person": "Actualitza persones",
|
||||
"rest": "Torna a carregar entitats de repòs",
|
||||
"scene": "Actualitza escenes",
|
||||
"script": "Actualitza programes",
|
||||
"statistics": "Torna a carregar entitats estadístiques",
|
||||
"template": "Torna a carregar entitats de plantilla",
|
||||
"universal": "Torna a carregar entitats del reproductor universal",
|
||||
"zone": "Actualitza zones"
|
||||
},
|
||||
"server_management": {
|
||||
@@ -1816,6 +1930,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "Afegeix etiqueta",
|
||||
"automation_title": "S'escanegi l'etiqueta {name}",
|
||||
"caption": "Etiquetes",
|
||||
"create_automation": "Crea una automatització amb una etiqueta",
|
||||
"description": "Gestiona les etiquetes",
|
||||
"detail": {
|
||||
"create": "Crea",
|
||||
"create_and_write": "Crea i escriu",
|
||||
"delete": "Elimina",
|
||||
"description": "Descripció",
|
||||
"name": "Nom",
|
||||
"new_tag": "Nova etiqueta",
|
||||
"tag_id": "ID de l'etiqueta",
|
||||
"tag_id_placeholder": "Autogenerat si es deixa buit",
|
||||
"update": "Actualitza"
|
||||
},
|
||||
"edit": "Edita",
|
||||
"headers": {
|
||||
"last_scanned": "Últim escaneig",
|
||||
"name": "Nom"
|
||||
},
|
||||
"never_scanned": "Mai escanejat",
|
||||
"no_tags": "Sense etiquetes",
|
||||
"write": "Escriu"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Afegir usuari",
|
||||
@@ -2137,6 +2277,7 @@
|
||||
"description": "Les plantilles es renderitzen mitjançant el motor Jinja2 amb algunes extensions específiques de Home Assistant.",
|
||||
"editor": "Editor de plantilles",
|
||||
"jinja_documentation": "Documentació sobre plantilles amb Jinja2",
|
||||
"reset": "Restableix a la plantilla de demostració",
|
||||
"template_extensions": "Extensions de plantilla de Home Assistant",
|
||||
"title": "Plantilla",
|
||||
"unknown_error_template": "Error desconegut renderitzant plantilla"
|
||||
@@ -2218,6 +2359,10 @@
|
||||
"description": "La targeta botó et permet afegir botons per realitzar diferents tasques.",
|
||||
"name": "Botó"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "La targeta Calendari mostra un calendari que inclou visualitzacions de dia, setmana i llista",
|
||||
"name": "Calendari"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Targeta",
|
||||
"change_type": "Canvia el tipus",
|
||||
@@ -2672,6 +2817,7 @@
|
||||
"intro": "Hola {name}, benvingut/uda a Home Assistant. Quin nom t'agradaria posar a la teva casa?",
|
||||
"intro_location": "Voldirem saber la zona on vius. Aquesta informació servirà per poder mostrar certa informació i configurar automatitzacions relatives a la posició del sol. Aquestes dades mai es compartiran fora de la teva xarxa personal.",
|
||||
"intro_location_detect": "Et podem ajudar a completar aquesta informació fent una única sol·licitud a un servei extern.",
|
||||
"location_name": "Nom de la instal·lació de Home Assistant",
|
||||
"location_name_default": "Casa"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
@@ -419,9 +419,16 @@
|
||||
"unlock": "Odemknout"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Procházet média",
|
||||
"media_next_track": "Další",
|
||||
"media_play": "Přehrát",
|
||||
"media_play_pause": "Přehrát/pozastavit",
|
||||
"media_previous_track": "Předchozí",
|
||||
"sound_mode": "Režim zvuku",
|
||||
"source": "Zdroj",
|
||||
"text_to_speak": "Převod textu na řeč"
|
||||
"text_to_speak": "Převod textu na řeč",
|
||||
"turn_off": "Vypnout",
|
||||
"turn_on": "Zapnout"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Zavřít"
|
||||
@@ -554,6 +561,26 @@
|
||||
"loading_history": "Historie stavu se načítá...",
|
||||
"no_history_found": "Historie stavu chybí."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Zvolte zdroj",
|
||||
"content-type": {
|
||||
"album": "Album",
|
||||
"artist": "Umělec",
|
||||
"library": "Knihovna",
|
||||
"playlist": "Seznam skladeb",
|
||||
"server": "Server"
|
||||
},
|
||||
"media-player-browser": "Prohlížeč přehrávače médií",
|
||||
"no_items": "Žádné položky",
|
||||
"pick": "Vybrat",
|
||||
"pick-media": "Vybrat média",
|
||||
"play": "Přehrát",
|
||||
"play-media": "Přehrát média"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "Obrázek",
|
||||
"unsupported_format": "Nepodporovaný formát, prosím vyberte obrázek typu JPEG, PNG nebo GIF."
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Oblast",
|
||||
"automation": "Část následujících automatizací",
|
||||
@@ -574,6 +601,7 @@
|
||||
"week": "{count} {count, plural,\none {týden}\nother {týdnů}\n}"
|
||||
},
|
||||
"future": "Za {time}",
|
||||
"just_now": "Právě teď",
|
||||
"never": "Nikdy",
|
||||
"past": "Před {time}"
|
||||
},
|
||||
@@ -656,6 +684,9 @@
|
||||
"required_error_msg": "Toto pole je povinné",
|
||||
"yaml_not_editable": "Nastavení této entity nelze upravovat z uživatelského rozhraní. Pouze entity nastavené z uživatelského rozhraní lze konfigurovat v uživatelském rozhraní."
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop": "Oříznout"
|
||||
},
|
||||
"more_info_control": {
|
||||
"dismiss": "Zavřít dialog",
|
||||
"edit": "Upravit entitu",
|
||||
@@ -837,7 +868,7 @@
|
||||
"add_option": "Přidat volbu",
|
||||
"conditions": "Podmínky",
|
||||
"default": "Výchozí akce",
|
||||
"label": "Vybrat",
|
||||
"label": "Volby",
|
||||
"option": "Volba {number}",
|
||||
"remove_option": "Odebrat volbu",
|
||||
"sequence": "Akce"
|
||||
@@ -862,12 +893,20 @@
|
||||
"service_data": "Data služby"
|
||||
},
|
||||
"repeat": {
|
||||
"label": "Opakovat",
|
||||
"label": "Opakovaní",
|
||||
"sequence": "Akce",
|
||||
"type_select": "Typ opakování",
|
||||
"type": {
|
||||
"count": {
|
||||
"label": "Počet"
|
||||
},
|
||||
"until": {
|
||||
"conditions": "Podmínky \"Dokud nenastane\"",
|
||||
"label": "Dokud nenastane"
|
||||
},
|
||||
"while": {
|
||||
"conditions": "Podmínky \"Pokud platí\"",
|
||||
"label": "Pokud platí"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1042,6 +1081,9 @@
|
||||
"sunrise": "Východ slunce",
|
||||
"sunset": "Západ slunce"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Značka"
|
||||
},
|
||||
"template": {
|
||||
"label": "Šablona",
|
||||
"value_template": "Šablona hodnoty"
|
||||
@@ -1319,6 +1361,7 @@
|
||||
"caption": "Zařízení",
|
||||
"confirm_delete": "Opravdu chcete toto zařízení odstranit?",
|
||||
"confirm_rename_entity_ids": "Chcete také přejmenovat ID entit vašich entit?",
|
||||
"confirm_rename_entity_ids_warning": "Žádná konfigurace (např. automatizace, skripty, scény, Lovelace), která tyto entity aktuálně používá, nebude změněna. Budete je muset aktualizovat sami.",
|
||||
"data_table": {
|
||||
"area": "Oblast",
|
||||
"battery": "Baterie",
|
||||
@@ -1481,6 +1524,8 @@
|
||||
"no_device": "Entity bez zařízení",
|
||||
"no_devices": "Tato integrace nemá žádná zařízení.",
|
||||
"options": "Možnosti",
|
||||
"reload_confirm": "Integrace byla obnovena",
|
||||
"reload_restart_confirm": "Restartujte Home Assistant pro obnovení této integrace",
|
||||
"rename": "Přejmenovat",
|
||||
"restart_confirm": "Restartujte Home Assistant pro odstranění této integrace",
|
||||
"settings_button": "Upravit nastavení pro {integration}",
|
||||
@@ -1639,8 +1684,81 @@
|
||||
"topic": "téma"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Konfigurovat",
|
||||
"common": {
|
||||
"controller": "Ovladač",
|
||||
"instance": "Instance",
|
||||
"network": "Síť",
|
||||
"node_id": "ID uzlu",
|
||||
"ozw_instance": "Instance OpenZWave",
|
||||
"zwave": "Z-Wave"
|
||||
},
|
||||
"device_info": {
|
||||
"node_failed": "Uzel selhal",
|
||||
"zwave_info": "Informace o Z-Wave"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Síť",
|
||||
"nodes": "Uzly",
|
||||
"select_instance": "Vyberte instanci"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "Všechny uzly byly zkontaktovány",
|
||||
"driverallnodesqueriedsomedead": "Všechny uzly byly zkontaktovány. Některé uzly byly nalezeny mrtvé",
|
||||
"driverawakenodesqueries": "Všechny probuzené uzly byly zkontaktovány",
|
||||
"driverfailed": "Nepodařilo se připojit k ovladači Z-Wave",
|
||||
"driverready": "Inicializuji ovladač Z-Wave",
|
||||
"driverremoved": "Ovladač byl odstraněn",
|
||||
"driverreset": "Ovladač byl resetován",
|
||||
"offline": "OZWDaemon je offline",
|
||||
"ready": "Připraveno k připojení",
|
||||
"started": "Připojeno k MQTT",
|
||||
"starting": "Připojování k MQTT",
|
||||
"stopped": "OpenZWave zastaven"
|
||||
},
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"starting": "Spouštění",
|
||||
"unknown": "Nezjištěno"
|
||||
},
|
||||
"network": {
|
||||
"header": "Správa sítě",
|
||||
"node_count": "{count} uzlů"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"configuration": "Získávám konfiguraci z uzlu",
|
||||
"dynamic": "Získávám často se měnící hodnoty z uzlu",
|
||||
"instances": "Získávám podrobnosti o tom, jaké instance nebo kanály zařízení podporuje",
|
||||
"manufacturerspecific1": "Získávám z uzlu kody výrobce a produktů",
|
||||
"manufacturerspecific2": "Získávám z uzlu další kódy výrobce a produktů",
|
||||
"neighbors": "Získávám seznam sousedů uzlu",
|
||||
"nodeinfo": "Získávám z uzlu podporované typy příkazů",
|
||||
"nodeplusinfo": "Získávám informace o Z-Wave + z uzlu",
|
||||
"probe": "Kontroluji, zda je uzel probuzený/živý",
|
||||
"protocolinfo": "Získávám z ovladače Z-Wave základní schopnosti tohoto uzlu",
|
||||
"session": "Získávám zřídka se měnící hodnoty z uzlu",
|
||||
"static": "Získávám statické hodnoty ze zařízení",
|
||||
"versions": "Získávám informace o verzích firmwaru a typech příkazů",
|
||||
"wakeup": "Nastavuji podporu pro probouzecí fronty a zprávy"
|
||||
},
|
||||
"refresh_node": {
|
||||
"battery_note": "Pokud je uzel napájen z baterie, nezapomeňte jej probudit, než budete pokračovat",
|
||||
"complete": "Obnova uzlu dokončena",
|
||||
"node_status": "Stav uzlu",
|
||||
"refreshing_description": "Aktualizuji informací o uzlu...",
|
||||
"start_refresh_button": "Spustit Obnovení",
|
||||
"step": "Krok",
|
||||
"title": "Aktualizovat informace o uzlu",
|
||||
"wakeup_header": "Pokyny pro probuzení pro"
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Vyberte instanci OpenZWave",
|
||||
"introduction": "Máte více než jednu instanci OpenZWave. Kterou instanci chcete spravovat?"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Přidat uzel",
|
||||
"remove_node": "Odebrat uzel"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1778,6 +1896,7 @@
|
||||
"person": "Znovu načíst osoby",
|
||||
"scene": "Znovu načíst scény",
|
||||
"script": "Znovu načíst skripty",
|
||||
"template": "Znovu načíst entity šablon",
|
||||
"zone": "Znovu načíst zóny"
|
||||
},
|
||||
"server_management": {
|
||||
@@ -1797,6 +1916,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "Přidat značku",
|
||||
"automation_title": "Značka {name} je naskenována",
|
||||
"caption": "Značky",
|
||||
"create_automation": "Vytvořit automatizaci se značkou",
|
||||
"description": "Správa značek",
|
||||
"detail": {
|
||||
"create": "Vytvořit",
|
||||
"create_and_write": "Vytvořit a zapsat",
|
||||
"delete": "Smazat",
|
||||
"description": "Popis",
|
||||
"name": "Název",
|
||||
"new_tag": "Nová značka",
|
||||
"tag_id": "ID značky",
|
||||
"tag_id_placeholder": "Automaticky vygenerováno, pokud zůstane prázdné",
|
||||
"update": "Aktualizovat"
|
||||
},
|
||||
"edit": "Upravit",
|
||||
"headers": {
|
||||
"last_scanned": "Naposledy naskenováno",
|
||||
"name": "Název"
|
||||
},
|
||||
"never_scanned": "Nikdy naskenováno",
|
||||
"no_tags": "Žádné značky",
|
||||
"write": "Zapsat"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Přidat uživatele",
|
||||
@@ -2038,7 +2183,7 @@
|
||||
"heal_network": "Vyléčit síť",
|
||||
"heal_node": "Uzdravit uzel",
|
||||
"node_info": "Informace o uzlu",
|
||||
"print_node": "Otisk nodu",
|
||||
"print_node": "Otisk uzlu",
|
||||
"refresh_entity": "Znovu načíst Entitu",
|
||||
"refresh_node": "Obnovit uzel",
|
||||
"remove_failed_node": "Odebrat selhaný uzel",
|
||||
@@ -2118,6 +2263,7 @@
|
||||
"description": "Šablony jsou vykreslovány pomocí Jinja2 šablonového enginu s některými specifickými rozšířeními pro Home Assistant.",
|
||||
"editor": "Editor šablon",
|
||||
"jinja_documentation": "Dokumentace šablony Jinja2",
|
||||
"reset": "Obnovit ukázkovou šablonu",
|
||||
"template_extensions": "Rozšíření šablony Home Assistant",
|
||||
"title": "Šablony",
|
||||
"unknown_error_template": "Šablona vykreslování neznámých chyb"
|
||||
@@ -2199,6 +2345,10 @@
|
||||
"description": "Karta Tlačítko umožňuje přidat tlačítka k provádění úkolů.",
|
||||
"name": "Tlačítko"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "Karta Kalendář zobrazuje kalendář včetně zobrazení dnů, týdnů a seznamů",
|
||||
"name": "Kalendář"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Karta",
|
||||
"change_type": "Změnit typ",
|
||||
@@ -2653,6 +2803,7 @@
|
||||
"intro": "Dobrý den, {name} , vítejte v Home Assistant. Jak byste chtěli pojmenovat svůj domov?",
|
||||
"intro_location": "Rádi bychom věděli, kde žijete. Tyto informace pomohou při zobrazování informací a určování přesné polohy slunce. Tato data nejsou nikdy sdílena mimo vaši síť.",
|
||||
"intro_location_detect": "Můžeme vám pomoci vyplnit tyto informace jednorázovým požadavkem na externí službu.",
|
||||
"location_name": "Název vaší instalace Home Assistant",
|
||||
"location_name_default": "Domov"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"boost": "Boost",
|
||||
"comfort": "Komfort",
|
||||
"eco": "Eco",
|
||||
"home": "Zu Hause",
|
||||
"home": "Zuhause",
|
||||
"normal": "Normal",
|
||||
"sleep": "Schlafen"
|
||||
}
|
||||
@@ -96,7 +96,7 @@
|
||||
"armed": "Aktiv",
|
||||
"armed_away": "Aktiv, abwesend",
|
||||
"armed_custom_bypass": "Aktiv, benutzerdefiniert",
|
||||
"armed_home": "Aktiv, zu Hause",
|
||||
"armed_home": "Aktiv, Zuhause",
|
||||
"armed_night": "Aktiv, Nacht",
|
||||
"arming": "Aktiviere",
|
||||
"disarmed": "Inaktiv",
|
||||
@@ -267,7 +267,7 @@
|
||||
"standby": "Standby"
|
||||
},
|
||||
"person": {
|
||||
"home": "Zu Hause"
|
||||
"home": "Zuhause"
|
||||
},
|
||||
"plant": {
|
||||
"ok": "OK",
|
||||
@@ -352,7 +352,7 @@
|
||||
"arm_away": "Aktivieren - Unterwegs",
|
||||
"arm_custom_bypass": "Benutzerdefinierter Bypass",
|
||||
"arm_home": "Aktivieren - Zuhause",
|
||||
"arm_night": "Nacht aktiviert",
|
||||
"arm_night": "Aktivieren - Nacht",
|
||||
"clear_code": "Löschen",
|
||||
"code": "Code",
|
||||
"disarm": "Deaktivieren"
|
||||
@@ -419,9 +419,12 @@
|
||||
"unlock": "Entriegeln"
|
||||
},
|
||||
"media_player": {
|
||||
"media_play": "Abspielen",
|
||||
"sound_mode": "Sound-Modus",
|
||||
"source": "Quelle",
|
||||
"text_to_speak": "Text zum Sprechen"
|
||||
"text_to_speak": "Text zum Sprechen",
|
||||
"turn_off": "Ausschalten",
|
||||
"turn_on": "Einschalten"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Ausblenden"
|
||||
@@ -554,6 +557,17 @@
|
||||
"loading_history": "Lade Zustandsverlauf...",
|
||||
"no_history_found": "Kein Zustandsverlauf gefunden."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Quelle wählen",
|
||||
"content-type": {
|
||||
"album": "Album",
|
||||
"artist": "Künstler",
|
||||
"library": "Bibliothek",
|
||||
"playlist": "Playlist",
|
||||
"server": "Server"
|
||||
},
|
||||
"play": "Abspielen"
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Bereich",
|
||||
"automation": "Teil der folgenden Automatisierungen",
|
||||
@@ -574,6 +588,7 @@
|
||||
"week": "{count} {count, plural,\none {Woche}\nother {Wochen}\n}"
|
||||
},
|
||||
"future": "In {time}",
|
||||
"just_now": "Gerade jetzt",
|
||||
"never": "Noch nie",
|
||||
"past": "Vor {time}"
|
||||
},
|
||||
@@ -833,6 +848,9 @@
|
||||
"name": "Aktion",
|
||||
"type_select": "Aktionstyp",
|
||||
"type": {
|
||||
"choose": {
|
||||
"label": "Auswählen"
|
||||
},
|
||||
"condition": {
|
||||
"label": "Bedingung"
|
||||
},
|
||||
@@ -852,6 +870,21 @@
|
||||
"label": "Ereignis auslösen",
|
||||
"service_data": "Dienstdaten"
|
||||
},
|
||||
"repeat": {
|
||||
"label": "Wiederholen",
|
||||
"type_select": "Wiederholungstyp",
|
||||
"type": {
|
||||
"count": {
|
||||
"label": "Anzahl"
|
||||
},
|
||||
"until": {
|
||||
"label": "Bis"
|
||||
},
|
||||
"while": {
|
||||
"label": "Während"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scene": {
|
||||
"label": "Szene aktivieren"
|
||||
},
|
||||
@@ -1298,7 +1331,7 @@
|
||||
},
|
||||
"cant_edit": "Du kannst nur Elemente bearbeiten, die in der Benutzeroberfläche erstellt wurden.",
|
||||
"caption": "Geräte",
|
||||
"confirm_delete": "Möchtest Du dieses Gerät wirklich löschen?",
|
||||
"confirm_delete": "Möchtest du dieses Gerät wirklich löschen?",
|
||||
"confirm_rename_entity_ids": "Möchten Sie auch die Entitäts-IDs Ihrer Entitäten umbenennen?",
|
||||
"data_table": {
|
||||
"area": "Bereich",
|
||||
@@ -1370,7 +1403,7 @@
|
||||
"introduction2": "Verwenden Sie die Entitätsregistrierung, um den Namen zu überschreiben, die Entität-ID zu ändern oder den Eintrag aus Home Assistant zu entfernen. Beachten Sie, dass das Entfernen des Entitätsregistrierungs-Eintrags die Entität nicht löscht. Folgen Sie dazu dem Link unten und entfernen Sie ihn in der Integrationsseite.",
|
||||
"remove_selected": {
|
||||
"button": "Ausgewählte entfernen",
|
||||
"confirm_partly_text": "Du kannst nur {removable} der ausgewählten {selected} Entitäten entfernen. Entitäten können nur entfernt werden, wenn die Integration die Entitäten nicht mehr bereitstellt. Manchmal musst du Home Assistant neu starten, bevor du die Entitäten einer entfernten Integration entfernen kannst. Möchtest Du die entfernbaren Entitäten wirklich entfernen?",
|
||||
"confirm_partly_text": "Du kannst nur {removable} der ausgewählten {selected} Entitäten entfernen. Entitäten können nur entfernt werden, wenn die Integration die Entitäten nicht mehr bereitstellt. Manchmal musst du Home Assistant neu starten, bevor du die Entitäten einer entfernten Integration entfernen kannst. Möchtest du die entfernbaren Entitäten wirklich entfernen?",
|
||||
"confirm_partly_title": "Es können nur {number} ausgewählte Objekte entfernt werden.",
|
||||
"confirm_text": "Du solltest sie aus deiner Lovelace-Konfiguration und deinen Automatisierungen entfernen, wenn sie diese Entitäten enthalten.",
|
||||
"confirm_title": "Möchtest du {number} Entitäten entfernen?"
|
||||
@@ -1507,9 +1540,9 @@
|
||||
"no_integrations": "Du hast anscheinend noch keine Integrationen konfiguriert. Klicke auf die Schaltfläche unten, um Deine erste Integration hinzuzufügen!",
|
||||
"none": "Noch nichts konfiguriert",
|
||||
"none_found": "Keine Integrationen gefunden",
|
||||
"none_found_detail": "Passe Deine Suchkriterien an.",
|
||||
"none_found_detail": "Passe deine Suchkriterien an.",
|
||||
"note_about_integrations": "Nicht alle Integrationen können über die Benutzeroberfläche konfiguriert werden.",
|
||||
"note_about_website_reference": "Weitere Informationen findest Du auf der ",
|
||||
"note_about_website_reference": "Weitere Informationen findest du auf der ",
|
||||
"rename_dialog": "Bearbeite den Namen dieses Konfigurationseintrags",
|
||||
"rename_input_label": "Eintragsname",
|
||||
"search": "Such-Integrationen"
|
||||
@@ -1595,7 +1628,7 @@
|
||||
},
|
||||
"no_resources": "keine Ressourcen"
|
||||
},
|
||||
"refresh_body": "Die Seite muss aktualisiert werden, um das Entfernen abzuschließen. Möchtest Du sie jetzt aktualisieren?",
|
||||
"refresh_body": "Die Seite muss aktualisiert werden, um das Entfernen abzuschließen. Möchtest du sie jetzt aktualisieren?",
|
||||
"refresh_header": "Möchtest du aktualisieren?",
|
||||
"types": {
|
||||
"css": "Stylesheet",
|
||||
@@ -1620,8 +1653,23 @@
|
||||
"topic": "Topic"
|
||||
},
|
||||
"ozw": {
|
||||
"common": {
|
||||
"instance": "Instanz"
|
||||
},
|
||||
"device_info": {
|
||||
"zwave_info": "Z-Wave Infos"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Netzwerk",
|
||||
"select_instance": "Instanz auswählen"
|
||||
},
|
||||
"network_status": {
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"unknown": "Unbekannt"
|
||||
},
|
||||
"network": {
|
||||
"header": "Netzwerkverwaltung"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1778,6 +1826,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"detail": {
|
||||
"name": "Name"
|
||||
},
|
||||
"headers": {
|
||||
"name": "Name"
|
||||
},
|
||||
"never_scanned": "Nie gescannt"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Benutzer hinzufügen",
|
||||
@@ -1938,7 +1995,7 @@
|
||||
"name": "Name",
|
||||
"new_zone": "Neue Zone",
|
||||
"passive": "Passiv",
|
||||
"passive_note": "Passive Zonen sind im Frontend versteckt und werden nicht als Ort für Device Tracker verwendet. Dies ist nützlich, wenn Du sie nur für Automatisierungen verwenden möchtest.",
|
||||
"passive_note": "Passive Zonen sind im Frontend versteckt und werden nicht als Ort für Device Tracker verwendet. Dies ist nützlich, wenn du sie nur für Automatisierungen verwenden möchtest.",
|
||||
"radius": "Radius",
|
||||
"required_error_msg": "Dieses Feld ist erforderlich",
|
||||
"update": "Aktualisieren"
|
||||
@@ -1948,7 +2005,7 @@
|
||||
"go_to_core_config": "Zur allgemeinen Konfiguration gehen?",
|
||||
"home_zone_core_config": "Der Standort deiner Homezone kann auf der allgemeinen Konfigurationsseite bearbeitet werden. Der Radius der Homezone kann vom Frontend aus noch nicht bearbeitet werden. Möchtest du zur allgemeinen Konfiguration gehen?",
|
||||
"introduction": "Mit Zonen kannst du bestimmte Regionen auf der Erde angeben. Befindet sich eine Person in einer Zone, übernimmt der Zustand den Namen aus der Zone. Zonen können auch als Auslöser oder Bedingung in Automatisierungs-Setups verwendet werden.",
|
||||
"no_zones_created_yet": "Es sieht so aus, als hättest Du noch keine Zonen erstellt."
|
||||
"no_zones_created_yet": "Es sieht so aus, als hättest du noch keine Zonen erstellt."
|
||||
},
|
||||
"zwave": {
|
||||
"button": "Konfigurieren",
|
||||
@@ -2099,6 +2156,7 @@
|
||||
"description": "Vorlagen werden durch die Jinja2-Template-Engine mit einigen für Home Assistant spezifischen Erweiterungen gerendert.",
|
||||
"editor": "Vorlageneditor",
|
||||
"jinja_documentation": "Jinja2 Template Dokumentation",
|
||||
"reset": "Zurücksetzen auf Demo-Vorlage",
|
||||
"template_extensions": "Home Assistant-Vorlagenerweiterungen",
|
||||
"title": "Vorlage",
|
||||
"unknown_error_template": "Unbekannter Fehler beim Rendern der Vorlage"
|
||||
@@ -2137,7 +2195,7 @@
|
||||
"empty_state": {
|
||||
"go_to_integrations_page": "Gehe zur Integrationsseite.",
|
||||
"no_devices": "Auf dieser Seite können Sie Ihre Geräte steuern, es sieht jedoch so aus, als hätten Sie noch keine eingerichtet. Gehen Sie zur Integrationsseite, um damit zu beginnen.",
|
||||
"title": "Willkommen zu Hause"
|
||||
"title": "Willkommen Zuhause"
|
||||
},
|
||||
"entities": {
|
||||
"never_triggered": "Nie ausgelöst"
|
||||
@@ -2180,6 +2238,9 @@
|
||||
"description": "Mit der Schaltflächen-Karte kannst du Schaltflächen hinzufügen, um Aufgaben auszuführen.",
|
||||
"name": "Schaltfläche"
|
||||
},
|
||||
"calendar": {
|
||||
"name": "Kalender"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Karte",
|
||||
"change_type": "Typ ändern",
|
||||
@@ -2206,7 +2267,7 @@
|
||||
"name": "Entität Filter"
|
||||
},
|
||||
"entity": {
|
||||
"description": "Mit der Entitätskarte erhältst Du einen schnellen Überblick über den Status Ihrer Entität.",
|
||||
"description": "Mit der Entitätskarte erhältst du einen schnellen Überblick über den Status Ihrer Entität.",
|
||||
"name": "Entität"
|
||||
},
|
||||
"gauge": {
|
||||
@@ -2233,7 +2294,7 @@
|
||||
"icon_height": "Symbol Höhe",
|
||||
"image": "Bildpfad",
|
||||
"manual": "Manuell",
|
||||
"manual_description": "Möchtest du eine benutzerdefinierte Karte hinzufügen, oder den Yaml-Code von Hand bearbeiten?",
|
||||
"manual_description": "Möchtest du eine benutzerdefinierte Karte hinzufügen oder den YAML-Code von Hand bearbeiten?",
|
||||
"maximum": "Maximum",
|
||||
"minimum": "Minimum",
|
||||
"name": "Name",
|
||||
@@ -2416,8 +2477,8 @@
|
||||
"para_sure": "Bist du dir sicher, dass du die Benutzeroberfläche selbst verwalten möchtest?",
|
||||
"save": "Kontrolle übernehmen",
|
||||
"yaml_config": "Um dir den Einstieg zu erleichtern, findest du hier die aktuelle Konfiguration dieses Dashboards:",
|
||||
"yaml_control": "Um die Kontrolle im YAML-Modus zu übernehmen, erstelle eine YAML-Datei mit dem Namen, den Du in Deiner Konfiguration für dieses Dashboard angegeben hast, oder mit der Standardeinstellung 'ui-lovelace.yaml'.",
|
||||
"yaml_mode": "Du verwendest den YAML-Modus für dieses Dashboard. Dies bedeutet, dass Du Deine Lovelace-Konfiguration nicht über die Benutzeroberfläche ändern kannst. Wenn Du dieses Dashboard über die Benutzeroberfläche verwalten möchtest, entferne \"mode: yaml\" aus Deiner Lovelace-Konfiguration in \"configuration.yaml\"."
|
||||
"yaml_control": "Um die Kontrolle im YAML-Modus zu übernehmen, erstelle eine YAML-Datei mit dem Namen, den du in Deiner Konfiguration für dieses Dashboard angegeben hast, oder mit der Standardeinstellung 'ui-lovelace.yaml'.",
|
||||
"yaml_mode": "Du verwendest den YAML-Modus für dieses Dashboard. Dies bedeutet, dass Du Deine Lovelace-Konfiguration nicht über die Benutzeroberfläche ändern kannst. Wenn du dieses Dashboard über die Benutzeroberfläche verwalten möchtest, entferne \"mode: yaml\" aus Deiner Lovelace-Konfiguration in \"configuration.yaml\"."
|
||||
},
|
||||
"suggest_card": {
|
||||
"add": "Zu Lovelace hinzufügen",
|
||||
@@ -2442,8 +2503,8 @@
|
||||
},
|
||||
"reload_lovelace": "Benutzeroberfläche neu laden",
|
||||
"reload_resources": {
|
||||
"refresh_body": "Du musst die Seite aktualisieren, um das Neuladen abzuschließen. Möchtest Du sie jetzt aktualisieren?",
|
||||
"refresh_header": "Möchtest Du aktualisieren?"
|
||||
"refresh_body": "Du musst die Seite aktualisieren, um das Neuladen abzuschließen. Möchtest du sie jetzt aktualisieren?",
|
||||
"refresh_header": "Möchtest du aktualisieren?"
|
||||
},
|
||||
"unused_entities": {
|
||||
"available_entities": "Dies sind die Entitäten, die du zur Verfügung hast, die aber noch nicht in deiner Lovelace-Benutzeroberfläche enthalten sind.",
|
||||
@@ -2627,6 +2688,7 @@
|
||||
"intro": "Hallo {name}, willkommen bei Home Assistant. Wie möchten Sie Ihre Haus benennen?",
|
||||
"intro_location": "Wir würden gerne wissen, wo Sie wohnen. Diese Daten helfen bei der Anzeige von Informationen und der Einrichtung von Sonnenstands-basierten Automatisierungen. Diese Daten werden niemals außerhalb Ihres Netzwerks weitergegeben.",
|
||||
"intro_location_detect": "Wir können helfen, diese Informationen auszufüllen, indem wir eine einmalige Anfrage an einen externen Dienstleister richten.",
|
||||
"location_name": "Name deiner Home Assistant Installation",
|
||||
"location_name_default": "Home"
|
||||
},
|
||||
"integration": {
|
||||
@@ -2738,6 +2800,7 @@
|
||||
"themes": {
|
||||
"accent_color": "Akzentfarbe",
|
||||
"dark_mode": {
|
||||
"auto": "Automatisch",
|
||||
"dark": "Dunkel",
|
||||
"light": "Hell"
|
||||
},
|
||||
|
||||
@@ -419,9 +419,16 @@
|
||||
"unlock": "Ξεκλείδωμα"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Αναζήτηση πολυμέσων",
|
||||
"media_next_track": "Επόμενο",
|
||||
"media_play": "Αναπαραγωγή",
|
||||
"media_play_pause": "Αναπαραγωγή/παύση",
|
||||
"media_previous_track": "Προηγούμενο",
|
||||
"sound_mode": "Λειτουργία ήχου",
|
||||
"source": "Πηγή",
|
||||
"text_to_speak": "Κείμενο προς εκφώνηση"
|
||||
"text_to_speak": "Κείμενο προς εκφώνηση",
|
||||
"turn_off": "Απενεργοποίηση",
|
||||
"turn_on": "Ενεργοποίηση"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Απόρριψη"
|
||||
@@ -502,8 +509,11 @@
|
||||
"error_required": "Υποχρεωτικό",
|
||||
"loading": "Φόρτωση",
|
||||
"menu": "Μενού",
|
||||
"next": "Επόμενο",
|
||||
"no": "Όχι",
|
||||
"overflow_menu": "Μενού υπερχείλισης",
|
||||
"previous": "Προηγούμενο",
|
||||
"refresh": "Ανανέωση",
|
||||
"save": "Αποθήκευση",
|
||||
"successfully_deleted": "Η διαγραφή ολοκληρώθηκε με επιτυχία",
|
||||
"successfully_saved": "Αποθηκεύτηκε με επιτυχία",
|
||||
@@ -551,6 +561,21 @@
|
||||
"loading_history": "Φόρτωση ιστορικού κατάστασης …",
|
||||
"no_history_found": "Δεν έχει βρεθεί ιστορικό κατάστασης."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Επιλέξτε Πηγή",
|
||||
"content-type": {
|
||||
"album": "Άλμπουμ",
|
||||
"artist": "Καλλιτέχνης",
|
||||
"library": "Βιβλιοθήκη",
|
||||
"server": "Διακομιστής"
|
||||
},
|
||||
"media-player-browser": "Πρόγραμμα περιήγησης πολυμέσων",
|
||||
"no_items": "Χωρίς στοιχεία",
|
||||
"pick": "Επιλέξετε",
|
||||
"pick-media": "Επιλογή μέσων",
|
||||
"play": "Αναπαραγωγή",
|
||||
"play-media": "Αναπαραγωγή πολυμέσων"
|
||||
},
|
||||
"relative_time": {
|
||||
"duration": {
|
||||
"day": "{count} {count, plural,\n one {μέρα}\n other {μέρες}\n}",
|
||||
@@ -560,6 +585,7 @@
|
||||
"week": "{count} {count, plural,\n one {εβδομάδα}\n other {εβδομάδες}\n}"
|
||||
},
|
||||
"future": "Σε {time}",
|
||||
"just_now": "Μόλις τώρα",
|
||||
"never": "Ποτέ",
|
||||
"past": "{time} πριν"
|
||||
},
|
||||
@@ -976,6 +1002,9 @@
|
||||
"sunrise": "Ανατολή ηλίου",
|
||||
"sunset": "Δύση ηλίου"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Ετικέτα"
|
||||
},
|
||||
"template": {
|
||||
"label": "Πρότυπο",
|
||||
"value_template": "Τιμή πρότυπου"
|
||||
@@ -1252,6 +1281,7 @@
|
||||
"caption": "Συσκευές",
|
||||
"confirm_delete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτή τη συσκευή;",
|
||||
"confirm_rename_entity_ids": "Θέλετε επίσης να μετονομάσετε τα αναγνωριστικά οντότητας των οντοτήτων σας;",
|
||||
"confirm_rename_entity_ids_warning": "Αυτό δεν θα αλλάξει καμία ρύθμιση παραμέτρων (όπως αυτοματισμούς, σενάρια, σκηνές, Lovelace) που χρησιμοποιεί αυτήν τη στιγμή αυτές τις οντότητες, θα πρέπει να τις ενημερώσετε μόνοι σας.",
|
||||
"data_table": {
|
||||
"area": "Περιοχή",
|
||||
"battery": "Μπαταρία",
|
||||
@@ -1370,6 +1400,7 @@
|
||||
"caption": "Ενσωματώσεις",
|
||||
"config_entry": {
|
||||
"area": "Στην {area}",
|
||||
"delete": "Διαγραφή",
|
||||
"delete_button": "Διαγραφή {integration}",
|
||||
"delete_confirm": "Είστε σίγουρος ότι θέλετε να διαγραφεί αυτή η ενοποίηση;",
|
||||
"device_unavailable": "συσκευή μη διαθέσιμη",
|
||||
@@ -1381,8 +1412,11 @@
|
||||
"no_area": "Καμία περιοχή",
|
||||
"no_device": "Οντότητες χωρίς συσκευές",
|
||||
"no_devices": "Αυτή η ενοποίηση δεν έχει συσκευές.",
|
||||
"options": "Επιλογές",
|
||||
"rename": "Μετονομασία",
|
||||
"restart_confirm": "Επανεκκινήστε το Home Assistant για να ολοκληρώσετε την κατάργηση αυτής της ενοποίησης",
|
||||
"settings_button": "Επεξεργασία ρυθμίσεων για {integration}",
|
||||
"system_options": "Επιλογές συστήματος",
|
||||
"system_options_button": "Επιλογές συστήματος για {integration}",
|
||||
"unnamed_entry": "Ανώνυμη καταχώριση"
|
||||
},
|
||||
@@ -1420,8 +1454,11 @@
|
||||
"integration_not_found": "Η ενσωμάτωση δε βρέθηκε.",
|
||||
"new": "Ρυθμίστε νέα ενοποίηση",
|
||||
"none": "Δεν υπάρχει διαμόρφωση ακόμα",
|
||||
"none_found": "Δεν βρέθηκαν ενσωματώσεις",
|
||||
"none_found_detail": "Προσαρμόστε τα κριτήρια αναζήτησης.",
|
||||
"note_about_integrations": "Δεν μπορούν όλες οι ενσωματώσεις να διαμορφωθούν από το UI ακόμη.",
|
||||
"note_about_website_reference": "Περισσότερα είναι διαθέσιμα στο",
|
||||
"rename_input_label": "Όνομα καταχώρησης",
|
||||
"search": "Αναζήτηση ενσωματώσεων"
|
||||
},
|
||||
"introduction": "Εδώ είναι δυνατή η διαμόρφωση του Home Assistant και των εξαρτημάτων. Δεν είναι δυνατή η διαμόρφωση όλων από την διεπαφή χρήστη (UI) αλλά εργαζόμαστε πάνω σε αυτό.",
|
||||
@@ -1443,8 +1480,10 @@
|
||||
"cant_edit_default": "Δεν είναι δυνατή η επεξεργασία του τυπικού πίνακα ελέγχου Lovelace από τη διεπαφή χρήστη. Μπορείτε να το αποκρύψετε ορίζοντας έναν άλλο πίνακα ελέγχου ως προεπιλογή.",
|
||||
"cant_edit_yaml": "Δεν είναι δυνατή η επεξεργασία πινάκων εργαλείων που ορίζονται στο YAML από το περιβάλλον εργασίας χρήστη. Αλλάξτε τα στο configuration.yaml.",
|
||||
"caption": "Επισκόπηση",
|
||||
"confirm_delete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτόν τον πίνακα ελέγχου;",
|
||||
"default_dashboard": "Αυτή είναι η προεπιλεγμένη επισκόπηση",
|
||||
"detail": {
|
||||
"dismiss": "Κλείστε",
|
||||
"icon": "Εικονίδιο",
|
||||
"title": "Τίτλος",
|
||||
"title_required": "Απαιτείται τίτλος.",
|
||||
@@ -1452,7 +1491,8 @@
|
||||
},
|
||||
"picker": {
|
||||
"headers": {
|
||||
"default": "Προεπιλογή"
|
||||
"default": "Προεπιλογή",
|
||||
"filename": "Όνομα_αρχείου"
|
||||
},
|
||||
"open": "Άνοιγμα"
|
||||
}
|
||||
@@ -1490,6 +1530,36 @@
|
||||
"stage": "Στάδιο",
|
||||
"zwave_info": "Πληροφορίες Z-Wave"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Δίκτυο",
|
||||
"nodes": "Κόμβοι",
|
||||
"select_instance": "Επιλέξτε στιγμιότυπο "
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "Όλοι οι κόμβοι έχουν ερωτηθεί.",
|
||||
"driverallnodesqueriedsomedead": "Όλοι οι κόμβοι έχουν ερωτηθεί. Μερικοί κόμβοι βρέθηκαν να μην αποκρίνονται.",
|
||||
"driverawakenodesqueries": "Όλοι οι ενεργοποιημένοι κόμβοι έχουν ερωτηθεί. ",
|
||||
"driverfailed": "Αποτυχία σύνδεσης με τον ελεγκτή Z-Wave",
|
||||
"driverready": "Προετοιμασία του ελεγκτή Z-Wave",
|
||||
"driverremoved": "Το πρόγραμμα οδήγησης έχει αφαιρεθεί",
|
||||
"driverreset": "Έγινε επαναφορά του προγράμματος οδήγησης",
|
||||
"offline": "OZWDaemon εκτός σύνδεσης",
|
||||
"ready": "Έτοιμος για σύνδεση",
|
||||
"started": "Συνδεδεμένος με MQTT",
|
||||
"starting": "Σύνδεση με MQTT",
|
||||
"stopped": "Το OpenZWave σταμάτησε"
|
||||
},
|
||||
"offline": "Εκτός σύνδεσης",
|
||||
"online": "Σε σύνδεση",
|
||||
"starting": "Εκκίνηση",
|
||||
"unknown": "Άγνωστο"
|
||||
},
|
||||
"network": {
|
||||
"header": "Διαχείριση δικτύου",
|
||||
"introduction": "Διαχείριση λειτουργιών δικτύου.",
|
||||
"node_count": "{count} κόμβοι"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"complete": "Η διαδικασία της συνέντευξης ολοκληρώθηκε",
|
||||
"configuration": "Λήψη τιμών διαμόρφωσης από τον κόμβο",
|
||||
@@ -1511,6 +1581,14 @@
|
||||
"step": "Βήμα",
|
||||
"title": "Ανανέωση πληροφοριών κόμβου",
|
||||
"wakeup_header": "Οδηγίες εκκίνησης απο"
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Επιλέξτε ένα στιγμιότυπο OpenZWave",
|
||||
"introduction": "Έχετε περισσότερα από ένα στιγμιότυπα OpenZWave σε ενέργεια. Ποιό στιγμιότυπο θέλετε να διαχειριστείτε;"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Προσθήκη κόμβου",
|
||||
"remove_node": "Κατάργηση κόμβου"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1586,7 +1664,7 @@
|
||||
},
|
||||
"script": {
|
||||
"caption": "Δέσμη ενεργειών",
|
||||
"description": "Δημιουργήσετε και να επεξεργαστείτε δέσμες ενεργειών",
|
||||
"description": "Δημιουργήσετε και επεξεργαστείτε δέσμες ενεργειών",
|
||||
"editor": {
|
||||
"alias": "Όνομα",
|
||||
"default_name": "Νέα δέσμη ενεργειών",
|
||||
@@ -1665,6 +1743,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"detail": {
|
||||
"create": "Δημιουργία",
|
||||
"create_and_write": "Δημιουργία και εγγραφή",
|
||||
"delete": "Διαγραφή",
|
||||
"description": "Περιγραφή",
|
||||
"name": "Όνομα",
|
||||
"new_tag": "Νέα ετικέτα",
|
||||
"update": "Ενημέρωση"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Προσθήκη χρήστη",
|
||||
@@ -1840,6 +1929,7 @@
|
||||
"exclude_entity": "Εξαίρεση αυτής της οντότητας από τον Home Assistant",
|
||||
"group": "Ομάδα",
|
||||
"header": "Διαχείριση κόμβων Z-Wave",
|
||||
"introduction": "Εκτελέστε εντολές Z-Wave που επηρεάζουν έναν μόνο κόμβο. Επιλέξτε έναν κόμβο για να δείτε μια λίστα με τις διαθέσιμες εντολές.",
|
||||
"max_associations": "Μέγιστες ενώσεις:",
|
||||
"node_group_associations": "Κόμβοι συσχετίσεων ομάδων",
|
||||
"node_protection": "Προστασία κόμβου",
|
||||
@@ -1944,6 +2034,7 @@
|
||||
"description": "Τα πρότυπα μετατρέπονται χρησιμοποιώντας τη μηχανή πρότυπου Jinja2 με ορισμένες εξειδικευμένες επεκτάσεις του Home Assistant.",
|
||||
"editor": "Πρόγραμμα επεξεργασίας προτύπων",
|
||||
"jinja_documentation": "Έγγραφα πρότυπου Jinja2",
|
||||
"reset": "Επαναφορά στο πρότυπο επίδειξης",
|
||||
"template_extensions": "Επεκτάσεις προτύπου Home Assistant",
|
||||
"title": "Πρότυπα",
|
||||
"unknown_error_template": "Άγνωστο σφάλμα ερμηνείας προτύπου"
|
||||
@@ -2013,6 +2104,10 @@
|
||||
"available_states": "Διαθέσιμες λειτουργίες",
|
||||
"name": "Πίνακας συναγερμών"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "Η κάρτα \"Ημερολόγιο\" εμφανίζει ένα ημερολόγιο που περιλαμβάνει προβολές ημέρας, εβδομάδας και λίστας",
|
||||
"name": "Ημερολόγιο"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Κάρτα",
|
||||
"conditions": "Συνθήκες",
|
||||
@@ -2264,6 +2359,7 @@
|
||||
"warning": {
|
||||
"entity_non_numeric": "Η οντότητα δεν είναι αριθμητική: {entity}",
|
||||
"entity_not_found": "Η οντότητα δεν είναι διαθέσιμη: {entity}",
|
||||
"entity_unavailable": "{entity} δεν είναι διαθέσιμο προς το παρόν",
|
||||
"starting": "Το Home Assistant ξεκινά, ενδέχεται να μην είναι ακόμη διαθέσιμα όλα"
|
||||
}
|
||||
},
|
||||
@@ -2424,6 +2520,7 @@
|
||||
"intro": "Γεια σου {name}, καλώς ήρθες στο Home Assistant. Πώς θα ήθελες να αναφέρεσαι στο σπίτι σου;",
|
||||
"intro_location": "Θα θέλαμε να μάθουμε πού ζεις. Αυτές οι πληροφορίες θα βοηθήσουν στην προβολή πληροφοριών και στη ρύθμιση αυτοματισμών που βασίζονται στη θέση ηλίου. Αυτά τα δεδομένα δεν μοιράζονται ποτέ εκτός του δικτύου σας.",
|
||||
"intro_location_detect": "Μπορούμε να σε βοηθήσουμε να συμπληρώσεις αυτές τις πληροφορίες, κάνοντας μια εφάπαξ αίτηση σε μια εξωτερική υπηρεσία.",
|
||||
"location_name": "Όνομα της εγκατάστασης σας του Home Assistant",
|
||||
"location_name_default": "Σπίτι"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
@@ -419,9 +419,16 @@
|
||||
"unlock": "Unlock"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Browse media",
|
||||
"media_next_track": "Next",
|
||||
"media_play": "Play",
|
||||
"media_play_pause": "Play/pause",
|
||||
"media_previous_track": "Previous",
|
||||
"sound_mode": "Sound mode",
|
||||
"source": "Source",
|
||||
"text_to_speak": "Text to speak"
|
||||
"text_to_speak": "Text to speak",
|
||||
"turn_off": "Turn off",
|
||||
"turn_on": "Turn on"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Dismiss"
|
||||
@@ -554,6 +561,22 @@
|
||||
"loading_history": "Loading state history...",
|
||||
"no_history_found": "No state history found."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Choose Source",
|
||||
"content-type": {
|
||||
"album": "Album",
|
||||
"artist": "Artist",
|
||||
"library": "Library",
|
||||
"playlist": "Playlist",
|
||||
"server": "Server"
|
||||
},
|
||||
"media-player-browser": "Media Player Browser",
|
||||
"no_items": "No items",
|
||||
"pick": "Pick",
|
||||
"pick-media": "Pick Media",
|
||||
"play": "Play",
|
||||
"play-media": "Play Media"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "Picture",
|
||||
"unsupported_format": "Unsupported format, please choose a JPEG, PNG or GIF image."
|
||||
@@ -578,6 +601,7 @@
|
||||
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n}"
|
||||
},
|
||||
"future": "In {time}",
|
||||
"just_now": "Just now",
|
||||
"never": "Never",
|
||||
"past": "{time} ago"
|
||||
},
|
||||
@@ -899,7 +923,7 @@
|
||||
"wait_template": "Wait Template"
|
||||
}
|
||||
},
|
||||
"unsupported_action": "Unsupported action: {action}"
|
||||
"unsupported_action": "No UI support for action: {action}"
|
||||
},
|
||||
"alias": "Name",
|
||||
"conditions": {
|
||||
@@ -965,7 +989,7 @@
|
||||
"zone": "Zone"
|
||||
}
|
||||
},
|
||||
"unsupported_condition": "Unsupported condition: {condition}"
|
||||
"unsupported_condition": "No UI support for condition: {condition}"
|
||||
},
|
||||
"default_name": "New Automation",
|
||||
"description": {
|
||||
@@ -1057,6 +1081,9 @@
|
||||
"sunrise": "Sunrise",
|
||||
"sunset": "Sunset"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Tag"
|
||||
},
|
||||
"template": {
|
||||
"label": "Template",
|
||||
"value_template": "Value template"
|
||||
@@ -1084,7 +1111,7 @@
|
||||
"zone": "Zone"
|
||||
}
|
||||
},
|
||||
"unsupported_platform": "Unsupported platform: {platform}"
|
||||
"unsupported_platform": "No UI support for platform: {platform}"
|
||||
},
|
||||
"unsaved_confirm": "You have unsaved changes. Are you sure you want to leave?"
|
||||
},
|
||||
@@ -1315,6 +1342,7 @@
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"add_prompt": "No {name} have been added using this device yet. You can add one by clicking the + button above.",
|
||||
"automation": {
|
||||
"actions": {
|
||||
"caption": "When something is triggered..."
|
||||
@@ -1334,6 +1362,7 @@
|
||||
"caption": "Devices",
|
||||
"confirm_delete": "Are you sure you want to delete this device?",
|
||||
"confirm_rename_entity_ids": "Do you also want to rename the entity id's of your entities?",
|
||||
"confirm_rename_entity_ids_warning": "This will not change any configuration (like automations, scripts, scenes, Lovelace) that is currently using these entities, you will have to update them yourself.",
|
||||
"data_table": {
|
||||
"area": "Area",
|
||||
"battery": "Battery",
|
||||
@@ -1496,6 +1525,9 @@
|
||||
"no_device": "Entities without devices",
|
||||
"no_devices": "This integration has no devices.",
|
||||
"options": "Options",
|
||||
"reload": "Reload",
|
||||
"reload_confirm": "The integration was reloaded",
|
||||
"reload_restart_confirm": "Restart Home Assistant to finish reloading this integration",
|
||||
"rename": "Rename",
|
||||
"restart_confirm": "Restart Home Assistant to finish removing this integration",
|
||||
"settings_button": "Edit settings for {integration}",
|
||||
@@ -1654,7 +1686,11 @@
|
||||
"topic": "topic"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Configure",
|
||||
"common": {
|
||||
"controller": "Controller",
|
||||
"instance": "Instance",
|
||||
"network": "Network",
|
||||
"node_id": "Node ID",
|
||||
"ozw_instance": "OpenZWave Instance",
|
||||
"zwave": "Z-Wave"
|
||||
@@ -1664,6 +1700,36 @@
|
||||
"stage": "Stage",
|
||||
"zwave_info": "Z-Wave Info"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Network",
|
||||
"nodes": "Nodes",
|
||||
"select_instance": "Select Instance"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "All nodes have been queried",
|
||||
"driverallnodesqueriedsomedead": "All nodes have been queried. Some nodes were found dead",
|
||||
"driverawakenodesqueries": "All awake nodes have been queried",
|
||||
"driverfailed": "Failed to connect to Z-Wave controller",
|
||||
"driverready": "Initializing the Z-Wave controller",
|
||||
"driverremoved": "The driver has been removed",
|
||||
"driverreset": "The driver has been reset",
|
||||
"offline": "OZWDaemon offline",
|
||||
"ready": "Ready to connect",
|
||||
"started": "Connected to MQTT",
|
||||
"starting": "Connecting to MQTT",
|
||||
"stopped": "OpenZWave stopped"
|
||||
},
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"starting": "Starting",
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"network": {
|
||||
"header": "Network Management",
|
||||
"introduction": "Manage network-wide functions.",
|
||||
"node_count": "{count} nodes"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "Refreshing association groups and memberships",
|
||||
"cacheload": "Loading information from the OpenZWave cache file. Battery nodes will stay at this stage until the node wakes up.",
|
||||
@@ -1694,6 +1760,14 @@
|
||||
"title": "Refresh Node Information",
|
||||
"wakeup_header": "Wakeup Instructions for",
|
||||
"wakeup_instructions_source": "Wakeup instructions are sourced from the OpenZWave community device database."
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Select an OpenZWave Instance",
|
||||
"introduction": "You have more than one OpenZWave instance running. Which instance would you like to manage?"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Add Node",
|
||||
"remove_node": "Remove Node"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1819,7 +1893,9 @@
|
||||
"section": {
|
||||
"reloading": {
|
||||
"automation": "Reload automations",
|
||||
"command_line": "Reload command line entities",
|
||||
"core": "Reload location & customizations",
|
||||
"filter": "Reload filter entities",
|
||||
"group": "Reload groups",
|
||||
"heading": "YAML configuration reloading",
|
||||
"input_boolean": "Reload input booleans",
|
||||
@@ -1829,8 +1905,12 @@
|
||||
"input_text": "Reload input texts",
|
||||
"introduction": "Some parts of Home Assistant can reload without requiring a restart. Hitting reload will unload their current YAML configuration and load the new one.",
|
||||
"person": "Reload persons",
|
||||
"rest": "Reload rest entities",
|
||||
"scene": "Reload scenes",
|
||||
"script": "Reload scripts",
|
||||
"statistics": "Reload statistics entities",
|
||||
"template": "Reload template entities",
|
||||
"universal": "Reload universal media player entities",
|
||||
"zone": "Reload zones"
|
||||
},
|
||||
"server_management": {
|
||||
@@ -1850,6 +1930,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "Add tag",
|
||||
"automation_title": "Tag {name} is scanned",
|
||||
"caption": "Tags",
|
||||
"create_automation": "Create automation with tag",
|
||||
"description": "Manage tags",
|
||||
"detail": {
|
||||
"create": "Create",
|
||||
"create_and_write": "Create and Write",
|
||||
"delete": "Delete",
|
||||
"description": "Description",
|
||||
"name": "Name",
|
||||
"new_tag": "New tag",
|
||||
"tag_id": "Tag id",
|
||||
"tag_id_placeholder": "Autogenerated when left empty",
|
||||
"update": "Update"
|
||||
},
|
||||
"edit": "Edit",
|
||||
"headers": {
|
||||
"last_scanned": "Last scanned",
|
||||
"name": "Name"
|
||||
},
|
||||
"never_scanned": "Never scanned",
|
||||
"no_tags": "No tags",
|
||||
"write": "Write"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Add user",
|
||||
@@ -2171,6 +2277,7 @@
|
||||
"description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.",
|
||||
"editor": "Template editor",
|
||||
"jinja_documentation": "Jinja2 template documentation",
|
||||
"reset": "Reset to demo template",
|
||||
"template_extensions": "Home Assistant template extensions",
|
||||
"title": "Template",
|
||||
"unknown_error_template": "Unknown error rendering template"
|
||||
@@ -2252,6 +2359,10 @@
|
||||
"description": "The Button card allows you to add buttons to perform tasks.",
|
||||
"name": "Button"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "The Calendar card displays a calendar including day, week and list views",
|
||||
"name": "Calendar"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Card",
|
||||
"change_type": "Change type",
|
||||
@@ -2706,6 +2817,7 @@
|
||||
"intro": "Hello {name}, welcome to Home Assistant. How would you like to name your home?",
|
||||
"intro_location": "We would like to know where you live. This information will help with displaying information and setting up sun-based automations. This data is never shared outside of your network.",
|
||||
"intro_location_detect": "We can help you fill in this information by making a one-time request to an external service.",
|
||||
"location_name": "Name of your Home Assistant installation",
|
||||
"location_name_default": "Home"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
@@ -13,15 +13,9 @@
|
||||
},
|
||||
"panel": {
|
||||
"calendar": "Calendario",
|
||||
"config": "",
|
||||
"developer_tools": "Herramientas para desarrolladores",
|
||||
"history": "",
|
||||
"logbook": "",
|
||||
"mailbox": "",
|
||||
"map": "",
|
||||
"profile": "Perfil",
|
||||
"shopping_list": "Lista de compras",
|
||||
"states": ""
|
||||
"shopping_list": "Lista de compras"
|
||||
},
|
||||
"state_attributes": {
|
||||
"climate": {
|
||||
@@ -1000,7 +994,6 @@
|
||||
"start": "Inicio"
|
||||
},
|
||||
"mqtt": {
|
||||
"label": "",
|
||||
"payload": "Payload (opcional)",
|
||||
"topic": "Topic"
|
||||
},
|
||||
@@ -1616,7 +1609,6 @@
|
||||
"start_listening": "Comenzar a escuchar",
|
||||
"stop_listening": "Deja de escuchar",
|
||||
"subscribe_to": "Tema para suscribirse",
|
||||
"title": "",
|
||||
"topic": "tema"
|
||||
},
|
||||
"person": {
|
||||
@@ -1947,7 +1939,6 @@
|
||||
},
|
||||
"zwave": {
|
||||
"button": "Configurar",
|
||||
"caption": "",
|
||||
"common": {
|
||||
"index": "Índice",
|
||||
"instance": "Instancia",
|
||||
|
||||
@@ -419,9 +419,16 @@
|
||||
"unlock": "Desbloquear"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Explorar medios",
|
||||
"media_next_track": "Siguiente",
|
||||
"media_play": "Reproducir",
|
||||
"media_play_pause": "Reproducir/pausa",
|
||||
"media_previous_track": "Anterior",
|
||||
"sound_mode": "Modo de sonido",
|
||||
"source": "Fuente",
|
||||
"text_to_speak": "Texto para hablar"
|
||||
"text_to_speak": "Texto para hablar",
|
||||
"turn_off": "Apagar",
|
||||
"turn_on": "Encender"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Descartar"
|
||||
@@ -554,6 +561,26 @@
|
||||
"loading_history": "Cargando historial de estado...",
|
||||
"no_history_found": "No se encontró historial de estado."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Elige la fuente",
|
||||
"content-type": {
|
||||
"album": "Álbum",
|
||||
"artist": "Artista",
|
||||
"library": "Biblioteca",
|
||||
"playlist": "Lista de reproducción",
|
||||
"server": "Servidor"
|
||||
},
|
||||
"media-player-browser": "Navegador del Reproductor Multimedia",
|
||||
"no_items": "No hay elementos",
|
||||
"pick": "Elegir",
|
||||
"pick-media": "Elegir medio",
|
||||
"play": "Reproducir",
|
||||
"play-media": "Reproducir medio"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "Imagen",
|
||||
"unsupported_format": "Formato no soportado, por favor, selecciona una imagen JPEG, PNG o GIF."
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Área",
|
||||
"automation": "Parte de las siguientes automatizaciones",
|
||||
@@ -574,6 +601,7 @@
|
||||
"week": "{count} {count, plural,\none {semana}\nother {semanas}\n}"
|
||||
},
|
||||
"future": "En {time}",
|
||||
"just_now": "Ahora mismo",
|
||||
"never": "Nunca",
|
||||
"past": "Hace {time}"
|
||||
},
|
||||
@@ -656,6 +684,9 @@
|
||||
"required_error_msg": "Este campo es obligatorio",
|
||||
"yaml_not_editable": "La configuración de esta entidad no se puede editar desde la IU. Solo las entidades configuradas desde la IU se pueden configurar desde la IU"
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop": "Recortar"
|
||||
},
|
||||
"more_info_control": {
|
||||
"dismiss": "Descartar diálogo",
|
||||
"edit": "Editar entidad",
|
||||
@@ -892,7 +923,7 @@
|
||||
"wait_template": "Plantilla de espera"
|
||||
}
|
||||
},
|
||||
"unsupported_action": "Acción no admitida: {action}"
|
||||
"unsupported_action": "No hay soporte en la IU para la acción: {action}"
|
||||
},
|
||||
"alias": "Nombre",
|
||||
"conditions": {
|
||||
@@ -958,7 +989,7 @@
|
||||
"zone": "Zona"
|
||||
}
|
||||
},
|
||||
"unsupported_condition": "Condición no admitida: {condition}"
|
||||
"unsupported_condition": "No hay soporte en la IU para la condición: {condition}"
|
||||
},
|
||||
"default_name": "Nueva automatización",
|
||||
"description": {
|
||||
@@ -1050,6 +1081,9 @@
|
||||
"sunrise": "Amanecer",
|
||||
"sunset": "Puesta de sol"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Etiqueta"
|
||||
},
|
||||
"template": {
|
||||
"label": "Plantilla",
|
||||
"value_template": "Valor de la plantilla"
|
||||
@@ -1077,7 +1111,7 @@
|
||||
"zone": "Zona"
|
||||
}
|
||||
},
|
||||
"unsupported_platform": "Plataforma no admitida: {platform}"
|
||||
"unsupported_platform": "No hay soporte en la IU para la plataforma: {platform}"
|
||||
},
|
||||
"unsaved_confirm": "Tienes cambios sin guardar. ¿Estás seguro de que quieres salir?"
|
||||
},
|
||||
@@ -1308,6 +1342,7 @@
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"add_prompt": "Todavía no se ha añadido ningún {name} usando este dispositivo. Puedes añadir uno pulsando en el botón + de arriba.",
|
||||
"automation": {
|
||||
"actions": {
|
||||
"caption": "Cuando algo se activa...."
|
||||
@@ -1327,6 +1362,7 @@
|
||||
"caption": "Dispositivos",
|
||||
"confirm_delete": "¿Estás seguro de que quieres eliminar este dispositivo?",
|
||||
"confirm_rename_entity_ids": "¿También quieres cambiar el nombre de los identificadores de entidad de tus entidades?",
|
||||
"confirm_rename_entity_ids_warning": "Esto no cambiará ninguna configuración (como automatizaciones, scripts, escenas, Lovelace) que estén utilizando actualmente estas entidades, tendrás que actualizarlas tú mismo.",
|
||||
"data_table": {
|
||||
"area": "Área",
|
||||
"battery": "Batería",
|
||||
@@ -1489,6 +1525,9 @@
|
||||
"no_device": "Entidades sin dispositivos",
|
||||
"no_devices": "Esta integración no tiene dispositivos.",
|
||||
"options": "Opciones",
|
||||
"reload": "Recargar",
|
||||
"reload_confirm": "La integración se ha recargado",
|
||||
"reload_restart_confirm": "Reinicia Home Assistant para terminar de recargar esta integración",
|
||||
"rename": "Renombrar",
|
||||
"restart_confirm": "Reinicia Home Assistant para terminar de eliminar esta integración.",
|
||||
"settings_button": "Editar configuración para {integration}",
|
||||
@@ -1647,7 +1686,11 @@
|
||||
"topic": "tema"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Configurar",
|
||||
"common": {
|
||||
"controller": "Controlador",
|
||||
"instance": "Instancia",
|
||||
"network": "Red",
|
||||
"node_id": "ID del Nodo",
|
||||
"ozw_instance": "Instancia OpenZWave",
|
||||
"zwave": "Z-Wave"
|
||||
@@ -1657,6 +1700,36 @@
|
||||
"stage": "Etapa",
|
||||
"zwave_info": "Información Z-Wave"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Red",
|
||||
"nodes": "Nodos",
|
||||
"select_instance": "Seleccionar Instancia"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "Se han consultado todos los nodos.",
|
||||
"driverallnodesqueriedsomedead": "Se han consultado todos los nodos. Se encontraron algunos nodos muertos",
|
||||
"driverawakenodesqueries": "Se han consultado todos los nodos despiertos",
|
||||
"driverfailed": "No se pudo conectar con el controlador Z-Wave",
|
||||
"driverready": "Iniciando el controlador Z-Wave",
|
||||
"driverremoved": "El controlador ha sido eliminado",
|
||||
"driverreset": "El controlador se ha reiniciado",
|
||||
"offline": "OZWDaemon desconectado",
|
||||
"ready": "Listo para conectar",
|
||||
"started": "Conectado a MQTT",
|
||||
"starting": "Conectando con MQTT",
|
||||
"stopped": "OpenZWave detenido"
|
||||
},
|
||||
"offline": "Desconectado",
|
||||
"online": "En línea",
|
||||
"starting": "Iniciando",
|
||||
"unknown": "Desconocido"
|
||||
},
|
||||
"network": {
|
||||
"header": "Administración de la Red",
|
||||
"introduction": "Gestionar las funciones de toda la red.",
|
||||
"node_count": "{count} nodos"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "Refrescando grupos de asociaciones y miembros",
|
||||
"cacheload": "Cargando información del archivo de caché de OpenZWave. Los nodos con batería permanecerán en esta etapa hasta que el nodo se active.",
|
||||
@@ -1687,6 +1760,14 @@
|
||||
"title": "Refrescar Información del Nodo",
|
||||
"wakeup_header": "Instrucciones para despertar a",
|
||||
"wakeup_instructions_source": "Las instrucciones para despertarlo se obtienen de la base de datos de dispositivos de la comunidad OpenZWave."
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Selecciona una instancia de OpenZWave",
|
||||
"introduction": "Tienes más de una instancia de OpenZWave en ejecución. ¿Qué instancia te gustaría gestionar?"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Añadir Nodo",
|
||||
"remove_node": "Eliminar Nodo"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1812,7 +1893,9 @@
|
||||
"section": {
|
||||
"reloading": {
|
||||
"automation": "Recargar automatizaciones",
|
||||
"command_line": "Recargar entidades de línea de comandos",
|
||||
"core": "Recargar ubicación y personalizaciones",
|
||||
"filter": "Recargar entidades de filtro",
|
||||
"group": "Recargar grupos",
|
||||
"heading": "Recargando la configuración YAML",
|
||||
"input_boolean": "Recargar los campos booleanos",
|
||||
@@ -1822,8 +1905,12 @@
|
||||
"input_text": "Recargar los campos de texto",
|
||||
"introduction": "Algunas partes de Home Assistant pueden recargarse sin necesidad de reiniciar. Al pulsar en recargar se descartará la configuración YAML actual y se cargará la nueva.",
|
||||
"person": "Recargar personas",
|
||||
"rest": "Recargar entidades rest",
|
||||
"scene": "Recargar escenas",
|
||||
"script": "Recargar scripts",
|
||||
"statistics": "Recargar entidades de estadísticas",
|
||||
"template": "Recargar entidades de plantilla",
|
||||
"universal": "Recargar entidades de reproductor multimedia universal",
|
||||
"zone": "Recargar zonas"
|
||||
},
|
||||
"server_management": {
|
||||
@@ -1843,6 +1930,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "Añadir etiqueta",
|
||||
"automation_title": "Se escanea la etiqueta {name}",
|
||||
"caption": "Etiquetas",
|
||||
"create_automation": "Crear automatización con etiqueta",
|
||||
"description": "Administrar etiquetas",
|
||||
"detail": {
|
||||
"create": "Crear",
|
||||
"create_and_write": "Crear y escribir",
|
||||
"delete": "Eliminar",
|
||||
"description": "Descripción",
|
||||
"name": "Nombre",
|
||||
"new_tag": "Nueva etiqueta",
|
||||
"tag_id": "ID de etiqueta",
|
||||
"tag_id_placeholder": "Autogenerado cuando se deja vacío",
|
||||
"update": "Actualizar"
|
||||
},
|
||||
"edit": "Editar",
|
||||
"headers": {
|
||||
"last_scanned": "Última vez escaneada",
|
||||
"name": "Nombre"
|
||||
},
|
||||
"never_scanned": "Nunca escaneado",
|
||||
"no_tags": "Sin etiquetas",
|
||||
"write": "Escribir"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Añadir usuario",
|
||||
@@ -2164,6 +2277,7 @@
|
||||
"description": "Las plantillas se muestran utilizando el motor de plantillas Jinja2 con algunas extensiones específicas de Home Assistant.",
|
||||
"editor": "Editor de plantillas",
|
||||
"jinja_documentation": "Documentación de plantilla Jinja2",
|
||||
"reset": "Reiniciar a la plantilla de demostración",
|
||||
"template_extensions": "Extensiones de plantilla de Home Assistant",
|
||||
"title": "Plantillas",
|
||||
"unknown_error_template": "Error desconocido al mostrar la plantilla"
|
||||
@@ -2245,6 +2359,10 @@
|
||||
"description": "La tarjeta Botón te permite agregar botones para realizar tareas.",
|
||||
"name": "Botón"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "La tarjeta Calendario muestra un calendario que incluye vistas de día, semana y lista",
|
||||
"name": "Calendario"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Tarjeta",
|
||||
"change_type": "Cambiar el tipo",
|
||||
@@ -2699,6 +2817,7 @@
|
||||
"intro": "Hola {name}, bienvenido a Home Assistant. ¿Cómo te gustaría llamar a tu casa?",
|
||||
"intro_location": "Nos gustaría saber dónde vives. Esta información ayudará a mostrar información y a configurar automatizaciones basadas en el sol. Estos datos nunca se comparten fuera de tu red.",
|
||||
"intro_location_detect": "Podemos ayudarte a completar esta información haciendo una solicitud única a un servicio externo.",
|
||||
"location_name": "Nombre de tu instalación de Home Assistant",
|
||||
"location_name_default": "Casa"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
@@ -923,10 +923,6 @@
|
||||
"at": "Kell",
|
||||
"label": "Aeg"
|
||||
},
|
||||
"webhook": {
|
||||
"label": "",
|
||||
"webhook_id": ""
|
||||
},
|
||||
"zone": {
|
||||
"enter": "Sisenemine",
|
||||
"entity": "Asukohaga olem",
|
||||
|
||||
@@ -419,9 +419,16 @@
|
||||
"unlock": "Déverrouiller"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Parcourir les médias",
|
||||
"media_next_track": "Suivant",
|
||||
"media_play": "Lecture",
|
||||
"media_play_pause": "Lecture/pause",
|
||||
"media_previous_track": "Précédent",
|
||||
"sound_mode": "Mode sonore",
|
||||
"source": "Source",
|
||||
"text_to_speak": "Texte à lire"
|
||||
"text_to_speak": "Texte à lire",
|
||||
"turn_off": "Éteindre",
|
||||
"turn_on": "Allumer"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Ignorer"
|
||||
@@ -554,6 +561,26 @@
|
||||
"loading_history": "Chargement de l'historique des valeurs ...",
|
||||
"no_history_found": "Aucun historique des valeurs trouvé."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Choisissez la source",
|
||||
"content-type": {
|
||||
"album": "Album",
|
||||
"artist": "Artiste",
|
||||
"library": "Bibliothèque",
|
||||
"playlist": "Liste de lecture",
|
||||
"server": "Serveur"
|
||||
},
|
||||
"media-player-browser": "Lecteur multimédia",
|
||||
"no_items": "Aucun éléments",
|
||||
"pick": "Choisir",
|
||||
"pick-media": "Choisissez un média",
|
||||
"play": "Lecture",
|
||||
"play-media": "Lire le média"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "Image",
|
||||
"unsupported_format": "Format non pris en charge, veuillez choisir une image JPEG, PNG ou GIF."
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Pièce",
|
||||
"automation": "Partie des automatisations suivantes",
|
||||
@@ -574,6 +601,7 @@
|
||||
"week": "{count} {count, plural,\nzero {semaine}\none {semaine}\nother {semaines}\n}"
|
||||
},
|
||||
"future": "Dans {time}",
|
||||
"just_now": "Juste maintenant",
|
||||
"never": "Jamais",
|
||||
"past": "Il y a {time}"
|
||||
},
|
||||
@@ -656,6 +684,9 @@
|
||||
"required_error_msg": "Ce champ est requis",
|
||||
"yaml_not_editable": "Les paramètres de cette entité ne peuvent pas être modifiés à partir de l'interface utilisateur. Seules les entités configurées à partir de l'interface utilisateur sont configurables à partir de l'interface utilisateur."
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop": "Recadrer"
|
||||
},
|
||||
"more_info_control": {
|
||||
"dismiss": "Fermer la fenêtre de dialogue",
|
||||
"edit": "Modifier l'entité",
|
||||
@@ -892,7 +923,7 @@
|
||||
"wait_template": "Template d'attente"
|
||||
}
|
||||
},
|
||||
"unsupported_action": "Action non supportée : {action}"
|
||||
"unsupported_action": "Pas d'interface utilisateur pour l'action : {action}"
|
||||
},
|
||||
"alias": "Nom",
|
||||
"conditions": {
|
||||
@@ -958,7 +989,7 @@
|
||||
"zone": "Zone"
|
||||
}
|
||||
},
|
||||
"unsupported_condition": "Condition non supportée: {condition}"
|
||||
"unsupported_condition": "Pas d'interface utilisateur pour la condition : {condition}"
|
||||
},
|
||||
"default_name": "Nouvelle automatisation",
|
||||
"description": {
|
||||
@@ -1050,6 +1081,9 @@
|
||||
"sunrise": "Lever du soleil",
|
||||
"sunset": "Coucher du soleil"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Balise"
|
||||
},
|
||||
"template": {
|
||||
"label": "Template",
|
||||
"value_template": "Contenu du template"
|
||||
@@ -1077,7 +1111,7 @@
|
||||
"zone": "Zone"
|
||||
}
|
||||
},
|
||||
"unsupported_platform": "Plateforme non supportée : {platform}"
|
||||
"unsupported_platform": "Pas d'interface utilisateur pour la plateforme : {platform}"
|
||||
},
|
||||
"unsaved_confirm": "Vous avez des changements non enregistrés. Êtes-vous sûr de vouloir quitter?"
|
||||
},
|
||||
@@ -1308,6 +1342,7 @@
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"add_prompt": "Aucun {name} n'a encore été ajouté à l'aide de cet appareil. Vous pouvez en ajouter un en cliquant sur le bouton + ci-dessus.",
|
||||
"automation": {
|
||||
"actions": {
|
||||
"caption": "Quand quelque chose est déclenché ..."
|
||||
@@ -1327,6 +1362,7 @@
|
||||
"caption": "Appareils",
|
||||
"confirm_delete": "Voulez-vous vraiment supprimer cet appareil ?",
|
||||
"confirm_rename_entity_ids": "Voulez-vous aussi renommer les ID de vos entités?",
|
||||
"confirm_rename_entity_ids_warning": "Cela ne changera aucune configuration (comme les automatismes, les scripts, les scènes, Lovelace) qui utilise actuellement ces entités, vous devrez les mettre à jour vous-même.",
|
||||
"data_table": {
|
||||
"area": "Zone",
|
||||
"battery": "Batterie",
|
||||
@@ -1383,7 +1419,7 @@
|
||||
"filter": {
|
||||
"filter": "Filtre",
|
||||
"show_disabled": "Afficher les entités désactivées",
|
||||
"show_readonly": "Afficher les entités désactivées",
|
||||
"show_readonly": "Afficher les entités en lecture seule",
|
||||
"show_unavailable": "Afficher les entités non disponibles"
|
||||
},
|
||||
"header": "Entités",
|
||||
@@ -1489,6 +1525,9 @@
|
||||
"no_device": "Entités sans appareils",
|
||||
"no_devices": "Cette intégration n'a pas d'appareils.",
|
||||
"options": "Options",
|
||||
"reload": "Recharger",
|
||||
"reload_confirm": "L'intégration a été rechargée",
|
||||
"reload_restart_confirm": "Redémarrer Home Assistant pour finaliser le rechargement de cette intégration",
|
||||
"rename": "Renommer",
|
||||
"restart_confirm": "Redémarrer Home Assistant pour terminer la suppression de cette intégration",
|
||||
"settings_button": "Modifier les paramètres pour {integration}",
|
||||
@@ -1647,7 +1686,11 @@
|
||||
"topic": "sujet"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Configurer",
|
||||
"common": {
|
||||
"controller": "Contrôleur",
|
||||
"instance": "Instance",
|
||||
"network": "Réseau",
|
||||
"node_id": "ID du nœud",
|
||||
"ozw_instance": "Instance Z-Wave",
|
||||
"zwave": "Z-Wave"
|
||||
@@ -1657,15 +1700,54 @@
|
||||
"stage": "Étape",
|
||||
"zwave_info": "Informations Z-Wave"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Réseau",
|
||||
"nodes": "Nœuds",
|
||||
"select_instance": "Sélectionnez une instance"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "Tous les nœuds ont été interrogés",
|
||||
"driverallnodesqueriedsomedead": "Tous les nœuds ont été interrogés. Certains nœuds ont été trouvés morts",
|
||||
"driverawakenodesqueries": "Tous les nœuds éveillés ont été interrogés",
|
||||
"driverfailed": "Échec de la connexion au contrôleur Z-Wave",
|
||||
"driverready": "Initialisation du contrôleur Z-Wave",
|
||||
"driverremoved": "Le pilote a été supprimé",
|
||||
"driverreset": "Le pilote a été réinitialisé",
|
||||
"offline": "OZWDaemon hors ligne",
|
||||
"ready": "Prêt à se connecter",
|
||||
"started": "Connecté à MQTT",
|
||||
"starting": "Connexion à MQTT",
|
||||
"stopped": "OpenZWave arrêté"
|
||||
},
|
||||
"offline": "Hors ligne",
|
||||
"online": "En ligne",
|
||||
"starting": "Démarrage",
|
||||
"unknown": "Inconnu"
|
||||
},
|
||||
"network": {
|
||||
"header": "Gestion du réseau",
|
||||
"introduction": "Διαχείρηση λειτουργιών δικτύου",
|
||||
"node_count": "{count} nœuds"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "Rafraîchissement les associations et les membres",
|
||||
"cacheload": "Chargement des informations à partir du fichier de cache OpenZWave. Les nœuds de batterie resteront à cette étape jusqu'à ce que le nœud se réveille.",
|
||||
"complete": "Le processus d'interrogation est terminé",
|
||||
"configuration": "Obtention la configuration à partir du nœud",
|
||||
"dynamic": "Obtention de valeurs fréquemment modifiées du nœud",
|
||||
"instances": "Obtention des détails sur les instances ou les canaux pris en charge par un appareil",
|
||||
"manufacturerspecific1": "Obtention des codes d'identification du fabricant et du produit auprès du nœud",
|
||||
"manufacturerspecific2": "Obtention de codes d’identification supplémentaires du fabricant et du produit à partir du nœud",
|
||||
"neighbors": "Obtenir une liste des nœuds voisins",
|
||||
"nodeinfo": "Obtention des classes de commande prises en charge à partir du nœud",
|
||||
"nodeplusinfo": "Obtention d'informations Z-Wave + à partir du nœud",
|
||||
"probe": "Vérification si le nœud est éveillé/vivant",
|
||||
"protocolinfo": "Obtention des capacités Z-Wave de base de ce nœud à partir du contrôleur",
|
||||
"session": "Obtention de valeurs rarement modifiées du nœud",
|
||||
"static": "Obtention des valeurs statiques de l'appareil"
|
||||
"static": "Obtention des valeurs statiques de l'appareil",
|
||||
"versions": "Obtention d'informations sur les versions des microprogrammes et des classes de commande",
|
||||
"wakeup": "Configuration de la prise en charge des files d'attente et des messages de réveil"
|
||||
},
|
||||
"refresh_node": {
|
||||
"battery_note": "Si le nœud est alimenté par batterie, assurez-vous de l'activer avant de continuer",
|
||||
@@ -1678,6 +1760,14 @@
|
||||
"title": "Actualiser les informations du nœud",
|
||||
"wakeup_header": "Instructions de réveil pour",
|
||||
"wakeup_instructions_source": "Les instructions de réveil proviennent de la base de données de la communauté OpenZWave."
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Sélectionnez une instance OpenZWave",
|
||||
"introduction": "Vous avez plus d'une instance OpenZWave en cours d'exécution. Quelle instance souhaitez-vous gérer ?"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Ajouter un nœud",
|
||||
"remove_node": "Supprimer le nœud"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1803,7 +1893,9 @@
|
||||
"section": {
|
||||
"reloading": {
|
||||
"automation": "Recharger les automatisations",
|
||||
"command_line": "Recharger les entités de ligne de commande",
|
||||
"core": "Recharger les emplacements et personnalisations",
|
||||
"filter": "Recharger les entités de filtre",
|
||||
"group": "Recharger les groupes",
|
||||
"heading": "Rechargement de la configuration",
|
||||
"input_boolean": "Recharger les booléens d'entrée",
|
||||
@@ -1813,8 +1905,11 @@
|
||||
"input_text": "Recharger les textes d'entrée",
|
||||
"introduction": "Certaines parties de Home Assistant peuvent être rechargées sans nécessiter de redémarrage. Le fait de cliquer sur recharger déchargera leur configuration actuelle et chargera la nouvelle.",
|
||||
"person": "Recharger les personnes",
|
||||
"rest": "Recharger les entités REST",
|
||||
"scene": "Recharger les scènes",
|
||||
"script": "Recharger les scripts",
|
||||
"statistics": "Recharger les entités de statistiques",
|
||||
"template": "Recharger les entités modèles",
|
||||
"zone": "Recharger les zones"
|
||||
},
|
||||
"server_management": {
|
||||
@@ -1834,6 +1929,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "Ajouter une balise",
|
||||
"automation_title": "La balise {name} est analysée",
|
||||
"caption": "Balises",
|
||||
"create_automation": "Créer une automatisation avec une balise",
|
||||
"description": "Gérer les balises",
|
||||
"detail": {
|
||||
"create": "Créer",
|
||||
"create_and_write": "Créer et Ecrire",
|
||||
"delete": "Supprimer",
|
||||
"description": "Description",
|
||||
"name": "Nom",
|
||||
"new_tag": "Nouvelle balise",
|
||||
"tag_id": "Id de balise",
|
||||
"tag_id_placeholder": "Généré automatiquement lorsque laissé vide",
|
||||
"update": "Mise à jour"
|
||||
},
|
||||
"edit": "Modifier",
|
||||
"headers": {
|
||||
"last_scanned": "Dernière analyse",
|
||||
"name": "Nom"
|
||||
},
|
||||
"never_scanned": "Jamais scanné",
|
||||
"no_tags": "Aucunes balises",
|
||||
"write": "Écrire"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Ajouter un utilisateur",
|
||||
@@ -2155,6 +2276,7 @@
|
||||
"description": "Les modèles sont rendus à l'aide du moteur de modèles Jinja2 avec certaines extensions spécifiques de Home Assistant.",
|
||||
"editor": "Éditeur de modèles",
|
||||
"jinja_documentation": "Documentation de modèle Jinja2",
|
||||
"reset": "Réinitialiser au modèle de démonstration",
|
||||
"template_extensions": "Extensions de modèles de Home Assistant",
|
||||
"title": "Modèle",
|
||||
"unknown_error_template": "Erreur inconnue lors du rendu du modèle"
|
||||
@@ -2236,6 +2358,10 @@
|
||||
"description": "La carte Bouton vous permet d'ajouter des boutons pour effectuer des tâches.",
|
||||
"name": "Bouton"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "La carte Calendrier affiche un calendrier comprenant des vues par jour, par semaine et par liste",
|
||||
"name": "Calendrier"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Carte",
|
||||
"change_type": "Changer le type",
|
||||
@@ -2497,7 +2623,7 @@
|
||||
},
|
||||
"menu": {
|
||||
"close": "Fermer",
|
||||
"configure_ui": "Configurer l'interface utilisateur",
|
||||
"configure_ui": "Modifier le tableau de bord",
|
||||
"exit_edit_mode": "Quitter le mode d'édition de l'interface utilisateur",
|
||||
"help": "Aide",
|
||||
"refresh": "Actualiser",
|
||||
@@ -2690,6 +2816,7 @@
|
||||
"intro": "Bonjour {name}, bienvenue dans Home Assistant. Comment voudriez-vous nommer votre maison?",
|
||||
"intro_location": "Nous aimerions savoir où vous habitez. Ces informations vous aideront à afficher des informations et à configurer des automatismes basés sur le soleil. Ces données ne sont jamais partagées en dehors de votre réseau.",
|
||||
"intro_location_detect": "Nous pouvons vous aider à renseigner ces informations en adressant une demande unique à un service externe.",
|
||||
"location_name": "Nom de votre installation Home Assistant",
|
||||
"location_name_default": "Maison"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
@@ -195,10 +195,8 @@
|
||||
},
|
||||
"triggers": {
|
||||
"add": "Trigger tafoegje",
|
||||
"header": "",
|
||||
"type": {
|
||||
"homeassistant": {
|
||||
"label": "",
|
||||
"shutdown": "Ofslúte",
|
||||
"start": "Opstarte"
|
||||
},
|
||||
|
||||
@@ -554,6 +554,10 @@
|
||||
"loading_history": "טוען היסטוריה...",
|
||||
"no_history_found": "לא נמצאה היסטוריה"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "תמונה",
|
||||
"unsupported_format": "פורמט לא נתמך, בחר תמונת JPEG, PNG או GIF."
|
||||
},
|
||||
"related-items": {
|
||||
"area": "אֵזוֹר",
|
||||
"automation": "חלק מהאוטומציות הבאות",
|
||||
@@ -656,6 +660,9 @@
|
||||
"required_error_msg": "שדה זה הוא חובה",
|
||||
"yaml_not_editable": "אין אפשרות לערוך את ההגדרות של ישות זו מממשק המשתמש. רק ישויות שהוגדרו מממשק המשתמש ניתנות להגדרה מתוך ממשק המשתמש."
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop": "חתוך"
|
||||
},
|
||||
"more_info_control": {
|
||||
"dismiss": "סגור",
|
||||
"edit": "ערוך יישות",
|
||||
@@ -1050,6 +1057,9 @@
|
||||
"sunrise": "זריחה",
|
||||
"sunset": "שקיעה"
|
||||
},
|
||||
"tag": {
|
||||
"label": "תג "
|
||||
},
|
||||
"template": {
|
||||
"label": "תבנית",
|
||||
"value_template": "תבנית ערך"
|
||||
@@ -1327,6 +1337,7 @@
|
||||
"caption": "התקנים",
|
||||
"confirm_delete": "האם אתה בטוח שברצונך למחוק מכשיר זה?",
|
||||
"confirm_rename_entity_ids": "האם אתה רוצה גם לשנות את המזהים של הישויות שלך?",
|
||||
"confirm_rename_entity_ids_warning": "שינוי זה לא ישפיע על התצורה (כמו אוטומציות, קבצי Script, סצנות, Lovelace) המשתמשת כעת בישויות אלה, יהיה עליך לעדכן אותן בעצמך.",
|
||||
"data_table": {
|
||||
"area": "אזור",
|
||||
"battery": "סוללה",
|
||||
@@ -1656,6 +1667,37 @@
|
||||
"node_failed": "הרכיב נכשל",
|
||||
"stage": "שלב",
|
||||
"zwave_info": "מידע Z-Wave"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "מרענן קבוצות וחברויות",
|
||||
"cacheload": "טוען מידע מקובץ המטמון של OpenZWave. רכיבי סוללה יישארו בשלב זה עד שהרכיב יתעורר.",
|
||||
"complete": "תהליך הראיון הושלם",
|
||||
"configuration": "מקבל ערכי תצורה מהרכיב",
|
||||
"dynamic": "מקבל ערכים המשתנים לעתים קרובות מהרכיב",
|
||||
"instances": "מקבל פרטים אודות המופעים או הערוצים שבהם הרכיב תומך",
|
||||
"manufacturerspecific1": "קבלת קודי יצרן ומזהי מוצר מהרכיב",
|
||||
"manufacturerspecific2": "מקבל קודי יצרן ומזהי מוצר נוספים מהרכיב",
|
||||
"neighbors": "קבלת רשימת שכנים מהרכיב",
|
||||
"nodeinfo": "קבלת מחלקות פקודות נתמכות מהרכיב",
|
||||
"nodeplusinfo": "קבלת מידע Z-Wave+ מהרכיב",
|
||||
"probe": "בדיקה האם הרכיב ער/חי",
|
||||
"protocolinfo": "קבלת יכולות Z-Wave בסיסיות של רכיב זה מהבקר",
|
||||
"session": "מקבל ערכים המשתנים לעתים רחוקות מהרכיב",
|
||||
"static": "מקבל ערכים סטטיים מהרכיב",
|
||||
"versions": "מקבל מידע אודות גירסאות קושחה ומחלקות פקודות",
|
||||
"wakeup": "הגדרת תמיכה עבור תורי התעוררות והודעות"
|
||||
},
|
||||
"refresh_node": {
|
||||
"battery_note": "אם הרכיב מופעל באמצעות סוללה, הקפד להעיר אותו לפני שתמשיך",
|
||||
"complete": "רענון הרכיב הושלם",
|
||||
"description": "זה יגיד ל- OpenZWave לראיין מחדש את הרכיב ולעדכן את הפקודות, היכולות והערכים של הרכיב.",
|
||||
"node_status": "מצה הרכיב",
|
||||
"refreshing_description": "מרענן מידע על הרכיב",
|
||||
"start_refresh_button": "התחל לרענן",
|
||||
"step": "שלב",
|
||||
"title": "רענן מידע הרכיב",
|
||||
"wakeup_header": "הוראות התעוררות עבור",
|
||||
"wakeup_instructions_source": "הוראות ההתעוררות מקורן במסד הנתונים של קהילת ה OpenZWave."
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1812,6 +1854,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "הוסף תג",
|
||||
"automation_title": "התג {name} נסרק",
|
||||
"caption": "תגים",
|
||||
"create_automation": "צור אוטומציה עם תג",
|
||||
"description": "ניהול תגים",
|
||||
"detail": {
|
||||
"create": "צור",
|
||||
"create_and_write": "צור וכתוב",
|
||||
"delete": "מחק",
|
||||
"description": "תיאור",
|
||||
"name": "שם",
|
||||
"new_tag": "תג חדש",
|
||||
"tag_id": "מזהה תג",
|
||||
"tag_id_placeholder": "מחולל אוטומטית אם ריק",
|
||||
"update": "עדכן"
|
||||
},
|
||||
"edit": "ערוך",
|
||||
"headers": {
|
||||
"last_scanned": "נסרק לאחרונה",
|
||||
"name": "שם"
|
||||
},
|
||||
"no_tags": "אין תגים",
|
||||
"write": "כתוב"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "הוסף משתמש",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"rename_dialog": "इस कॉन्फ़िगरेशन प्रविष्टि का नाम संपादित करें",
|
||||
"rename_input_label": "प्रवेश का नाम"
|
||||
},
|
||||
"introduction": "In questa schermata è possibile configurare Home Assistant e i suoi componenti. Non è ancora possibile configurare tutto tramite l'interfaccia, ma ci stiamo lavorando.",
|
||||
"logs": {
|
||||
"title": "लॉग्स"
|
||||
},
|
||||
@@ -363,6 +364,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"logbook": {
|
||||
"showing_entries": "Visualizza gli inserimenti per"
|
||||
},
|
||||
"lovelace": {
|
||||
"cards": {
|
||||
"starting": {
|
||||
@@ -370,6 +374,9 @@
|
||||
"header": "होम असिस्टेंट शुरू हो रहा है ..."
|
||||
}
|
||||
},
|
||||
"changed_toast": {
|
||||
"message": "La configurazione della interfaccia di Lovelaceper questa dashboard è stata aggiornata, vuoi ricaricare la pagina per vedere i cambiamenti?"
|
||||
},
|
||||
"editor": {
|
||||
"card": {
|
||||
"entity": {
|
||||
|
||||
@@ -419,9 +419,16 @@
|
||||
"unlock": "Sbloccato"
|
||||
},
|
||||
"media_player": {
|
||||
"browse_media": "Sfoglia i file multimediali",
|
||||
"media_next_track": "Avanti",
|
||||
"media_play": "Riproduci",
|
||||
"media_play_pause": "Riproduci/pausa",
|
||||
"media_previous_track": "Indietro",
|
||||
"sound_mode": "Modalità audio",
|
||||
"source": "Fonte",
|
||||
"text_to_speak": "Testo da leggere"
|
||||
"text_to_speak": "Testo da leggere",
|
||||
"turn_off": "Spegni",
|
||||
"turn_on": "Accendi"
|
||||
},
|
||||
"persistent_notification": {
|
||||
"dismiss": "Rimuovi"
|
||||
@@ -554,6 +561,26 @@
|
||||
"loading_history": "Caricamento storico...",
|
||||
"no_history_found": "Nessuno storico trovato."
|
||||
},
|
||||
"media-browser": {
|
||||
"choose-source": "Scegli origine",
|
||||
"content-type": {
|
||||
"album": "Album",
|
||||
"artist": "Artista",
|
||||
"library": "Libreria",
|
||||
"playlist": "Elenco di riproduzione",
|
||||
"server": "Server"
|
||||
},
|
||||
"media-player-browser": "Lettore multimediale",
|
||||
"no_items": "Nessun elemento",
|
||||
"pick": "Scegli",
|
||||
"pick-media": "Seleziona file multimediali",
|
||||
"play": "Riproduci",
|
||||
"play-media": "Riproduci file multimediali"
|
||||
},
|
||||
"picture-upload": {
|
||||
"label": "Immagine",
|
||||
"unsupported_format": "Formato non supportato, scegliere un'immagine JPEG, PNG o GIF."
|
||||
},
|
||||
"related-items": {
|
||||
"area": "Area",
|
||||
"automation": "Parte delle seguenti automazioni",
|
||||
@@ -574,6 +601,7 @@
|
||||
"week": "{count} {count, plural,\none {settimana}\nother {settimane}\n}"
|
||||
},
|
||||
"future": "tra {time}",
|
||||
"just_now": "Proprio ora",
|
||||
"never": "Mai",
|
||||
"past": "{time} fa"
|
||||
},
|
||||
@@ -656,6 +684,9 @@
|
||||
"required_error_msg": "Questo campo è obbligatorio",
|
||||
"yaml_not_editable": "Le impostazioni di questa entità non possono essere modificate dall'Interfaccia Utente. Solo le entità impostate dall'Interfaccia Utente sono configurabili dall'Interfaccia Utente."
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop": "Ritaglia"
|
||||
},
|
||||
"more_info_control": {
|
||||
"dismiss": "Chiudi finestra di dialogo",
|
||||
"edit": "Modifica entità",
|
||||
@@ -892,7 +923,7 @@
|
||||
"wait_template": "Modello di attesa"
|
||||
}
|
||||
},
|
||||
"unsupported_action": "Azione non supportata: {action}"
|
||||
"unsupported_action": "Nessun supporto dell'Interfaccia Utente per l'azione: {action}"
|
||||
},
|
||||
"alias": "Nome",
|
||||
"conditions": {
|
||||
@@ -958,7 +989,7 @@
|
||||
"zone": "Zona"
|
||||
}
|
||||
},
|
||||
"unsupported_condition": "Condizione non supportata: {condition}"
|
||||
"unsupported_condition": "Nessun supporto dell'Interfaccia Utente per la condizione: {condition}"
|
||||
},
|
||||
"default_name": "Nuova Automazione",
|
||||
"description": {
|
||||
@@ -1050,6 +1081,9 @@
|
||||
"sunrise": "Alba",
|
||||
"sunset": "Tramonto"
|
||||
},
|
||||
"tag": {
|
||||
"label": "Etichetta"
|
||||
},
|
||||
"template": {
|
||||
"label": "Modello",
|
||||
"value_template": "Valore modello"
|
||||
@@ -1077,7 +1111,7 @@
|
||||
"zone": "Zona"
|
||||
}
|
||||
},
|
||||
"unsupported_platform": "Piattaforma non supportata: {platform}"
|
||||
"unsupported_platform": "Nessun supporto dell'Interfaccia Utente per la piattaforma: {platform}"
|
||||
},
|
||||
"unsaved_confirm": "Hai delle modifiche non salvate. Sei sicuro di voler uscire?"
|
||||
},
|
||||
@@ -1308,6 +1342,7 @@
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"add_prompt": "Nessun {name} è stato ancora aggiunto utilizzando questo dispositivo. Puoi aggiungerne uno facendo clic sul pulsante + sopra.",
|
||||
"automation": {
|
||||
"actions": {
|
||||
"caption": "Quando qualcosa si attiva..."
|
||||
@@ -1327,6 +1362,7 @@
|
||||
"caption": "Dispositivi",
|
||||
"confirm_delete": "Sei sicuro di voler eliminare questo dispositivo?",
|
||||
"confirm_rename_entity_ids": "Vuoi anche rinominare gli ID entità delle tue entità?",
|
||||
"confirm_rename_entity_ids_warning": "Questo non cambierà alcuna configurazione (come automazioni, script, scene, Lovelace) che attualmente utilizza queste entità, dovrai aggiornarle tu stesso.",
|
||||
"data_table": {
|
||||
"area": "Area",
|
||||
"battery": "Batteria",
|
||||
@@ -1489,6 +1525,9 @@
|
||||
"no_device": "Entità senza dispositivi",
|
||||
"no_devices": "Questa integrazione non ha dispositivi.",
|
||||
"options": "Opzioni",
|
||||
"reload": "Ricarica",
|
||||
"reload_confirm": "L'integrazione è stata ricaricata",
|
||||
"reload_restart_confirm": "Riavvia Home Assistant per completare il ricaricamento di questa integrazione",
|
||||
"rename": "Rinomina",
|
||||
"restart_confirm": "Riavvia Home Assistant per completare la rimozione di questa integrazione",
|
||||
"settings_button": "Modificare le impostazioni per {integration}.",
|
||||
@@ -1647,7 +1686,11 @@
|
||||
"topic": "argomento"
|
||||
},
|
||||
"ozw": {
|
||||
"button": "Configura",
|
||||
"common": {
|
||||
"controller": "Controllore",
|
||||
"instance": "Istanza",
|
||||
"network": "Rete",
|
||||
"node_id": "ID del nodo",
|
||||
"ozw_instance": "Istanza OpenZWave",
|
||||
"zwave": "Z-Wave"
|
||||
@@ -1656,6 +1699,75 @@
|
||||
"node_failed": "Nodo non riuscito",
|
||||
"stage": "Fase",
|
||||
"zwave_info": "Informazioni Z-Wave"
|
||||
},
|
||||
"navigation": {
|
||||
"network": "Rete",
|
||||
"nodes": "Nodi",
|
||||
"select_instance": "Seleziona istanza"
|
||||
},
|
||||
"network_status": {
|
||||
"details": {
|
||||
"driverallnodesqueried": "Tutti i nodi sono stati interrogati.",
|
||||
"driverallnodesqueriedsomedead": "Tutti i nodi sono stati interrogati. Alcuni nodi sono stati trovati morti",
|
||||
"driverawakenodesqueries": "Tutti i nodi svegli sono stati interrogati",
|
||||
"driverfailed": "Impossibile connettersi al controller Z-Wave",
|
||||
"driverready": "Inizializzazione del controller Z-Wave",
|
||||
"driverremoved": "Il driver è stato rimosso",
|
||||
"driverreset": "Il driver è stato reimpostato",
|
||||
"offline": "OZWDaemon non in linea",
|
||||
"ready": "Pronto per la connessione",
|
||||
"started": "Collegato a MQTT",
|
||||
"starting": "In collegamento a MQTT",
|
||||
"stopped": "OpenZWave si è fermato"
|
||||
},
|
||||
"offline": "Disconnesso",
|
||||
"online": "In linea",
|
||||
"starting": "In avvio",
|
||||
"unknown": "Sconosciuto"
|
||||
},
|
||||
"network": {
|
||||
"header": "Gestione della rete",
|
||||
"introduction": "Gestisci le funzioni a livello di rete.",
|
||||
"node_count": "{count} nodi"
|
||||
},
|
||||
"node_query_stages": {
|
||||
"associations": "Aggiornamento dell'associazione gruppi e appartenenze",
|
||||
"cacheload": "Caricamento delle informazioni dal file di cache di OpenZWave. I nodi a batteria rimarranno in questa fase fino a quando il nodo non si riattiva.",
|
||||
"complete": "Il processo di interrogazione è completo",
|
||||
"configuration": "Recupero dei valori di configurazione dal nodo",
|
||||
"dynamic": "Recupero di valori che cambiano di frequente dal nodo",
|
||||
"instances": "Recupero dei dettagli sulle istanze o sui canali supportati da un dispositivo",
|
||||
"manufacturerspecific1": "Recupero dei codici ID produttore e prodotto dal nodo",
|
||||
"manufacturerspecific2": "Recupero di ulteriori codici ID prodotto e produttore dal nodo",
|
||||
"neighbors": "Ottenere un elenco dei vicini del nodo",
|
||||
"nodeinfo": "Recupero delle classi di comando supportate dal nodo",
|
||||
"nodeplusinfo": "Recupero delle informazioni Z-Wave+ dal nodo",
|
||||
"probe": "Controllo se il nodo è sveglio/attivo",
|
||||
"protocolinfo": "Recupero delle funzionalità di base Z-Wave di questo nodo dal controller",
|
||||
"session": "Recupero di valori che cambiano di rado dal nodo",
|
||||
"static": "Recupero di valori statici dal dispositivo",
|
||||
"versions": "Recupero di informazioni sulle versioni del firmware e della classe di comando",
|
||||
"wakeup": "Configurazione del supporto per code di riattivazione e messaggi "
|
||||
},
|
||||
"refresh_node": {
|
||||
"battery_note": "Se il nodo è alimentato a batteria, assicurarsi di riattivarlo prima di procedere",
|
||||
"complete": "Aggiornamento del nodo completato",
|
||||
"description": "In questo modo si dirà a OpenZWave di re-interrogare un nodo e aggiornare le classi di comando, le funzionalità e i valori del nodo.",
|
||||
"node_status": "Stato del nodo",
|
||||
"refreshing_description": "Aggiornamento delle informazioni sul nodo in corso ...",
|
||||
"start_refresh_button": "Avvia aggiornamento",
|
||||
"step": "Passo",
|
||||
"title": "Aggiorna le informazioni sul nodo",
|
||||
"wakeup_header": "Istruzioni di riattivazione per",
|
||||
"wakeup_instructions_source": "Le istruzioni di riattivazione provengono dal database dei dispositivi della comunità di OpenZWave."
|
||||
},
|
||||
"select_instance": {
|
||||
"header": "Seleziona un'istanza OpenZWave",
|
||||
"introduction": "Hai più di un'istanza OpenZWave in esecuzione. Quale istanza vorresti gestire?"
|
||||
},
|
||||
"services": {
|
||||
"add_node": "Aggiungi Nodo",
|
||||
"remove_node": "Rimuovi nodo"
|
||||
}
|
||||
},
|
||||
"person": {
|
||||
@@ -1781,7 +1893,9 @@
|
||||
"section": {
|
||||
"reloading": {
|
||||
"automation": "Ricarica le Automazioni",
|
||||
"command_line": "Ricarica le entità della riga di comando",
|
||||
"core": "Ricarica la Posizione e le Personalizzazioni",
|
||||
"filter": "Ricaricare le entità filtro",
|
||||
"group": "Ricarica i Gruppi",
|
||||
"heading": "Ricarica Configurazione YAML",
|
||||
"input_boolean": "Ricarica input booleani",
|
||||
@@ -1791,8 +1905,12 @@
|
||||
"input_text": "Ricarica input testuali",
|
||||
"introduction": "Alcune parti di Home Assistant possono essere ricaricate senza richiedere un riavvio. Premendo su Ricarica si rimuoverà la loro Configurazione YAML attuale e si caricherà la versione aggiornata.",
|
||||
"person": "Ricarica le persone",
|
||||
"rest": "Ricarica le entità della piattaforma rest",
|
||||
"scene": "Ricarica le Scene",
|
||||
"script": "Ricarica gli Script",
|
||||
"statistics": "Ricaricare le entità statistiche",
|
||||
"template": "Ricaricare le entità modello",
|
||||
"universal": "Ricarica le entità del lettore multimediale universale",
|
||||
"zone": "Ricarica le zone"
|
||||
},
|
||||
"server_management": {
|
||||
@@ -1812,6 +1930,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"add_tag": "Aggiungi Etichetta",
|
||||
"automation_title": "L'etichetta {name} è scansionata",
|
||||
"caption": "Etichette",
|
||||
"create_automation": "Creare un'automazione con l'etichetta",
|
||||
"description": "Gestisci le etichette",
|
||||
"detail": {
|
||||
"create": "Crea",
|
||||
"create_and_write": "Crea e Scrivi",
|
||||
"delete": "Cancella",
|
||||
"description": "Descrizione",
|
||||
"name": "Nome",
|
||||
"new_tag": "Nuova Etichetta",
|
||||
"tag_id": "Id Etichetta",
|
||||
"tag_id_placeholder": "Autogenerato quando lasciato vuoto",
|
||||
"update": "Aggiorna"
|
||||
},
|
||||
"edit": "Modifica",
|
||||
"headers": {
|
||||
"last_scanned": "Ultima scansione",
|
||||
"name": "Nome"
|
||||
},
|
||||
"never_scanned": "Mai scansionato",
|
||||
"no_tags": "Nessuna etichetta",
|
||||
"write": "Scrivi"
|
||||
},
|
||||
"users": {
|
||||
"add_user": {
|
||||
"caption": "Aggiungi utente",
|
||||
@@ -2133,6 +2277,7 @@
|
||||
"description": "Il rendering dei modelli viene eseguito utilizzando il motore di modelli Jinja2 con alcune estensioni specifiche di Home Assistant.",
|
||||
"editor": "Editor di modelli",
|
||||
"jinja_documentation": "Documentazione del modello Jinja2",
|
||||
"reset": "Ripristina il modello di esempio",
|
||||
"template_extensions": "Estensioni del modello Home Assistant",
|
||||
"title": "Modelli",
|
||||
"unknown_error_template": "Errore sconosciuto nel rendering del modello"
|
||||
@@ -2214,6 +2359,10 @@
|
||||
"description": "La scheda Pulsante consente di aggiungere pulsanti per eseguire attività.",
|
||||
"name": "Pulsante"
|
||||
},
|
||||
"calendar": {
|
||||
"description": "La scheda Calendario mostra un calendario che include le visualizzazioni per giorno, settimana ed elenco",
|
||||
"name": "Calendario"
|
||||
},
|
||||
"conditional": {
|
||||
"card": "Scheda",
|
||||
"change_type": "Cambia tipo",
|
||||
@@ -2668,6 +2817,7 @@
|
||||
"intro": "Salve {name}, benvenuto su Home Assistant. Come vorresti nominare la tua casa?",
|
||||
"intro_location": "Vorremmo sapere dove vivete. Queste informazioni aiuteranno a visualizzare informazioni e automazioni basate sul sole. Questi dati non vengono mai condivisi al di fuori della vostra rete.",
|
||||
"intro_location_detect": "Possiamo aiutarti a compilare queste informazioni effettuando una richiesta una volta a un servizio esterno.",
|
||||
"location_name": "Nome della tua installazione di Home Assistant",
|
||||
"location_name_default": "Casa"
|
||||
},
|
||||
"integration": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user